Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quellcode-Bibliothek common.nsh   Sprache: unbekannt

 
Spracherkennung für: .nsh vermutete Sprache: Unknown {[0] [0] [0]} [Methode: Schwerpunktbildung, einfache Gewichte, sechs Dimensionen]

# 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/.


################################################################################
# Helper defines and macros for toolkit applications

/**
 * Avoid creating macros / functions that overwrite registers (see the
 * GetLongPath macro for one way to avoid this)!
 *
 * Before using the registers exchange the passed in params and save existing
 * register values to the stack.
 *
 * Exch $R9 ; exhange the original $R9 with the top of the stack
 * Exch 1   ; exchange the top of the stack with 1 below the top of the stack
 * Exch $R8 ; exchange the original $R8 with the top of the stack
 * Exch 2   ; exchange the top of the stack with 2 below the top of the stack
 * Exch $R7 ; exchange the original $R7 with the top of the stack
 * Push $R6 ; push the original $R6 onto the top of the stack
 * Push $R5 ; push the original $R5 onto the top of the stack
 * Push $R4 ; push the original $R4 onto the top of the stack
 *
 * <do stuff>
 *
 * ; Restore the values.
 * Pop $R4  ; restore the value for $R4 from the top of the stack
 * Pop $R5  ; restore the value for $R5 from the top of the stack
 * Pop $R6  ; restore the value for $R6 from the top of the stack
 * Exch $R7 ; exchange the new $R7 value with the top of the stack
 * Exch 2   ; exchange the top of the stack with 2 below the top of the stack
 * Exch $R8 ; exchange the new $R8 value with the top of the stack
 * Exch 1   ; exchange the top of the stack with 2 below the top of the stack
 * Exch $R9 ; exchange the new $R9 value with the top of the stack
 *
 *
 * When inserting macros in common.nsh from another macro in common.nsh that
 * can be used from the uninstaller _MOZFUNC_UN will be undefined when it is
 * inserted. Use the following to redefine _MOZFUNC_UN with its original value
 * (see the RegCleanMain macro for an example).
 *
 * !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
 * !insertmacro ${_MOZFUNC_UN_TMP}FileJoin
 * !insertmacro ${_MOZFUNC_UN_TMP}LineFind
 * !insertmacro ${_MOZFUNC_UN_TMP}TextCompareNoDetails
 * !insertmacro ${_MOZFUNC_UN_TMP}TrimNewLines
 * !undef _MOZFUNC_UN
 * !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
 * !undef _MOZFUNC_UN_TMP
 */

; When including a file provided by NSIS check if its verbose macro is defined
; to prevent loading the file a second time.
!ifmacrondef TEXTFUNC_VERBOSE
  !include TextFunc.nsh
!endif

!ifmacrondef FILEFUNC_VERBOSE
  !include FileFunc.nsh
!endif

!ifmacrondef LOGICLIB_VERBOSITY
  !include LogicLib.nsh
!endif

!ifndef WINMESSAGES_INCLUDED
  !include WinMessages.nsh
!endif

; When including WinVer.nsh check if ___WINVER__NSH___ is defined to prevent
; loading the file a second time.
!ifndef ___WINVER__NSH___
  !include WinVer.nsh
!endif

; When including x64.nsh check if ___X64__NSH___ is defined to prevent
; loading the file a second time.
!ifndef ___X64__NSH___
  !include x64.nsh
!endif

; NSIS provided macros that we have overridden.
!include overrides.nsh

!define SHORTCUTS_LOG "shortcuts_log.ini"
!define TO_BE_DELETED "tobedeleted"

; !define SHCNF_DWORD     0x0003
; !define SHCNF_FLUSH     0x1000
!ifndef SHCNF_DWORDFLUSH
  !define SHCNF_DWORDFLUSH 0x1003
!endif
!ifndef SHCNE_ASSOCCHANGED
  !define SHCNE_ASSOCCHANGED 0x08000000
!endif

################################################################################
# Macros for debugging

/**
 * The following two macros assist with verifying that a macro doesn't
 * overwrite any registers.
 *
 * Usage:
 * ${debugSetRegisters}
 * <do stuff>
 * ${debugDisplayRegisters}
 */

/**
 * Sets all register values to their name to assist with verifying that a macro
 * doesn't overwrite any registers.
 */
!macro debugSetRegisters
  StrCpy $0 "$$0"
  StrCpy $1 "$$1"
  StrCpy $2 "$$2"
  StrCpy $3 "$$3"
  StrCpy $4 "$$4"
  StrCpy $5 "$$5"
  StrCpy $6 "$$6"
  StrCpy $7 "$$7"
  StrCpy $8 "$$8"
  StrCpy $9 "$$9"
  StrCpy $R0 "$$R0"
  StrCpy $R1 "$$R1"
  StrCpy $R2 "$$R2"
  StrCpy $R3 "$$R3"
  StrCpy $R4 "$$R4"
  StrCpy $R5 "$$R5"
  StrCpy $R6 "$$R6"
  StrCpy $R7 "$$R7"
  StrCpy $R8 "$$R8"
  StrCpy $R9 "$$R9"
!macroend
!define debugSetRegisters "!insertmacro debugSetRegisters"

/**
 * Displays all register values to assist with verifying that a macro doesn't
 * overwrite any registers.
 */
!macro debugDisplayRegisters
  MessageBox MB_OK \
      "Register Values:$\n\
       $$0 = $0$\n$$1 = $1$\n$$2 = $2$\n$$3 = $3$\n$$4 = $4$\n\
       $$5 = $5$\n$$6 = $6$\n$$7 = $7$\n$$8 = $8$\n$$9 = $9$\n\
       $$R0 = $R0$\n$$R1 = $R1$\n$$R2 = $R2$\n$$R3 = $R3$\n$$R4 = $R4$\n\
       $$R5 = $R5$\n$$R6 = $R6$\n$$R7 = $R7$\n$$R8 = $R8$\n$$R9 = $R9"
!macroend
!define debugDisplayRegisters "!insertmacro debugDisplayRegisters"


################################################################################
# Modern User Interface (MUI) override macros

; Removed macros in nsis 2.33u (ported from nsis 2.22)
;  MUI_LANGUAGEFILE_DEFINE
;  MUI_LANGUAGEFILE_LANGSTRING_PAGE
;  MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE
;  MUI_LANGUAGEFILE_LANGSTRING_DEFINE
;  MUI_LANGUAGEFILE_UNLANGSTRING_PAGE

!macro MOZ_MUI_LANGUAGEFILE_DEFINE DEFINE NAME

  !ifndef "${DEFINE}"
    !define "${DEFINE}" "${${NAME}}"
  !endif
  !undef "${NAME}"

!macroend

!macro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE PAGE NAME

  !ifdef MUI_${PAGE}PAGE
    LangString "${NAME}" 0 "${${NAME}}"
    !undef "${NAME}"
  !else
    !undef "${NAME}"
  !endif

!macroend

!macro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE PAGE NAME

  !ifdef MUI_${PAGE}PAGE | MUI_UN${PAGE}PAGE
    LangString "${NAME}" 0 "${${NAME}}"
    !undef "${NAME}"
  !else
    !undef "${NAME}"
  !endif

!macroend

!macro MOZ_MUI_LANGUAGEFILE_LANGSTRING_DEFINE DEFINE NAME

  !ifdef "${DEFINE}"
    LangString "${NAME}" 0 "${${NAME}}"
  !endif
  !undef "${NAME}"

!macroend

!macro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE PAGE NAME

  !ifdef MUI_UNINSTALLER
    !ifdef MUI_UN${PAGE}PAGE
      LangString "${NAME}" 0 "${${NAME}}"
      !undef "${NAME}"
    !else
      !undef "${NAME}"
    !endif
  !else
    !undef "${NAME}"
  !endif

!macroend

; Modified version of the following MUI macros to support Mozilla localization.
; MUI_LANGUAGE
; MUI_LANGUAGEFILE_BEGIN
; MOZ_MUI_LANGUAGEFILE_END
; See <NSIS App Dir>/Contrib/Modern UI/System.nsh for more information
!define MUI_INSTALLOPTIONS_READ "!insertmacro MUI_INSTALLOPTIONS_READ"

!macro MOZ_MUI_LANGUAGE LANGUAGE
  !verbose push
  !verbose ${MUI_VERBOSE}
  !include "${LANGUAGE}.nsh"
  !verbose pop
!macroend

!macro MOZ_MUI_LANGUAGEFILE_BEGIN LANGUAGE
  !insertmacro MUI_INSERT
  !ifndef "MUI_LANGUAGEFILE_${LANGUAGE}_USED"
    !define "MUI_LANGUAGEFILE_${LANGUAGE}_USED"
    LoadLanguageFile "${LANGUAGE}.nlf"
  !else
    !error "Modern UI language file ${LANGUAGE} included twice!"
  !endif
!macroend

; Custom version of MUI_LANGUAGEFILE_END. The macro to add the default MUI
; strings and the macros for several strings that are part of the NSIS MUI and
; not in our locale files have been commented out.
!macro MOZ_MUI_LANGUAGEFILE_END

#  !include "${NSISDIR}\Contrib\Modern UI\Language files\Default.nsh"
  !ifdef MUI_LANGUAGEFILE_DEFAULT_USED
    !undef MUI_LANGUAGEFILE_DEFAULT_USED
    !warning "${LANGUAGE} Modern UI language file version doesn't match. Using default English texts for missing strings."
  !endif

  !insertmacro MOZ_MUI_LANGUAGEFILE_DEFINE "MUI_${LANGUAGE}_LANGNAME" "MUI_LANGNAME"

  !ifndef MUI_LANGDLL_PUSHLIST
    !define MUI_LANGDLL_PUSHLIST "'${MUI_${LANGUAGE}_LANGNAME}' ${LANG_${LANGUAGE}} "
  !else
    !ifdef MUI_LANGDLL_PUSHLIST_TEMP
      !undef MUI_LANGDLL_PUSHLIST_TEMP
    !endif
    !define MUI_LANGDLL_PUSHLIST_TEMP "${MUI_LANGDLL_PUSHLIST}"
    !undef MUI_LANGDLL_PUSHLIST
    !define MUI_LANGDLL_PUSHLIST "'${MUI_${LANGUAGE}_LANGNAME}' ${LANG_${LANGUAGE}} ${MUI_LANGDLL_PUSHLIST_TEMP}"
  !endif

  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE WELCOME "MUI_TEXT_WELCOME_INFO_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE WELCOME "MUI_TEXT_WELCOME_INFO_TEXT"

!ifdef MUI_TEXT_LICENSE_TITLE
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_TEXT_LICENSE_TITLE"
!endif
!ifdef MUI_TEXT_LICENSE_SUBTITLE
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_TEXT_LICENSE_SUBTITLE"
!endif
!ifdef MUI_INNERTEXT_LICENSE_TOP
  !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE LICENSE "MUI_INNERTEXT_LICENSE_TOP"
!endif

#  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_INNERTEXT_LICENSE_BOTTOM"

!ifdef MUI_INNERTEXT_LICENSE_BOTTOM_CHECKBOX
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_INNERTEXT_LICENSE_BOTTOM_CHECKBOX"
!endif

!ifdef MUI_INNERTEXT_LICENSE_BOTTOM_RADIOBUTTONS
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE LICENSE "MUI_INNERTEXT_LICENSE_BOTTOM_RADIOBUTTONS"
!endif

  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE COMPONENTS "MUI_TEXT_COMPONENTS_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE COMPONENTS "MUI_TEXT_COMPONENTS_SUBTITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE COMPONENTS "MUI_INNERTEXT_COMPONENTS_DESCRIPTION_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE COMPONENTS "MUI_INNERTEXT_COMPONENTS_DESCRIPTION_INFO"

  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE DIRECTORY "MUI_TEXT_DIRECTORY_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE DIRECTORY "MUI_TEXT_DIRECTORY_SUBTITLE"

  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE STARTMENU "MUI_TEXT_STARTMENU_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE STARTMENU "MUI_TEXT_STARTMENU_SUBTITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE STARTMENU "MUI_INNERTEXT_STARTMENU_TOP"
#  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE STARTMENU "MUI_INNERTEXT_STARTMENU_CHECKBOX"

  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_INSTALLING_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_INSTALLING_SUBTITLE"

  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_FINISH_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_FINISH_SUBTITLE"

  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_ABORT_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE INSTFILES "MUI_TEXT_ABORT_SUBTITLE"

  !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_BUTTONTEXT_FINISH"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_INFO_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_INFO_TEXT"
  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_INFO_REBOOT"
  !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_REBOOTNOW"
  !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_REBOOTLATER"
#  !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_RUN"
#  !insertmacro MOZ_MUI_LANGUAGEFILE_MULTILANGSTRING_PAGE FINISH "MUI_TEXT_FINISH_SHOWREADME"

; Support for using the existing MUI_TEXT_ABORTWARNING string
!ifdef MOZ_MUI_CUSTOM_ABORT
    LangString MOZ_MUI_TEXT_ABORTWARNING 0 "${MUI_TEXT_ABORTWARNING}"
!endif

  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_DEFINE MUI_ABORTWARNING "MUI_TEXT_ABORTWARNING"


  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE WELCOME "MUI_UNTEXT_WELCOME_INFO_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE WELCOME "MUI_UNTEXT_WELCOME_INFO_TEXT"

  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE CONFIRM "MUI_UNTEXT_CONFIRM_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE CONFIRM "MUI_UNTEXT_CONFIRM_SUBTITLE"

#  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNTEXT_LICENSE_TITLE"
#  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNTEXT_LICENSE_SUBTITLE"

#  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNINNERTEXT_LICENSE_BOTTOM"
#  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNINNERTEXT_LICENSE_BOTTOM_CHECKBOX"
#  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE LICENSE "MUI_UNINNERTEXT_LICENSE_BOTTOM_RADIOBUTTONS"

#  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE COMPONENTS "MUI_UNTEXT_COMPONENTS_TITLE"
#  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE COMPONENTS "MUI_UNTEXT_COMPONENTS_SUBTITLE"

#  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE DIRECTORY "MUI_UNTEXT_DIRECTORY_TITLE"
#  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE DIRECTORY  "MUI_UNTEXT_DIRECTORY_SUBTITLE"

  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_UNINSTALLING_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_UNINSTALLING_SUBTITLE"

  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_FINISH_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_FINISH_SUBTITLE"

  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_ABORT_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE INSTFILES "MUI_UNTEXT_ABORT_SUBTITLE"

  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE FINISH "MUI_UNTEXT_FINISH_INFO_TITLE"
  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE FINISH "MUI_UNTEXT_FINISH_INFO_TEXT"
  !insertmacro MOZ_MUI_LANGUAGEFILE_UNLANGSTRING_PAGE FINISH "MUI_UNTEXT_FINISH_INFO_REBOOT"

  !insertmacro MOZ_MUI_LANGUAGEFILE_LANGSTRING_DEFINE MUI_UNABORTWARNING "MUI_UNTEXT_ABORTWARNING"

  !ifndef MUI_LANGDLL_LANGUAGES
    !define MUI_LANGDLL_LANGUAGES "'${LANGFILE_${LANGUAGE}_NAME}' '${LANG_${LANGUAGE}}' "
    !define MUI_LANGDLL_LANGUAGES_CP "'${LANGFILE_${LANGUAGE}_NAME}' '${LANG_${LANGUAGE}}' '${LANG_${LANGUAGE}_CP}' "
  !else
    !ifdef MUI_LANGDLL_LANGUAGES_TEMP
      !undef MUI_LANGDLL_LANGUAGES_TEMP
    !endif
    !define MUI_LANGDLL_LANGUAGES_TEMP "${MUI_LANGDLL_LANGUAGES}"
    !undef MUI_LANGDLL_LANGUAGES

    !ifdef MUI_LANGDLL_LANGUAGES_CP_TEMP
      !undef MUI_LANGDLL_LANGUAGES_CP_TEMP
    !endif
    !define MUI_LANGDLL_LANGUAGES_CP_TEMP "${MUI_LANGDLL_LANGUAGES_CP}"
    !undef MUI_LANGDLL_LANGUAGES_CP

    !define MUI_LANGDLL_LANGUAGES "'${LANGFILE_${LANGUAGE}_NAME}' '${LANG_${LANGUAGE}}' ${MUI_LANGDLL_LANGUAGES_TEMP}"
    !define MUI_LANGDLL_LANGUAGES_CP "'${LANGFILE_${LANGUAGE}_NAME}' '${LANG_${LANGUAGE}}' '${LANG_${LANGUAGE}_CP}' ${MUI_LANGDLL_LANGUAGES_CP_TEMP}"
  !endif

!macroend

/**
 * Creates an InstallOptions file with a UTF-16LE BOM and adds the RTL value
 * to the Settings section.
 *
 * @param   _FILE
 *          The name of the file to be created in $PLUGINSDIR.
 */
!macro InitInstallOptionsFile _FILE
  Push $R9

  FileOpen $R9 "$PLUGINSDIR\${_FILE}" w
  FileWriteWord $R9 "65279"
  FileClose $R9
  WriteIniStr "$PLUGINSDIR\${_FILE}" "Settings" "RTL" "$(^RTL)"

  Pop $R9
!macroend


################################################################################
# Macros for handling files in use

/**
 * Checks for files in use in the $INSTDIR directory. To check files in
 * sub-directories this macro would need to be rewritten to create
 * sub-directories in the temporary directory used to backup the files that are
 * checked.
 *
 * Example usage:
 *
 *  ; The first string to be pushed onto the stack MUST be "end" to indicate
 *  ; that there are no more files in the $INSTDIR directory to check.
 *  Push "end"
 *  Push "freebl3.dll"
 *  ; The last file pushed should be the app's main exe so if it is in use this
 *  ; macro will return after the first check.
 *  Push "${FileMainEXE}"
 *  ${CheckForFilesInUse} $R9
 *
 * !IMPORTANT - this macro uses the $R7, $R8, and $R9 registers and makes no
 *              attempt to restore their original values.
 *
 * @return  _RESULT
 *          false if all of the files popped from the stack are not in use.
 *          True if any of the files popped from the stack are in use.
 * $R7 = Temporary backup directory where the files will be copied to.
 * $R8 = value popped from the stack. This will either be a file name for a file
 *       in the $INSTDIR directory or "end" to indicate that there are no
 *       additional files to check.
 * $R9 = _RESULT
 */
!macro CheckForFilesInUse

  !ifndef ${_MOZFUNC_UN}CheckForFilesInUse
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}CheckForFilesInUse "!insertmacro ${_MOZFUNC_UN}CheckForFilesInUseCall"

    Function ${_MOZFUNC_UN}CheckForFilesInUse
      ; Create a temporary backup directory.
      GetTempFileName $R7 "$INSTDIR"
      Delete "$R7"
      SetOutPath "$R7"
      StrCpy $R9 "false"

      Pop $R8
      ${While} $R8 != "end"
        ${Unless} ${FileExists} "$INSTDIR\$R8"
          Pop $R8 ; get next file to check before continuing
          ${Continue}
        ${EndUnless}

        ClearErrors
        CopyFiles /SILENT "$INSTDIR\$R8" "$R7\$R8" ; try to copy
        ${If} ${Errors}
          ; File is in use
          StrCpy $R9 "true"
          ${Break}
        ${EndIf}

        Delete "$INSTDIR\$R8" ; delete original
        ${If} ${Errors}
          ; File is in use
          StrCpy $R9 "true"
          Delete "$R7\$R8" ; delete temp copy
          ${Break}
        ${EndIf}

        Pop $R8 ; get next file to check
      ${EndWhile}

      ; clear stack
      ${While} $R8 != "end"
        Pop $R8
      ${EndWhile}

      ; restore everything
      SetOutPath "$INSTDIR"
      CopyFiles /SILENT "$R7\*" "$INSTDIR\"
      RmDir /r "$R7"
      SetOutPath "$EXEDIR"
      ClearErrors

      Push $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro CheckForFilesInUseCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call CheckForFilesInUse
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.CheckForFilesInUseCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.CheckForFilesInUse
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.CheckForFilesInUse
  !ifndef un.CheckForFilesInUse
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro CheckForFilesInUse

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

!macro GetCommonDirectory

  !ifndef ${_MOZFUNC_UN}GetCommonDirectory
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}GetCommonDirectory "!insertmacro ${_MOZFUNC_UN}GetCommonDirectoryCall"

    Function ${_MOZFUNC_UN}GetCommonDirectory
      Push $0   ; Save $0

      ; This gets C:\ProgramData or the equivalent.
      ${GetCommonAppDataFolder} $0

      ; Add our subdirectory, this is hardcoded as grandparent of the update directory in
      ; several other places.
      StrCpy $0 "$0\Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38"

      Exch $0   ; Restore original $0 and put our $0 on the stack.
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro GetCommonDirectoryCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call GetCommonDirectory
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.GetCommonDirectoryCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.GetCommonDirectory
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.GetCommonDirectory
  !ifndef un.GetCommonDirectory
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro GetCommonDirectory

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * The macros below will automatically prepend un. to the function names when
 * they are defined (e.g. !define un.RegCleanMain).
 */
!verbose push
!verbose 3
!ifndef _MOZFUNC_VERBOSE
  !define _MOZFUNC_VERBOSE 3
!endif
!verbose ${_MOZFUNC_VERBOSE}
!define MOZFUNC_VERBOSE "!insertmacro MOZFUNC_VERBOSE"
!define _MOZFUNC_UN
!define _MOZFUNC_S
!verbose pop

!macro MOZFUNC_VERBOSE _VERBOSE
  !verbose push
  !verbose 3
  !undef _MOZFUNC_VERBOSE
  !define _MOZFUNC_VERBOSE ${_VERBOSE}
  !verbose pop
!macroend

/**
 * Displays a MessageBox and then calls abort to prevent continuing to the
 * next page when the specified Window Class is found.
 *
 * @param   _WINDOW_CLASS
 *          The Window Class to search for with FindWindow.
 * @param   _MSG
 *          The message text to display in the message box.
 *
 * $R7 = return value from FindWindow
 * $R8 = _WINDOW_CLASS
 * $R9 = _MSG
 */
!macro ManualCloseAppPrompt

  !ifndef ${_MOZFUNC_UN}ManualCloseAppPrompt
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}ManualCloseAppPrompt "!insertmacro ${_MOZFUNC_UN}ManualCloseAppPromptCall"

    Function ${_MOZFUNC_UN}ManualCloseAppPrompt
      Exch $R9
      Exch 1
      Exch $R8
      Push $R7

      FindWindow $R7 "$R8"
      ${If} $R7 <> 0 ; integer comparison
        MessageBox MB_OK|MB_ICONQUESTION "$R9"
        Abort
      ${EndIf}

      Pop $R7
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro ManualCloseAppPromptCall _WINDOW_CLASS _MSG
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_WINDOW_CLASS}"
  Push "${_MSG}"
  Call ManualCloseAppPrompt
  !verbose pop
!macroend

!macro un.ManualCloseAppPromptCall _WINDOW_CLASS _MSG
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_WINDOW_CLASS}"
  Push "${_MSG}"
  Call un.ManualCloseAppPrompt
  !verbose pop
!macroend

!macro un.ManualCloseAppPrompt
  !ifndef un.ManualCloseAppPrompt
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro ManualCloseAppPrompt

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend


################################################################################
# Macros for working with the registry

/**
 * Writes a registry string using SHCTX and the supplied params and logs the
 * action to the install log and the uninstall log if _LOG_UNINSTALL equals 1.
 *
 * Define NO_LOG to prevent all logging when calling this from the uninstaller.
 *
 * @param   _ROOT
 *          The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.).
 *          This will only be used for logging.
 * @param   _KEY
 *          The subkey in relation to the key root.
 * @param   _NAME
 *          The key value name to write to.
 * @param   _STR
 *          The string to write to the key value name.
 * @param   _LOG_UNINSTALL
 *          0 = don't add to uninstall log, 1 = add to uninstall log.
 *
 * $R5 = _ROOT
 * $R6 = _KEY
 * $R7 = _NAME
 * $R8 = _STR
 * $R9 = _LOG_UNINSTALL
 */
!macro WriteRegStr2

  !ifndef ${_MOZFUNC_UN}WriteRegStr2
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}WriteRegStr2 "!insertmacro ${_MOZFUNC_UN}WriteRegStr2Call"

    Function ${_MOZFUNC_UN}WriteRegStr2
      Exch $R9
      Exch 1
      Exch $R8
      Exch 2
      Exch $R7
      Exch 3
      Exch $R6
      Exch 4
      Exch $R5

      ClearErrors
      WriteRegStr SHCTX "$R6" "$R7" "$R8"

      !ifndef NO_LOG
        ${If} ${Errors}
          ${LogMsg} "** ERROR Adding Registry String: $R5 | $R6 | $R7 | $R8 **"
        ${Else}
          ${If} $R9 == 1 ; add to the uninstall log?
            ${LogUninstall} "RegVal: $R5 | $R6 | $R7"
          ${EndIf}
          ${LogMsg} "Added Registry String: $R5 | $R6 | $R7 | $R8"
        ${EndIf}
      !endif

      Exch $R5
      Exch 4
      Exch $R6
      Exch 3
      Exch $R7
      Exch 2
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro WriteRegStr2Call _ROOT _KEY _NAME _STR _LOG_UNINSTALL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_ROOT}"
  Push "${_KEY}"
  Push "${_NAME}"
  Push "${_STR}"
  Push "${_LOG_UNINSTALL}"
  Call WriteRegStr2
  !verbose pop
!macroend

!macro un.WriteRegStr2Call _ROOT _KEY _NAME _STR _LOG_UNINSTALL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_ROOT}"
  Push "${_KEY}"
  Push "${_NAME}"
  Push "${_STR}"
  Push "${_LOG_UNINSTALL}"
  Call un.WriteRegStr2
  !verbose pop
!macroend

!macro un.WriteRegStr2
  !ifndef un.WriteRegStr2
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro WriteRegStr2

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Writes a registry dword using SHCTX and the supplied params and logs the
 * action to the install log and the uninstall log if _LOG_UNINSTALL equals 1.
 *
 * Define NO_LOG to prevent all logging when calling this from the uninstaller.
 *
 * @param   _ROOT
 *          The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.).
 *          This will only be used for logging.
 * @param   _KEY
 *          The subkey in relation to the key root.
 * @param   _NAME
 *          The key value name to write to.
 * @param   _DWORD
 *          The dword to write to the key value name.
 * @param   _LOG_UNINSTALL
 *          0 = don't add to uninstall log, 1 = add to uninstall log.
 *
 * $R5 = _ROOT
 * $R6 = _KEY
 * $R7 = _NAME
 * $R8 = _DWORD
 * $R9 = _LOG_UNINSTALL
 */
!macro WriteRegDWORD2

  !ifndef ${_MOZFUNC_UN}WriteRegDWORD2
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}WriteRegDWORD2 "!insertmacro ${_MOZFUNC_UN}WriteRegDWORD2Call"

    Function ${_MOZFUNC_UN}WriteRegDWORD2
      Exch $R9
      Exch 1
      Exch $R8
      Exch 2
      Exch $R7
      Exch 3
      Exch $R6
      Exch 4
      Exch $R5

      ClearErrors
      WriteRegDWORD SHCTX "$R6" "$R7" "$R8"

      !ifndef NO_LOG
        ${If} ${Errors}
          ${LogMsg} "** ERROR Adding Registry DWord: $R5 | $R6 | $R7 | $R8 **"
        ${Else}
          ${If} $R9 == 1 ; add to the uninstall log?
            ${LogUninstall} "RegVal: $R5 | $R6 | $R7"
          ${EndIf}
          ${LogMsg} "Added Registry DWord: $R5 | $R6 | $R7 | $R8"
        ${EndIf}
      !endif

      Exch $R5
      Exch 4
      Exch $R6
      Exch 3
      Exch $R7
      Exch 2
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro WriteRegDWORD2Call _ROOT _KEY _NAME _DWORD _LOG_UNINSTALL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_ROOT}"
  Push "${_KEY}"
  Push "${_NAME}"
  Push "${_DWORD}"
  Push "${_LOG_UNINSTALL}"
  Call WriteRegDWORD2
  !verbose pop
!macroend

!macro un.WriteRegDWORD2Call _ROOT _KEY _NAME _DWORD _LOG_UNINSTALL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_ROOT}"
  Push "${_KEY}"
  Push "${_NAME}"
  Push "${_DWORD}"
  Push "${_LOG_UNINSTALL}"
  Call un.WriteRegDWORD2
  !verbose pop
!macroend

!macro un.WriteRegDWORD2
  !ifndef un.WriteRegDWORD2
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro WriteRegDWORD2

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Writes a registry string to HKCR using the supplied params and logs the
 * action to the install log and the uninstall log if _LOG_UNINSTALL equals 1.
 *
 * Define NO_LOG to prevent all logging when calling this from the uninstaller.
 *
 * @param   _ROOT
 *          The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.).
 *          This will only be used for logging.
 * @param   _KEY
 *          The subkey in relation to the key root.
 * @param   _NAME
 *          The key value name to write to.
 * @param   _STR
 *          The string to write to the key value name.
 * @param   _LOG_UNINSTALL
 *          0 = don't add to uninstall log, 1 = add to uninstall log.
 *
 * $R5 = _ROOT
 * $R6 = _KEY
 * $R7 = _NAME
 * $R8 = _STR
 * $R9 = _LOG_UNINSTALL
 */
!macro WriteRegStrHKCR

  !ifndef ${_MOZFUNC_UN}WriteRegStrHKCR
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}WriteRegStrHKCR "!insertmacro ${_MOZFUNC_UN}WriteRegStrHKCRCall"

    Function ${_MOZFUNC_UN}WriteRegStrHKCR
      Exch $R9
      Exch 1
      Exch $R8
      Exch 2
      Exch $R7
      Exch 3
      Exch $R6
      Exch 4
      Exch $R5

      ClearErrors
      WriteRegStr HKCR "$R6" "$R7" "$R8"

      !ifndef NO_LOG
        ${If} ${Errors}
          ${LogMsg} "** ERROR Adding Registry String: $R5 | $R6 | $R7 | $R8 **"
        ${Else}
          ${If} $R9 == 1 ; add to the uninstall log?
            ${LogUninstall} "RegVal: $R5 | $R6 | $R7"
          ${EndIf}
          ${LogMsg} "Added Registry String: $R5 | $R6 | $R7 | $R8"
        ${EndIf}
      !endif

      Exch $R5
      Exch 4
      Exch $R6
      Exch 3
      Exch $R7
      Exch 2
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro WriteRegStrHKCRCall _ROOT _KEY _NAME _STR _LOG_UNINSTALL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_ROOT}"
  Push "${_KEY}"
  Push "${_NAME}"
  Push "${_STR}"
  Push "${_LOG_UNINSTALL}"
  Call WriteRegStrHKCR
  !verbose pop
!macroend

!macro un.WriteRegStrHKCRCall _ROOT _KEY _NAME _STR _LOG_UNINSTALL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_ROOT}"
  Push "${_KEY}"
  Push "${_NAME}"
  Push "${_STR}"
  Push "${_LOG_UNINSTALL}"
  Call un.WriteRegStrHKCR
  !verbose pop
!macroend

!macro un.WriteRegStrHKCR
  !ifndef un.WriteRegStrHKCR
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro WriteRegStrHKCR

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

!ifndef KEY_SET_VALUE
  !define KEY_SET_VALUE 0x0002
!endif
!ifndef KEY_WOW64_64KEY
  !define KEY_WOW64_64KEY 0x0100
!endif
!ifndef HAVE_64BIT_BUILD
  !define CREATE_KEY_SAM ${KEY_SET_VALUE}
!else
  !define CREATE_KEY_SAM ${KEY_SET_VALUE}|${KEY_WOW64_64KEY}
!endif

/**
 * Creates a registry key. This will log the actions to the install and
 * uninstall logs. Alternatively you can set a registry value to create the key
 * and then delete the value.
 *
 * Define NO_LOG to prevent all logging when calling this from the uninstaller.
 *
 * @param   _ROOT
 *          The registry key root as defined by NSIS (e.g. HKLM, HKCU, etc.).
 * @param   _KEY
 *          The subkey in relation to the key root.
 * @param   _LOG_UNINSTALL
 *          0 = don't add to uninstall log, 1 = add to uninstall log.
 *
 * $R4 = [out] handle to newly created registry key. If this is not a key
 *       located in one of the predefined registry keys this must be closed
 *       with RegCloseKey (this should not be needed unless someone decides to
 *       do something extremely squirrelly with NSIS).
 * $R5 = return value from RegCreateKeyExW (represented by R5 in the system call).
 * $R6 = [in] hKey passed to RegCreateKeyExW.
 * $R7 = _ROOT
 * $R8 = _KEY
 * $R9 = _LOG_UNINSTALL
 */
!macro CreateRegKey

  !ifndef ${_MOZFUNC_UN}CreateRegKey
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}CreateRegKey "!insertmacro ${_MOZFUNC_UN}CreateRegKeyCall"

    Function ${_MOZFUNC_UN}CreateRegKey
      Exch $R9
      Exch 1
      Exch $R8
      Exch 2
      Exch $R7
      Push $R6
      Push $R5
      Push $R4

      StrCmp $R7 "HKCR" +1 +2
      StrCpy $R6 "0x80000000"
      StrCmp $R7 "HKCU" +1 +2
      StrCpy $R6 "0x80000001"
      StrCmp $R7 "HKLM" +1 +2
      StrCpy $R6 "0x80000002"

      ; see definition of RegCreateKey
      System::Call "Advapi32::RegCreateKeyExW(i R6, w R8, i 0, i 0, i 0,\
                                              i ${CREATE_KEY_SAM}, i 0, *i .R4,\
                                              i 0) i .R5"

      !ifndef NO_LOG
        ; if $R5 is not 0 then there was an error creating the registry key.
        ${If} $R5 <> 0
          ${LogMsg} "** ERROR Adding Registry Key: $R7 | $R8 **"
        ${Else}
          ${If} $R9 == 1 ; add to the uninstall log?
            ${LogUninstall} "RegKey: $R7 | $R8"
          ${EndIf}
          ${LogMsg} "Added Registry Key: $R7 | $R8"
        ${EndIf}
      !endif

      StrCmp $R5 0 +1 +2
      System::Call "Advapi32::RegCloseKey(iR4)"

      Pop $R4
      Pop $R5
      Pop $R6
      Exch $R7
      Exch 2
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro CreateRegKeyCall _ROOT _KEY _LOG_UNINSTALL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_ROOT}"
  Push "${_KEY}"
  Push "${_LOG_UNINSTALL}"
  Call CreateRegKey
  !verbose pop
!macroend

!macro un.CreateRegKeyCall _ROOT _KEY _LOG_UNINSTALL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_ROOT}"
  Push "${_KEY}"
  Push "${_LOG_UNINSTALL}"
  Call un.CreateRegKey
  !verbose pop
!macroend

!macro un.CreateRegKey
  !ifndef un.CreateRegKey
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro CreateRegKey

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Helper for checking for the existence of a registry key.
 * SHCTX is the root key to search.
 *
 * @param   _MAIN_KEY
 *          Sub key to iterate for the key in question
 * @param   _KEY
 *          Key name to search for
 * @return  _RESULT
 *          'true' / 'false' result
 */
!macro CheckIfRegistryKeyExists
  !ifndef CheckIfRegistryKeyExists
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define CheckIfRegistryKeyExists "!insertmacro CheckIfRegistryKeyExistsCall"

    Function CheckIfRegistryKeyExists
      ; stack: main key, key
      Exch $R9 ; main key, stack: old R9, key
      Exch 1   ; stack: key, old R9
      Exch $R8 ; key, stack: old R8, old R9
      Push $R7
      Push $R6
      Push $R5

      StrCpy $R5 "false"
      StrCpy $R7 "0" # loop index
      ${Do}
        EnumRegKey $R6 SHCTX "$R9" "$R7"
        ${If} "$R6" == "$R8"
          StrCpy $R5 "true"
          ${Break}
        ${EndIf}
        IntOp $R7 $R7 + 1
      ${LoopWhile} $R6 != ""
      ClearErrors

      StrCpy $R9 $R5

      Pop $R5
      Pop $R6
      Pop $R7 ; stack: old R8, old R9
      Pop $R8 ; stack: old R9
      Exch $R9 ; stack: result
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro CheckIfRegistryKeyExistsCall _MAIN_KEY _KEY _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Push "${_MAIN_KEY}"
  Call CheckIfRegistryKeyExists
  Pop ${_RESULT}
  !verbose pop
!macroend

/**
 * Read the value of an installer pref that's been set by the product.
 *
 * @param   _KEY ($R1)
 *          Sub key containing all the installer prefs
 *          Usually "Software\Mozilla\${AppName}"
 * @param   _PREF ($R2)
 *          Name of the pref to look up
 * @return  _RESULT ($R3)
 *          'true' or 'false' (only boolean prefs are supported)
 *          If no value exists for the requested pref, the result is 'false'
 */
!macro GetInstallerRegistryPref
  !ifndef ${_MOZFUNC_UN}GetInstallerRegistryPref
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}GetInstallerRegistryPref "!insertmacro GetInstallerRegistryPrefCall"

    Function ${_MOZFUNC_UN}GetInstallerRegistryPref
      ; stack: key, pref
      Exch $R1 ; key, stack: old R1, pref
      Exch 1   ; stack: pref, old R1
      Exch $R2 ; pref, stack: old R2, old R1
      Push $R3

      StrCpy $R3 0

      ; These prefs are always stored in the native registry.
      SetRegView 64

      ClearErrors
      ReadRegDWORD $R3 HKCU "$R1\Installer\$AppUserModelID" "$R2"

      SetRegView lastused

      ${IfNot} ${Errors}
      ${AndIf} $R3 != 0
        StrCpy $R1 "true"
      ${Else}
        StrCpy $R1 "false"
      ${EndIf}

      ; stack: old R3, old R2, old R1
      Pop $R3 ; stack: old R2, old R1
      Pop $R2 ; stack: old R1
      Exch $R1 ; stack: result
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro GetInstallerRegistryPrefCall _KEY _PREF _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_PREF}"
  Push "${_KEY}"
  Call GetInstallerRegistryPref
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.GetInstallerRegistryPrefCall _KEY _PREF _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_PREF}"
  Push "${_KEY}"
  Call un.GetInstallerRegistryPref
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.GetInstallerRegistryPref
  !ifndef un.GetInstallerRegistryPref
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro GetInstallerRegistryPref

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

################################################################################
# Macros for adding file and protocol handlers

/**
 * Writes common registry values for a handler using SHCTX.
 *
 * @param   _KEY
 *          The subkey in relation to the key root.
 * @param   _VALOPEN
 *          The path and args to launch the application.
 * @param   _VALICON
 *          The path to the binary that contains the icon group for the default icon
 *          followed by a comma and either the icon group's resource index or the icon
 *          group's resource id prefixed with a minus sign
 * @param   _DISPNAME
 *          The display name for the handler. If emtpy no value will be set.
 * @param   _ISPROTOCOL
 *          Sets protocol handler specific registry values when "true".
 *          Deletes protocol handler specific registry values when "delete".
 *          Otherwise doesn't touch handler specific registry values.
 * @param   _ISDDE
 *          Sets DDE specific registry values when "true".
 *
 * $R3 = string value of the current registry key path.
 * $R4 = _KEY
 * $R5 = _VALOPEN
 * $R6 = _VALICON
 * $R7 = _DISPNAME
 * $R8 = _ISPROTOCOL
 * $R9 = _ISDDE
 */
!macro AddHandlerValues

  !ifndef ${_MOZFUNC_UN}AddHandlerValues
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}AddHandlerValues "!insertmacro ${_MOZFUNC_UN}AddHandlerValuesCall"

    Function ${_MOZFUNC_UN}AddHandlerValues
      Exch $R9
      Exch 1
      Exch $R8
      Exch 2
      Exch $R7
      Exch 3
      Exch $R6
      Exch 4
      Exch $R5
      Exch 5
      Exch $R4
      Push $R3

      StrCmp "$R7" "" +6 +1
      ReadRegStr $R3 SHCTX "$R4" "FriendlyTypeName"

      StrCmp "$R3" "" +1 +3
      WriteRegStr SHCTX "$R4" "" "$R7"
      WriteRegStr SHCTX "$R4" "FriendlyTypeName" "$R7"

      StrCmp "$R8" "true" +1 +2
      WriteRegStr SHCTX "$R4" "URL Protocol" ""
      StrCmp "$R8" "delete" +1 +2
      DeleteRegValue SHCTX "$R4" "URL Protocol"
      StrCpy $R3 ""
      ReadRegDWord $R3 SHCTX "$R4" "EditFlags"
      StrCmp $R3 "" +1 +3  ; Only add EditFlags if a value doesn't exist
      DeleteRegValue SHCTX "$R4" "EditFlags"
      WriteRegDWord SHCTX "$R4" "EditFlags" 0x00000002

      StrCmp "$R6" "" +2 +1
      WriteRegStr SHCTX "$R4\DefaultIcon" "" "$R6"

      StrCmp "$R5" "" +2 +1
      WriteRegStr SHCTX "$R4\shell\open\command" "" "$R5"

!ifdef DDEApplication
      StrCmp "$R9" "true" +1 +11
      WriteRegStr SHCTX "$R4\shell\open\ddeexec" "" "$\"%1$\",,0,0,,,,"
      WriteRegStr SHCTX "$R4\shell\open\ddeexec" "NoActivateHandler" ""
      WriteRegStr SHCTX "$R4\shell\open\ddeexec\Application" "" "${DDEApplication}"
      WriteRegStr SHCTX "$R4\shell\open\ddeexec\Topic" "" "WWW_OpenURL"
      ; The ifexec key may have been added by another application so try to
      ; delete it to prevent it from breaking this app's shell integration.
      ; Also, IE 6 and below doesn't remove this key when it sets itself as the
      ; default handler and if this key exists IE's shell integration breaks.
      DeleteRegKey HKLM "$R4\shell\open\ddeexec\ifexec"
      DeleteRegKey HKCU "$R4\shell\open\ddeexec\ifexec"
!endif

      ClearErrors

      Pop $R3
      Exch $R4
      Exch 5
      Exch $R5
      Exch 4
      Exch $R6
      Exch 3
      Exch $R7
      Exch 2
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro AddHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL _ISDDE
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Push "${_VALOPEN}"
  Push "${_VALICON}"
  Push "${_DISPNAME}"
  Push "${_ISPROTOCOL}"
  Push "${_ISDDE}"
  Call AddHandlerValues
  !verbose pop
!macroend

!macro un.AddHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL _ISDDE
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Push "${_VALOPEN}"
  Push "${_VALICON}"
  Push "${_DISPNAME}"
  Push "${_ISPROTOCOL}"
  Push "${_ISDDE}"
  Call un.AddHandlerValues
  !verbose pop
!macroend

!macro un.AddHandlerValues
  !ifndef un.AddHandlerValues
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro AddHandlerValues

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Writes common registry values for a handler that DOES NOT use DDE using SHCTX.
 *
 * @param   _KEY
 *          The key name in relation to the HKCR root. SOFTWARE\Classes is
 *          prefixed to this value when using SHCTX.
 * @param   _VALOPEN
 *          The path and args to launch the application.
 * @param   _VALICON
 *          The path to the binary that contains the icon group for the default icon
 *          followed by a comma and either the icon group's resource index or the icon
 *          group's resource id prefixed with a minus sign
 * @param   _DISPNAME
 *          The display name for the handler. If emtpy no value will be set.
 * @param   _ISPROTOCOL
 *          Sets protocol handler specific registry values when "true".
 *          Deletes protocol handler specific registry values when "delete".
 *          Otherwise doesn't touch handler specific registry values.
 *
 * $R3 = storage for SOFTWARE\Classes
 * $R4 = string value of the current registry key path.
 * $R5 = _KEY
 * $R6 = _VALOPEN
 * $R7 = _VALICON
 * $R8 = _DISPNAME
 * $R9 = _ISPROTOCOL
 */
!macro AddDisabledDDEHandlerValues

  !ifndef ${_MOZFUNC_UN}AddDisabledDDEHandlerValues
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}AddDisabledDDEHandlerValues "!insertmacro ${_MOZFUNC_UN}AddDisabledDDEHandlerValuesCall"

    Function ${_MOZFUNC_UN}AddDisabledDDEHandlerValues
      Exch $R9 ; _ISPROTOCOL
      Exch 1
      Exch $R8 ; FriendlyTypeName
      Exch 2
      Exch $R7 ; icon index
      Exch 3
      Exch $R6 ; shell\open\command
      Exch 4
      Exch $R5 ; reg key
      Push $R4 ;
      Push $R3 ; base reg class

      StrCpy $R3 "SOFTWARE\Classes"
      StrCmp "$R8" "" +6 +1
      ReadRegStr $R4 SHCTX "$R5" "FriendlyTypeName"

      StrCmp "$R4" "" +1 +3
      WriteRegStr SHCTX "$R3\$R5" "" "$R8"
      WriteRegStr SHCTX "$R3\$R5" "FriendlyTypeName" "$R8"

      StrCmp "$R9" "true" +1 +2
      WriteRegStr SHCTX "$R3\$R5" "URL Protocol" ""
      StrCmp "$R9" "delete" +1 +2
      DeleteRegValue SHCTX "$R3\$R5" "URL Protocol"
      StrCpy $R4 ""
      ReadRegDWord $R4 SHCTX "$R3\$R5" "EditFlags"
      StrCmp $R4 "" +1 +3  ; Only add EditFlags if a value doesn't exist
      DeleteRegValue SHCTX "$R3\$R5" "EditFlags"
      WriteRegDWord SHCTX "$R3\$R5" "EditFlags" 0x00000002

      StrCmp "$R7" "" +2 +1
      WriteRegStr SHCTX "$R3\$R5\DefaultIcon" "" "$R7"

      ; Main command handler for the app
      WriteRegStr SHCTX "$R3\$R5\shell" "" "open"
      WriteRegStr SHCTX "$R3\$R5\shell\open\command" "" "$R6"

      ; Drop support for DDE (bug 491947), and remove old dde entries if
      ; they exist.
      ;
      ; Note, changes in SHCTX should propegate to hkey classes root when
      ; current user or local machine entries are written. Windows will also
      ; attempt to propegate entries when a handler is used. CR entries are a
      ; combination of LM and CU, with CU taking priority.
      ;
      ; To disable dde, an empty shell/ddeexec key must be created in current
      ; user or local machine. Unfortunately, settings have various different
      ; behaviors depending on the windows version. The following code attempts
      ; to address these differences.
      ;
      ; IE does not configure ddeexec, so issues with left over ddeexec keys
      ; in LM are reduced. We configure an empty ddeexec key with an empty default
      ; string in CU to be sure.
      ;
      DeleteRegKey SHCTX "SOFTWARE\Classes\$R5\shell\open\ddeexec"
      WriteRegStr SHCTX "SOFTWARE\Classes\$R5\shell\open\ddeexec" "" ""

      ClearErrors

      Pop $R3
      Pop $R4
      Exch $R5
      Exch 4
      Exch $R6
      Exch 3
      Exch $R7
      Exch 2
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro AddDisabledDDEHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Push "${_VALOPEN}"
  Push "${_VALICON}"
  Push "${_DISPNAME}"
  Push "${_ISPROTOCOL}"
  Call AddDisabledDDEHandlerValues
  !verbose pop
!macroend

!macro un.AddDisabledDDEHandlerValuesCall _KEY _VALOPEN _VALICON _DISPNAME _ISPROTOCOL
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Push "${_VALOPEN}"
  Push "${_VALICON}"
  Push "${_DISPNAME}"
  Push "${_ISPROTOCOL}"
  Call un.AddDisabledDDEHandlerValues
  !verbose pop
!macroend

!macro un.AddDisabledDDEHandlerValues
  !ifndef un.AddDisabledDDEHandlerValues
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro AddDisabledDDEHandlerValues

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend


################################################################################
# Macros for handling DLL registration

!macro RegisterDLL DLL

  ; The x64 regsvr32.exe registers x86 DLL's properly so just use it
  ; when installing on an x64 systems even when installing an x86 application.
  ${If} ${RunningX64}
  ${OrIf} ${IsNativeARM64}
    ${DisableX64FSRedirection}
    ExecWait '"$SYSDIR\regsvr32.exe" /s "${DLL}"'
    ${EnableX64FSRedirection}
  ${Else}
    RegDLL "${DLL}"
  ${EndIf}

!macroend

!macro UnregisterDLL DLL

  ; The x64 regsvr32.exe registers x86 DLL's properly so just use it
  ; when installing on an x64 systems even when installing an x86 application.
  ${If} ${RunningX64}
  ${OrIf} ${IsNativeARM64}
    ${DisableX64FSRedirection}
    ExecWait '"$SYSDIR\regsvr32.exe" /s /u "${DLL}"'
    ${EnableX64FSRedirection}
  ${Else}
    UnRegDLL "${DLL}"
  ${EndIf}

!macroend

!define RegisterDLL "!insertmacro RegisterDLL"
!define UnregisterDLL "!insertmacro UnregisterDLL"


################################################################################
# Macros for retrieving special folders

/**
 * These macro get special folder paths directly, without depending on
 * SetShellVarContext.
 *
 * Usage:
 * ${GetProgramsFolder} $0
 * ${GetLocalAppDataFolder} $0
 * ${GetCommonAppDataFolder} $0
 *
 */
!macro GetSpecialFolder _ID _RESULT
  ; This system call gets the directory path. The arguments are:
  ;   A null ptr for hwnd
  ;   t.s puts the output string on the NSIS stack
  ;   id indicates which dir to get
  ;   false for fCreate (i.e. Do not create the folder if it doesn't exist)
  System::Call "Shell32::SHGetSpecialFolderPathW(p 0, t.s, i ${_ID}, i 0)"
  Pop ${_RESULT}
!macroend

!define CSIDL_PROGRAMS          0x0002
!define CSIDL_LOCAL_APPDATA     0x001c
!define CSIDL_COMMON_APPDATA    0x0023

; Current User's Start Menu Programs
!define GetProgramsFolder       "!insertmacro GetSpecialFolder ${CSIDL_PROGRAMS}"

; Current User's Local App Data (e.g. C:\Users\<user>\AppData\Local)
!define GetLocalAppDataFolder   "!insertmacro GetSpecialFolder ${CSIDL_LOCAL_APPDATA}"

; Common App Data (e.g. C:\ProgramData)
!define GetCommonAppDataFolder  "!insertmacro GetSpecialFolder ${CSIDL_COMMON_APPDATA}"

################################################################################
# Macros for retrieving existing install paths

/**
 * Finds a second installation of the application so we can make informed
 * decisions about registry operations. This uses SHCTX to determine the
 * registry hive so you must call SetShellVarContext first.
 *
 * @param   _KEY
 *          The registry subkey (typically this will be Software\Mozilla).
 * @return  _RESULT
 *          false if a second install isn't found, path to the main exe if a
 *          second install is found.
 *
 * $R3 = stores the long path to $INSTDIR
 * $R4 = counter for the outer loop's EnumRegKey
 * $R5 = return value from ReadRegStr and RemoveQuotesFromPath
 * $R6 = return value from GetParent
 * $R7 = return value from the loop's EnumRegKey
 * $R8 = storage for _KEY
 * $R9 = _KEY and _RESULT
 */
!macro GetSecondInstallPath

  !ifndef ${_MOZFUNC_UN}GetSecondInstallPath
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}GetSecondInstallPath "!insertmacro ${_MOZFUNC_UN}GetSecondInstallPathCall"

    Function ${_MOZFUNC_UN}GetSecondInstallPath
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4
      Push $R3

      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R3

      StrCpy $R4 0       ; set the counter for the loop to 0
      StrCpy $R8 "$R9"   ; Registry key path to search
      StrCpy $R9 "false" ; default return value

      loop:
      EnumRegKey $R7 SHCTX $R8 $R4
      StrCmp $R7 "" end +1  ; if empty there are no more keys to enumerate
      IntOp $R4 $R4 + 1     ; increment the loop's counter
      ClearErrors
      ReadRegStr $R5 SHCTX "$R8\$R7\bin" "PathToExe"
      IfErrors loop

      ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R5" $R5

      IfFileExists "$R5" +1 loop
      ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
      ${${_MOZFUNC_UN}GetParent} "$R5" $R6
      StrCmp "$R6" "$R3" loop +1
      StrCmp "$R6\${FileMainEXE}" "$R5" +1 loop
      StrCpy $R9 "$R5"

      end:
      ClearErrors

      Pop $R3
      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro GetSecondInstallPathCall _KEY _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call GetSecondInstallPath
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.GetSecondInstallPathCall _KEY _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call un.GetSecondInstallPath
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.GetSecondInstallPath
  !ifndef un.GetSecondInstallPath
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro GetSecondInstallPath

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Finds an existing installation path for the application based on the
 * application's executable name so we can default to using this path for the
 * install. If there is zero or more than one installation of the application
 * then we default to the default installation path. This uses SHCTX to
 * determine the registry hive to read from so you must call SetShellVarContext
 * first.
 *
 * @param   _KEY
 *          The registry subkey (typically this will be Software\Mozilla\App Name).
 * @return  _RESULT
 *          false if a single install location for this app name isn't found,
 *          path to the install directory if a single install location is found.
 *
 * $R5 = counter for the loop's EnumRegKey
 * $R6 = return value from EnumRegKey
 * $R7 = return value from ReadRegStr
 * $R8 = storage for _KEY
 * $R9 = _KEY and _RESULT
 */
!macro GetSingleInstallPath

  !ifndef ${_MOZFUNC_UN}GetSingleInstallPath
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}GetSingleInstallPath "!insertmacro ${_MOZFUNC_UN}GetSingleInstallPathCall"

    Function ${_MOZFUNC_UN}GetSingleInstallPath
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5

      StrCpy $R8 $R9
      StrCpy $R9 "false"
      StrCpy $R5 0  ; set the counter for the loop to 0

      loop:
      ClearErrors
      EnumRegKey $R6 SHCTX $R8 $R5
      IfErrors cleanup
      StrCmp $R6 "" cleanup +1  ; if empty there are no more keys to enumerate
      IntOp $R5 $R5 + 1         ; increment the loop's counter
      ClearErrors
      ReadRegStr $R7 SHCTX "$R8\$R6\Main" "PathToExe"
      IfErrors loop
      ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R7" $R7
      GetFullPathName $R7 "$R7"
      IfErrors loop

      StrCmp "$R9" "false" +1 +3
      StrCpy $R9 "$R7"
      GoTo Loop

      StrCpy $R9 "false"

      cleanup:
      StrCmp $R9 "false" end +1
      ${${_MOZFUNC_UN}GetLongPath} "$R9" $R9
      ${${_MOZFUNC_UN}GetParent} "$R9" $R9

      end:
      ClearErrors

      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro GetSingleInstallPathCall _KEY _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call GetSingleInstallPath
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.GetSingleInstallPathCall _KEY _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call un.GetSingleInstallPath
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.GetSingleInstallPath
  !ifndef un.GetSingleInstallPath
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro GetSingleInstallPath

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Find the first existing installation for the application.
 * This is similar to GetSingleInstallPath, except that it always returns the
 * first path it finds, instead of an error when more than one path exists.
 *
 * The shell context and the registry view should already have been set.
 *
 * @param   _KEY
 *          The registry subkey (typically Software\Mozilla\App Name).
 * @return  _RESULT
 *          path to the install directory of the first location found, or
 *          the string "false" if no existing installation was found.
 *
 * $R5 = counter for the loop's EnumRegKey
 * $R6 = return value from EnumRegKey
 * $R7 = return value from ReadRegStr
 * $R8 = storage for _KEY
 * $R9 = _KEY and _RESULT
 */
!macro GetFirstInstallPath
  !ifndef ${_MOZFUNC_UN}GetFirstInstallPath
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}GetFirstInstallPath "!insertmacro ${_MOZFUNC_UN}__GetFirstInstallPathCall"

    Function ${_MOZFUNC_UN}__GetFirstInstallPath
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5

      StrCpy $R8 $R9
      StrCpy $R9 "false"
      StrCpy $R5 0

      ${Do}
        ClearErrors
        EnumRegKey $R6 SHCTX $R8 $R5
        ${If} ${Errors}
        ${OrIf} $R6 == ""
          ${Break}
        ${EndIf}

        IntOp $R5 $R5 + 1

        ReadRegStr $R7 SHCTX "$R8\$R6\Main" "PathToExe"
        ${If} ${Errors}
          ${Continue}
        ${EndIf}

        ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R7" $R7
        GetFullPathName $R7 "$R7"
        ${If} ${Errors}
          ${Continue}
        ${EndIf}

        StrCpy $R9 "$R7"
        ${Break}
      ${Loop}

      ${If} $R9 != "false"
        ${${_MOZFUNC_UN}GetLongPath} "$R9" $R9
        ${${_MOZFUNC_UN}GetParent} "$R9" $R9
      ${EndIf}

      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro __GetFirstInstallPathCall _KEY _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call __GetFirstInstallPath
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.__GetFirstInstallPathCall _KEY _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call un.__GetFirstInstallPath
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.__GetFirstInstallPath
  !ifndef un.__GetFirstInstallPath
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro __GetFirstInstallPath

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend


################################################################################
# Macros for working with the file system

/**
 * Attempts to delete a file if it exists. This will fail if the file is in use.
 *
 * @param   _FILE
 *          The path to the file that is to be deleted.
 */
!macro DeleteFile _FILE
  ${If} ${FileExists} "${_FILE}"
    Delete "${_FILE}"
  ${EndIf}
!macroend
!define DeleteFile "!insertmacro DeleteFile"

/**
 * Removes a directory if it exists and is empty.
 *
 * @param   _DIR
 *          The path to the directory that is to be removed.
 */
!macro RemoveDir _DIR
  ${If} ${FileExists} "${_DIR}"
    RmDir "${_DIR}"
  ${EndIf}
!macroend
!define RemoveDir "!insertmacro RemoveDir"

/**
 * Checks whether it is possible to create and delete a directory and a file in
 * the install directory. Creation and deletion of files and directories are
 * checked since a user may have rights for one and not the other. If creation
 * and deletion of a file and a directory are successful this macro will return
 * true... if not, this it return false.
 *
 * @return  _RESULT
 *          true if files and directories can be created and deleted in the
 *          install directory otherwise false.
 *
 * $R8 = temporary filename in the installation directory returned from
 *       GetTempFileName.
 * $R9 = _RESULT
 */
!macro CanWriteToInstallDir

  !ifndef ${_MOZFUNC_UN}CanWriteToInstallDir
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}CanWriteToInstallDir "!insertmacro ${_MOZFUNC_UN}CanWriteToInstallDirCall"

    Function ${_MOZFUNC_UN}CanWriteToInstallDir
      Push $R9
      Push $R8

      StrCpy $R9 "true"

      ; IfFileExists returns false for $INSTDIR when $INSTDIR is the root of a
      ; UNC path so always try to create $INSTDIR
      CreateDirectory "$INSTDIR\"
      GetTempFileName $R8 "$INSTDIR\"

      ${Unless} ${FileExists} $R8 ; Can files be created?
        StrCpy $R9 "false"
        Goto done
      ${EndUnless}

      Delete $R8
      ${If} ${FileExists} $R8 ; Can files be deleted?
        StrCpy $R9 "false"
        Goto done
      ${EndIf}

      CreateDirectory $R8
      ${Unless} ${FileExists} $R8  ; Can directories be created?
        StrCpy $R9 "false"
        Goto done
      ${EndUnless}

      RmDir $R8
      ${If} ${FileExists} $R8  ; Can directories be deleted?
        StrCpy $R9 "false"
        Goto done
      ${EndIf}

      done:

      RmDir "$INSTDIR\" ; Only remove $INSTDIR if it is empty
      ClearErrors

      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro CanWriteToInstallDirCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call CanWriteToInstallDir
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.CanWriteToInstallDirCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.CanWriteToInstallDir
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.CanWriteToInstallDir
  !ifndef un.CanWriteToInstallDir
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro CanWriteToInstallDir

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Checks whether there is sufficient free space available for the installation
 * directory using GetDiskFreeSpaceExW which respects disk quotas. This macro
 * will calculate the size of all sections that are selected, compare that with
 * the free space available, and if there is sufficient free space it will
 * return true... if not, it will return false.
 *
 * @return  _RESULT
 *          "true" if there is sufficient free space otherwise "false".
 *
 * $R5 = return value from SectionGetSize
 * $R6 = return value from SectionGetFlags
 *       return value from an 'and' comparison of SectionGetFlags (1=selected)
 *       return value for lpFreeBytesAvailable from GetDiskFreeSpaceExW
 *       return value for System::Int64Op $R6 / 1024
 *       return value for System::Int64Op $R6 > $R8
 * $R7 = the counter for enumerating the sections
 *       the temporary file name for the directory created under $INSTDIR passed
 *       to GetDiskFreeSpaceExW.
 * $R8 = sum in KB of all selected sections
 * $R9 = _RESULT
 */
!macro CheckDiskSpace

  !ifndef ${_MOZFUNC_UN}CheckDiskSpace
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}CheckDiskSpace "!insertmacro ${_MOZFUNC_UN}CheckDiskSpaceCall"

    Function ${_MOZFUNC_UN}CheckDiskSpace
      Push $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5

      ClearErrors

      StrCpy $R9 "true" ; default return value
      StrCpy $R8 "0"    ; sum in KB of all selected sections
      StrCpy $R7 "0"    ; counter for enumerating sections

      ; Enumerate the sections and sum up the sizes of the sections that are
      ; selected.
      SectionGetFlags $R7 $R6
      IfErrors +7 +1
      IntOp $R6 ${SF_SELECTED} & $R6
      IntCmp $R6 0 +3 +1 +1
      SectionGetSize $R7 $R5
      IntOp $R8 $R8 + $R5
      IntOp $R7 $R7 + 1
      GoTo -7

      ; The directory passed to GetDiskFreeSpaceExW must exist for the call to
      ; succeed.  Since the CanWriteToInstallDir macro is called prior to this
      ; macro the call to CreateDirectory will always succeed.

      ; IfFileExists returns false for $INSTDIR when $INSTDIR is the root of a
      ; UNC path so always try to create $INSTDIR
      CreateDirectory "$INSTDIR\"
      GetTempFileName $R7 "$INSTDIR\"
      Delete "$R7"
      CreateDirectory "$R7"

      System::Call 'kernel32::GetDiskFreeSpaceExW(w, *l, *l, *l) i(R7, .R6, ., .) .'

      ; Convert to KB for comparison with $R8 which is in KB
      System::Int64Op $R6 / 1024
      Pop $R6

      System::Int64Op $R6 > $R8
      Pop $R6

      IntCmp $R6 1 end +1 +1
      StrCpy $R9 "false"

      end:
      RmDir "$R7"
      RmDir "$INSTDIR\" ; Only remove $INSTDIR if it is empty

      ClearErrors

      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro CheckDiskSpaceCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call CheckDiskSpace
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.CheckDiskSpaceCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.CheckDiskSpace
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro un.CheckDiskSpace
  !ifndef un.CheckDiskSpace
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro CheckDiskSpace

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
* Returns the path found within a passed in string. The path is quoted or not
* with the exception of an unquoted non 8dot3 path without arguments that is
* also not a DefaultIcon path, is a 8dot3 path or not, has command line
* arguments, or is a registry DefaultIcon path (e.g. <path to binary>,# where #
* is the icon's resuorce id). The string does not need to be a valid path or
* exist. It is up to the caller to pass in a string of one of the forms noted
* above and to verify existence if necessary.
*
* Examples:
* In:  C:\PROGRA~1\MOZILL~1\FIREFOX.EXE -flag "%1"
* In:  C:\PROGRA~1\MOZILL~1\FIREFOX.EXE,0
* In:  C:\PROGRA~1\MOZILL~1\FIREFOX.EXE
* In:  "C:\PROGRA~1\MOZILL~1\FIREFOX.EXE"
* In:  "C:\PROGRA~1\MOZILL~1\FIREFOX.EXE" -flag "%1"
* Out: C:\PROGRA~1\MOZILL~1\FIREFOX.EXE
*
* In:  "C:\Program Files\Mozilla Firefox\firefox.exe" -flag "%1"
* In:  C:\Program Files\Mozilla Firefox\firefox.exe,0
* In:  "C:\Program Files\Mozilla Firefox\firefox.exe"
* Out: C:\Program Files\Mozilla Firefox\firefox.exe
*
* @param   _IN_PATH
*          The string containing the path.
* @param   _OUT_PATH
*          The register to store the path to.
*
* $R7 = counter for the outer loop's EnumRegKey
* $R8 = return value from ReadRegStr
* $R9 = _IN_PATH and _OUT_PATH
*/
!macro GetPathFromString

  !ifndef ${_MOZFUNC_UN}GetPathFromString
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}GetPathFromString "!insertmacro ${_MOZFUNC_UN}GetPathFromStringCall"

    Function ${_MOZFUNC_UN}GetPathFromString
      Exch $R9
      Push $R8
      Push $R7

      StrCpy $R7 0          ; Set the counter to 0.

      ; Handle quoted paths with arguments.
      StrCpy $R8 $R9 1      ; Copy the first char.
      StrCmp $R8 '"' +2 +1  ; Is it a "?
      StrCmp $R8 "'" +1 +9  ; Is it a '?
      StrCpy $R9 $R9 "" 1   ; Remove the first char.
      IntOp $R7 $R7 + 1     ; Increment the counter.
      StrCpy $R8 $R9 1 $R7  ; Starting from the counter copy the next char.
      StrCmp $R8 "" end +1  ; Are there no more chars?
      StrCmp $R8 '"' +2 +1  ; Is it a " char?
      StrCmp $R8 "'" +1 -4  ; Is it a ' char?
      StrCpy $R9 $R9 $R7    ; Copy chars up to the counter.
      GoTo end

      ; Handle DefaultIcon paths. DefaultIcon paths are not quoted and end with
      ; a , and a number.
      IntOp $R7 $R7 - 1     ; Decrement the counter.
      StrCpy $R8 $R9 1 $R7  ; Copy one char from the end minus the counter.
      StrCmp $R8 '' +4 +1   ; Are there no more chars?
      StrCmp $R8 ',' +1 -3  ; Is it a , char?
      StrCpy $R9 $R9 $R7    ; Copy chars up to the end minus the counter.
      GoTo end

      ; Handle unquoted paths with arguments. An unquoted path with arguments
      ; must be an 8dot3 path.
      StrCpy $R7 -1          ; Set the counter to -1 so it will start at 0.
      IntOp $R7 $R7 + 1      ; Increment the counter.
      StrCpy $R8 $R9 1 $R7   ; Starting from the counter copy the next char.
      StrCmp $R8 "" end +1   ; Are there no more chars?
      StrCmp $R8 " " +1 -3   ; Is it a space char?
      StrCpy $R9 $R9 $R7     ; Copy chars up to the counter.

      end:
      ClearErrors

      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro GetPathFromStringCall _IN_PATH _OUT_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_IN_PATH}"
  Call GetPathFromString
  Pop ${_OUT_PATH}
  !verbose pop
!macroend

!macro un.GetPathFromStringCall _IN_PATH _OUT_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_IN_PATH}"
  Call un.GetPathFromString
  Pop ${_OUT_PATH}
  !verbose pop
!macroend

!macro un.GetPathFromString
  !ifndef un.GetPathFromString
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro GetPathFromString

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Removes the quotes from each end of a string if present.
 *
 * @param   _IN_PATH
 *          The string containing the path.
 * @param   _OUT_PATH
 *          The register to store the long path.
 *
 * $R7 = storage for single character comparison
 * $R8 = storage for _IN_PATH
 * $R9 = _IN_PATH and _OUT_PATH
 */
!macro RemoveQuotesFromPath

  !ifndef ${_MOZFUNC_UN}RemoveQuotesFromPath
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}RemoveQuotesFromPath "!insertmacro ${_MOZFUNC_UN}RemoveQuotesFromPathCall"

    Function ${_MOZFUNC_UN}RemoveQuotesFromPath
      Exch $R9
      Push $R8
      Push $R7

      StrCpy $R7 "$R9" 1
      StrCmp $R7 "$\"" +1 +2
      StrCpy $R9 "$R9" "" 1

      StrCpy $R7 "$R9" "" -1
      StrCmp $R7 "$\"" +1 +2
      StrCpy $R9 "$R9" -1

      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro RemoveQuotesFromPathCall _IN_PATH _OUT_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_IN_PATH}"
  Call RemoveQuotesFromPath
  Pop ${_OUT_PATH}
  !verbose pop
!macroend

!macro un.RemoveQuotesFromPathCall _IN_PATH _OUT_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_IN_PATH}"
  Call un.RemoveQuotesFromPath
  Pop ${_OUT_PATH}
  !verbose pop
!macroend

!macro un.RemoveQuotesFromPath
  !ifndef un.RemoveQuotesFromPath
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro RemoveQuotesFromPath

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Returns the long path for an existing file or directory. GetLongPathNameW
 * may not be available on Win95 if Microsoft Layer for Unicode is not
 * installed and GetFullPathName only returns a long path for the last file or
 * directory that doesn't end with a \ in the path that it is passed. If the
 * path does not exist on the file system this will return an empty string. To
 * provide a consistent result trailing back-slashes are always removed.
 *
 * Note: 1024 used by GetLongPathNameW is the maximum NSIS string length.
 *
 * @param   _IN_PATH
 *          The string containing the path.
 * @param   _OUT_PATH
 *          The register to store the long path.
 *
 * $R4 = counter value when the previous \ was found
 * $R5 = directory or file name found during loop
 * $R6 = return value from GetLongPathNameW and loop counter
 * $R7 = long path from GetLongPathNameW and single char from path for comparison
 * $R8 = storage for _IN_PATH
 * $R9 = _IN_PATH _OUT_PATH
 */
!macro GetLongPath

  !ifndef ${_MOZFUNC_UN}GetLongPath
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}GetLongPath "!insertmacro ${_MOZFUNC_UN}GetLongPathCall"

    Function ${_MOZFUNC_UN}GetLongPath
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4

      ClearErrors

      GetFullPathName $R8 "$R9"
      IfErrors end_GetLongPath +1 ; If the path doesn't exist return an empty string.

      ; Make the drive letter uppercase.
      StrCpy $R9 "$R8" 1    ; Copy the first char.
      StrCpy $R8 "$R8" "" 1 ; Copy everything after the first char.
      ; Convert the first char to uppercase.
      System::Call "User32::CharUpper(w R9 R9)i"
      StrCpy $R8 "$R9$R8"   ; Copy the uppercase char and the rest of the chars.

      ; Do it the hard way.
      StrCpy $R4 0     ; Stores the position in the string of the last \ found.
      StrCpy $R6 -1    ; Set the counter to -1 so it will start at 0.

      loop_GetLongPath:
      IntOp $R6 $R6 + 1      ; Increment the counter.
      StrCpy $R7 $R8 1 $R6   ; Starting from the counter copy the next char.
      StrCmp $R7 "" +2 +1    ; Are there no more chars?
      StrCmp $R7 "\" +1 -3   ; Is it a \?

      ; Copy chars starting from the previously found \ to the counter.
      StrCpy $R5 $R8 $R6 $R4

      ; If this is the first \ found we want to swap R9 with R5 so a \ will
      ; be appended to the drive letter and colon (e.g. C: will become C:\).
      StrCmp $R4 0 +1 +3
      StrCpy $R9 $R5
      StrCpy $R5 ""

      GetFullPathName $R9 "$R9\$R5"

      StrCmp $R7 "" end_GetLongPath +1 ; Are there no more chars?

      ; Store the counter for the current \ and prefix it for StrCpy operations.
      StrCpy $R4 "+$R6"
      IntOp $R6 $R6 + 1      ; Increment the counter so we skip over the \.
      StrCpy $R8 $R8 "" $R6  ; Copy chars starting from the counter to the end.
      StrCpy $R6 -1          ; Reset the counter to -1 so it will start over at 0.
      GoTo loop_GetLongPath

      end_GetLongPath:
      ; If there is a trailing slash remove it
      StrCmp $R9 "" +4 +1
      StrCpy $R8 "$R9" "" -1
      StrCmp $R8 "\" +1 +2
      StrCpy $R9 "$R9" -1

      ClearErrors

      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro GetLongPathCall _IN_PATH _OUT_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_IN_PATH}"
  Call GetLongPath
  Pop ${_OUT_PATH}
  !verbose pop
!macroend

!macro un.GetLongPathCall _IN_PATH _OUT_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_IN_PATH}"
  Call un.GetLongPath
  Pop ${_OUT_PATH}
  !verbose pop
!macroend

!macro un.GetLongPath
  !ifndef un.GetLongPath
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro GetLongPath

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend


################################################################################
# Macros for cleaning up the registry and file system

/**
 * Removes registry keys that reference this install location and for paths that
 * no longer exist. This uses SHCTX to determine the registry hive so you must
 * call SetShellVarContext first.
 *
 * @param   _KEY
 *          The registry subkey (typically this will be Software\Mozilla).
 *
 * $0  = loop counter
 * $1  = temporary value used for string searches
 * $R0 = on x64 systems set to 'false' at the beginning of the macro when
 *       enumerating the x86 registry view and set to 'true' when enumerating
 *       the x64 registry view.
 * $R1 = stores the long path to $INSTDIR
 * $R2 = return value from the stack from the GetParent and GetLongPath macros
 * $R3 = return value from the outer loop's EnumRegKey and ESR string
 * $R4 = return value from the inner loop's EnumRegKey
 * $R5 = return value from ReadRegStr
 * $R6 = counter for the outer loop's EnumRegKey
 * $R7 = counter for the inner loop's EnumRegKey
 * $R8 = return value from the stack from the RemoveQuotesFromPath macro
 * $R9 = _KEY
 */
!macro RegCleanMain

  !ifndef ${_MOZFUNC_UN}RegCleanMain
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}RegCleanMain "!insertmacro ${_MOZFUNC_UN}RegCleanMainCall"

    Function ${_MOZFUNC_UN}RegCleanMain
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4
      Push $R3
      Push $R2
      Push $R1
      Push $R0
      Push $0
      Push $1

      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R1
      StrCpy $R6 0  ; set the counter for the outer loop to 0

      ${If} ${RunningX64}
      ${OrIf} ${IsNativeARM64}
        StrCpy $R0 "false"
        ; Set the registry to the 32 bit registry for 64 bit installations or to
        ; the 64 bit registry for 32 bit installations at the beginning so it can
        ; easily be set back to the correct registry view when finished.
        !ifdef HAVE_64BIT_BUILD
          SetRegView 32
        !else
          SetRegView 64
        !endif
      ${EndIf}

      outerloop:
      EnumRegKey $R3 SHCTX $R9 $R6
      StrCmp $R3 "" end +1  ; if empty there are no more keys to enumerate
      IntOp $R6 $R6 + 1     ; increment the outer loop's counter
      ClearErrors
      ReadRegStr $R5 SHCTX "$R9\$R3\bin" "PathToExe"
      IfErrors 0 outercontinue
      StrCpy $R7 0  ; set the counter for the inner loop to 0

      innerloop:
      EnumRegKey $R4 SHCTX "$R9\$R3" $R7
      StrCmp $R4 "" outerloop +1  ; if empty there are no more keys to enumerate
      IntOp $R7 $R7 + 1  ; increment the inner loop's counter
      ClearErrors
      ReadRegStr $R5 SHCTX "$R9\$R3\$R4\Main" "PathToExe"
      IfErrors innerloop

      ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R5" $R8
      ${${_MOZFUNC_UN}GetParent} "$R8" $R2
      ${${_MOZFUNC_UN}GetLongPath} "$R2" $R2
      IfFileExists "$R2" +1 innerloop
      StrCmp "$R2" "$R1" +1 innerloop

      ClearErrors
      DeleteRegKey SHCTX "$R9\$R3\$R4"
      IfErrors innerloop
      IntOp $R7 $R7 - 1 ; decrement the inner loop's counter when the key is deleted successfully.
      ClearErrors
      DeleteRegKey /ifempty SHCTX "$R9\$R3"
      IfErrors innerloop outerdecrement

      outercontinue:
      ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R5" $R8
      ${${_MOZFUNC_UN}GetParent} "$R8" $R2
      ${${_MOZFUNC_UN}GetLongPath} "$R2" $R2
      IfFileExists "$R2" +1 outerloop
      StrCmp "$R2" "$R1" +1 outerloop

      ClearErrors
      DeleteRegKey SHCTX "$R9\$R3"
      IfErrors outerloop

      outerdecrement:
      IntOp $R6 $R6 - 1 ; decrement the outer loop's counter when the key is deleted successfully.
      GoTo outerloop

      end:
      ; Check if _KEY\${BrandFullNameInternal} refers to a key that's been
      ; removed, either just now by this function or earlier by something else,
      ; and if so either update it to a key that does exist or remove it if we
      ; can't find anything to update it to.
      ; We'll run this check twice, once looking for non-ESR keys and then again
      ; looking specifically for the separate ESR keys.
      StrCpy $R3 ""
      ${For} $0 0 1
        ClearErrors
        ReadRegStr $R5 SHCTX "$R9\${BrandFullNameInternal}$R3" "CurrentVersion"
        ${IfNot} ${Errors}
          ReadRegStr $R5 SHCTX "$R9\${BrandFullNameInternal}\$R5" ""
          ${If} ${Errors}
            ; Key doesn't exist, update or remove CurrentVersion and default.
            StrCpy $R5 ""
            StrCpy $R6 0
            EnumRegKey $R4 SHCTX "$R9\${BrandFullNameInternal}" $R6
            ${While} $R4 != ""
              ClearErrors
              ${WordFind} "$R4" "esr" "E#" $1
              ${If} $R3 == ""
                ; The key we're looking to update is a non-ESR, so we need to
                ; select only another non-ESR to update it with.
                ${If} ${Errors}
                  StrCpy $R5 "$R4"
                  ${Break}
                ${EndIf}
              ${Else}
                ; The key we're looking to update is an ESR, so we need to
                ; select only another ESR to update it with.
                ${IfNot} ${Errors}
                  StrCpy $R5 "$R4"
                  ${Break}
                ${EndIf}
              ${EndIf}

              IntOp $R6 $R6 + 1
              EnumRegKey $R4 SHCTX "$R9\${BrandFullNameInternal}" $R6
            ${EndWhile}

            ${If} $R5 == ""
              ; We didn't find an install to update the key with, so delete the
              ; CurrentVersion value and the entire key if it has no subkeys.
              DeleteRegValue SHCTX "$R9\${BrandFullNameInternal}$R3" "CurrentVersion"
              DeleteRegValue SHCTX "$R9\${BrandFullNameInternal}$R3" ""
              DeleteRegKey /ifempty SHCTX "$R9\${BrandFullNameInternal}$R3"
            ${Else}
              ; We do have another still-existing install, so update the key to
              ; that version.
              WriteRegStr SHCTX "$R9\${BrandFullNameInternal}$R3" \
                                "CurrentVersion" "$R5"
              ${WordFind} "$R5" " " "+1{" $R5
              WriteRegStr SHCTX "$R9\${BrandFullNameInternal}$R3" "" "$R5"
            ${EndIf}
          ${EndIf}
          ; Else, the key referenced in CurrentVersion still exists,
          ; so there's nothing to update or remove.
        ${EndIf}

        ; Set up for the second iteration of the loop, where we'll be looking
        ; for the separate ESR keys.
        StrCpy $R3 " ESR"
      ${Next}

      ${If} ${RunningX64}
      ${OrIf} ${IsNativeARM64}
        ${If} "$R0" == "false"
          ; Set the registry to the correct view.
          !ifdef HAVE_64BIT_BUILD
            SetRegView 64
          !else
            SetRegView 32
          !endif

          StrCpy $R6 0  ; set the counter for the outer loop to 0
          StrCpy $R0 "true"
          GoTo outerloop
        ${EndIf}
      ${EndIf}

      ClearErrors

      Pop $1
      Pop $0
      Pop $R0
      Pop $R1
      Pop $R2
      Pop $R3
      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro RegCleanMainCall _KEY
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call RegCleanMain
  !verbose pop
!macroend

!macro un.RegCleanMainCall _KEY
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call un.RegCleanMain
  !verbose pop
!macroend

!macro un.RegCleanMain
  !ifndef un.RegCleanMain
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro RegCleanMain

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend


/**
 * Removes registry keys that reference this install location and for paths that
 * no longer exist.
 *
 * @param   _KEY ($R1)
 *          The registry subkey
 *          (typically this will be Software\Mozilla\${AppName}).
 */
!macro RegCleanPrefs
  !ifndef ${_MOZFUNC_UN}RegCleanPrefs
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}RegCleanPrefs "!insertmacro ${_MOZFUNC_UN}RegCleanPrefsCall"

    Function ${_MOZFUNC_UN}RegCleanPrefs
      Exch $R1 ; get _KEY from the stack

      ; These prefs are always stored in the native registry.
      SetRegView 64

      ; Delete the installer prefs key for this installation, if one exists.
      DeleteRegKey HKCU "$R1\Installer\$AppUserModelID"

      ; If there aren't any more installer prefs keys, delete the parent key.
      DeleteRegKey /ifempty HKCU "$R1\Installer"

      SetRegView lastused

      Pop $R1 ; restore the previous $R1
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro RegCleanPrefsCall _KEY
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call RegCleanPrefs
  !verbose pop
!macroend

!macro un.RegCleanPrefsCall _KEY
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_KEY}"
  Call un.RegCleanPrefs
  !verbose pop
!macroend

!macro un.RegCleanPrefs
  !ifndef un.RegCleanPrefs
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro RegCleanPrefs

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Removes all registry keys from \Software\Windows\CurrentVersion\Uninstall
 * that reference this install location in both the 32 bit and 64 bit registry
 * view. This macro uses SHCTX to determine the registry hive so you must call
 * SetShellVarContext first.
 *
 * $R3 = on x64 systems set to 'false' at the beginning of the macro when
 *       enumerating the x86 registry view and set to 'true' when enumerating
 *       the x64 registry view.
 * $R4 = stores the long path to $INSTDIR
 * $R5 = return value from ReadRegStr
 * $R6 = string for the base reg key (e.g. Software\Microsoft\Windows\CurrentVersion\Uninstall)
 * $R7 = return value from EnumRegKey
 * $R8 = counter for EnumRegKey
 * $R9 = return value from the stack from the RemoveQuotesFromPath and GetLongPath macros
 */
!macro RegCleanUninstall

  !ifndef ${_MOZFUNC_UN}RegCleanUninstall
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}RegCleanUninstall "!insertmacro ${_MOZFUNC_UN}RegCleanUninstallCall"

    Function ${_MOZFUNC_UN}RegCleanUninstall
      Push $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4
      Push $R3

      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R4
      StrCpy $R6 "Software\Microsoft\Windows\CurrentVersion\Uninstall"
      StrCpy $R7 ""
      StrCpy $R8 0

      ${If} ${RunningX64}
      ${OrIf} ${IsNativeARM64}
        StrCpy $R3 "false"
        ; Set the registry to the 32 bit registry for 64 bit installations or to
        ; the 64 bit registry for 32 bit installations at the beginning so it can
        ; easily be set back to the correct registry view when finished.
        !ifdef HAVE_64BIT_BUILD
          SetRegView 32
        !else
          SetRegView 64
        !endif
      ${EndIf}

      loop:
      EnumRegKey $R7 SHCTX $R6 $R8
      StrCmp $R7 "" end +1
      IntOp $R8 $R8 + 1 ; Increment the counter
      ClearErrors
      ReadRegStr $R5 SHCTX "$R6\$R7" "InstallLocation"
      IfErrors loop
      ${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R5" $R9

      ; Detect when the path is just a drive letter without a trailing
      ; backslash (e.g., "C:"), and add a backslash. If we don't, the Win32
      ; calls in GetLongPath will interpret that syntax as a shorthand
      ; for the working directory, because that's the DOS 2.0 convention,
      ; and will return the path to that directory instead of just the drive.
      ; Back here, we would then successfully match that with our $INSTDIR,
      ; and end up deleting a registry key that isn't really ours.
      StrLen $R5 "$R9"
      ${If} $R5 == 2
        StrCpy $R5 "$R9" 1 1
        ${If} "$R5" == ":"
          StrCpy $R9 "$R9\"
        ${EndIf}
      ${EndIf}

      ${${_MOZFUNC_UN}GetLongPath} "$R9" $R9
      StrCmp "$R9" "$R4" +1 loop
      ClearErrors
      DeleteRegKey SHCTX "$R6\$R7"
      IfErrors loop +1
      IntOp $R8 $R8 - 1 ; Decrement the counter on successful deletion
      GoTo loop

      end:
      ${If} ${RunningX64}
      ${OrIf} ${IsNativeARM64}
        ${If} "$R3" == "false"
          ; Set the registry to the correct view.
          !ifdef HAVE_64BIT_BUILD
            SetRegView 64
          !else
            SetRegView 32
          !endif

          StrCpy $R7 ""
          StrCpy $R8 0
          StrCpy $R3 "true"
          GoTo loop
        ${EndIf}
      ${EndIf}

      ClearErrors

      Pop $R3
      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Pop $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro RegCleanUninstallCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call RegCleanUninstall
  !verbose pop
!macroend

!macro un.RegCleanUninstallCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.RegCleanUninstall
  !verbose pop
!macroend

!macro un.RegCleanUninstall
  !ifndef un.RegCleanUninstall
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro RegCleanUninstall

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Removes an application specific handler registry key under Software\Classes
 * for both HKCU and HKLM when its open command refers to this install
 * location or the install location doesn't exist.
 *
 * @param   _HANDLER_NAME
 *          The registry name for the handler.
 *
 * $R7 = stores the long path to the $INSTDIR
 * $R8 = stores the path to the open command's parent directory
 * $R9 = _HANDLER_NAME
 */
!macro RegCleanAppHandler

  !ifndef ${_MOZFUNC_UN}RegCleanAppHandler
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}GetPathFromString
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}RegCleanAppHandler "!insertmacro ${_MOZFUNC_UN}RegCleanAppHandlerCall"

    Function ${_MOZFUNC_UN}RegCleanAppHandler
      Exch $R9
      Push $R8
      Push $R7

      ClearErrors
      ReadRegStr $R8 HKCU "Software\Classes\$R9\shell\open\command" ""
      IfErrors next +1
      ${${_MOZFUNC_UN}GetPathFromString} "$R8" $R8
      ${${_MOZFUNC_UN}GetParent} "$R8" $R8
      IfFileExists "$R8" +3 +1
      DeleteRegKey HKCU "Software\Classes\$R9"
      GoTo next

      ${${_MOZFUNC_UN}GetLongPath} "$R8" $R8
      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R7
      StrCmp "$R7" "$R8" +1 next
      DeleteRegKey HKCU "Software\Classes\$R9"

      next:
      ReadRegStr $R8 HKLM "Software\Classes\$R9\shell\open\command" ""
      IfErrors end
      ${${_MOZFUNC_UN}GetPathFromString} "$R8" $R8
      ${${_MOZFUNC_UN}GetParent} "$R8" $R8
      IfFileExists "$R8" +3 +1
      DeleteRegKey HKLM "Software\Classes\$R9"
      GoTo end

      ${${_MOZFUNC_UN}GetLongPath} "$R8" $R8
      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R7
      StrCmp "$R7" "$R8" +1 end
      DeleteRegKey HKLM "Software\Classes\$R9"

      end:

      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro RegCleanAppHandlerCall _HANDLER_NAME
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_HANDLER_NAME}"
  Call RegCleanAppHandler
  !verbose pop
!macroend

!macro un.RegCleanAppHandlerCall _HANDLER_NAME
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_HANDLER_NAME}"
  Call un.RegCleanAppHandler
  !verbose pop
!macroend

!macro un.RegCleanAppHandler
  !ifndef un.RegCleanAppHandler
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro RegCleanAppHandler

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Cleans up the registry for a protocol handler when its open command
 * refers to this install location. For HKCU the registry key is deleted
 * and for HKLM the values set by the application are deleted.
 *
 * @param   _HANDLER_NAME
 *          The registry name for the handler.
 *
 * $R7 = stores the long path to $INSTDIR
 * $R8 = stores the the long path to the open command's parent directory
 * $R9 = _HANDLER_NAME
 */
!macro un.RegCleanProtocolHandler

  !ifndef un.RegCleanProtocolHandler
    !insertmacro un.GetLongPath
    !insertmacro un.GetParent
    !insertmacro un.GetPathFromString

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define un.RegCleanProtocolHandler "!insertmacro un.RegCleanProtocolHandlerCall"

    Function un.RegCleanProtocolHandler
      Exch $R9
      Push $R8
      Push $R7

      ReadRegStr $R8 HKCU "Software\Classes\$R9\shell\open\command" ""
      ${un.GetLongPath} "$INSTDIR" $R7

      StrCmp "$R8" "" next +1
      ${un.GetPathFromString} "$R8" $R8
      ${un.GetParent} "$R8" $R8
      ${un.GetLongPath} "$R8" $R8
      StrCmp "$R7" "$R8" +1 next
      DeleteRegKey HKCU "Software\Classes\$R9"

      next:
      ReadRegStr $R8 HKLM "Software\Classes\$R9\shell\open\command" ""
      StrCmp "$R8" "" end +1
      ${un.GetLongPath} "$INSTDIR" $R7
      ${un.GetPathFromString} "$R8" $R8
      ${un.GetParent} "$R8" $R8
      ${un.GetLongPath} "$R8" $R8
      StrCmp "$R7" "$R8" +1 end
      DeleteRegValue HKLM "Software\Classes\$R9\DefaultIcon" ""
      DeleteRegValue HKLM "Software\Classes\$R9\shell\open" ""
      DeleteRegValue HKLM "Software\Classes\$R9\shell\open\command" ""
      DeleteRegValue HKLM "Software\Classes\$R9\shell\ddeexec" ""
      DeleteRegValue HKLM "Software\Classes\$R9\shell\ddeexec\Application" ""
      DeleteRegValue HKLM "Software\Classes\$R9\shell\ddeexec\Topic" ""

      end:

      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro un.RegCleanProtocolHandlerCall _HANDLER_NAME
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_HANDLER_NAME}"
  Call un.RegCleanProtocolHandler
  !verbose pop
!macroend

/**
 * Cleans up the registry for a file handler when the passed in value equals
 * the default value for the file handler. For HKCU the registry key is deleted
 * and for HKLM the default value is deleted.
 *
 * @param   _HANDLER_NAME
 *          The registry name for the handler.
 * @param   _DEFAULT_VALUE
 *          The value to check for against the handler's default value.
 *
 * $R6 = stores the long path to $INSTDIR
 * $R7 = _DEFAULT_VALUE
 * $R9 = _HANDLER_NAME
 */
!macro RegCleanFileHandler

  !ifndef ${_MOZFUNC_UN}RegCleanFileHandler
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}GetPathFromString
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}RegCleanFileHandler "!insertmacro ${_MOZFUNC_UN}RegCleanFileHandlerCall"

    Function ${_MOZFUNC_UN}RegCleanFileHandler
      Exch $R9
      Exch 1
      Exch $R8
      Push $R7

      DeleteRegValue HKCU "Software\Classes\$R9\OpenWithProgids" $R8
      EnumRegValue $R7 HKCU "Software\Classes\$R9\OpenWithProgids" 0
      StrCmp "$R7" "" +1 +2
      DeleteRegKey HKCU "Software\Classes\$R9\OpenWithProgids"
      ReadRegStr $R7 HKCU "Software\Classes\$R9" ""
      StrCmp "$R7" "$R8" +1 +2
      DeleteRegKey HKCU "Software\Classes\$R9"

      DeleteRegValue HKLM "Software\Classes\$R9\OpenWithProgids" $R8
      EnumRegValue $R7 HKLM "Software\Classes\$R9\OpenWithProgids" 0
      StrCmp "$R7" "" +1 +2
      DeleteRegKey HKLM "Software\Classes\$R9\OpenWithProgids"
      ReadRegStr $R7 HKLM "Software\Classes\$R9" ""
      StrCmp "$R7" "$R8" +1 +2
      DeleteRegValue HKLM "Software\Classes\$R9" ""

      ClearErrors

      Pop $R7
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro RegCleanFileHandlerCall _HANDLER_NAME _DEFAULT_VALUE
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_DEFAULT_VALUE}"
  Push "${_HANDLER_NAME}"
  Call RegCleanFileHandler
  !verbose pop
!macroend

!macro un.RegCleanFileHandlerCall _HANDLER_NAME _DEFAULT_VALUE
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_DEFAULT_VALUE}"
  Push "${_HANDLER_NAME}"
  Call un.RegCleanFileHandler
  !verbose pop
!macroend

!macro un.RegCleanFileHandler
  !ifndef un.RegCleanFileHandler
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro RegCleanFileHandler

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Checks if a handler's open command points to this installation directory.
 * Uses SHCTX to determine the registry hive (e.g. HKLM or HKCU) to check.
 *
 * @param   _HANDLER_NAME
 *          The registry name for the handler.
 * @param   _RESULT
 *          true if it is the handler's open command points to this
 *          installation directory and false if it does not.
 *
 * $R7 = stores the value of the open command and the path macros return values
 * $R8 = stores the handler's registry key name
 * $R9 = _DEFAULT_VALUE and _RESULT
 */
!macro IsHandlerForInstallDir

  !ifndef ${_MOZFUNC_UN}IsHandlerForInstallDir
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}GetPathFromString
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}IsHandlerForInstallDir "!insertmacro ${_MOZFUNC_UN}IsHandlerForInstallDirCall"

    Function ${_MOZFUNC_UN}IsHandlerForInstallDir
      Exch $R9
      Push $R8
      Push $R7

      StrCpy $R8 "$R9"
      StrCpy $R9 "false"
      ReadRegStr $R7 SHCTX "Software\Classes\$R8\shell\open\command" ""

      ${If} $R7 != ""
        ${GetPathFromString} "$R7" $R7
        ${GetParent} "$R7" $R7
        ${GetLongPath} "$R7" $R7
        ${If} $R7 == $INSTDIR
          StrCpy $R9 "true"
        ${EndIf}
      ${EndIf}

      ClearErrors

      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro IsHandlerForInstallDirCall _HANDLER_NAME _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_HANDLER_NAME}"
  Call IsHandlerForInstallDir
  Pop "${_RESULT}"
  !verbose pop
!macroend

!macro un.IsHandlerForInstallDirCall _HANDLER_NAME _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_HANDLER_NAME}"
  Call un.IsHandlerForInstallDir
  Pop "${_RESULT}"
  !verbose pop
!macroend

!macro un.IsHandlerForInstallDir
  !ifndef un.IsHandlerForInstallDir
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro IsHandlerForInstallDir

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Removes the application's VirtualStore directory if present when the
 * installation directory is a sub-directory of the program files directory.
 *
 * $R3 = Local App Data dir
 * $R4 = $PROGRAMFILES/$PROGRAMFILES64 for CleanVirtualStore_Internal
 * $R5 = various path values.
 * $R6 = length of the long path to $PROGRAMFILES32 or $PROGRAMFILES64
 * $R7 = long path to $PROGRAMFILES32 or $PROGRAMFILES64
 * $R8 = length of the long path to $INSTDIR
 * $R9 = long path to $INSTDIR
 */
!macro CleanVirtualStore

  !ifndef ${_MOZFUNC_UN}CleanVirtualStore
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}CleanVirtualStore "!insertmacro ${_MOZFUNC_UN}CleanVirtualStoreCall"

    Function ${_MOZFUNC_UN}CleanVirtualStore
      Push $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4
      Push $R3

      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R9
      ${If} "$R9" != ""
        StrLen $R8 "$R9"

        StrCpy $R4 $PROGRAMFILES32
        Call ${_MOZFUNC_UN}CleanVirtualStore_Internal

        ${If} ${RunningX64}
        ${OrIf} ${IsNativeARM64}
          StrCpy $R4 $PROGRAMFILES64
          Call ${_MOZFUNC_UN}CleanVirtualStore_Internal
        ${EndIf}

      ${EndIf}

      ClearErrors

      Pop $R3
      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Pop $R9
    FunctionEnd

    Function ${_MOZFUNC_UN}CleanVirtualStore_Internal
      ${${_MOZFUNC_UN}GetLongPath} "" $R7
      ${If} "$R7" != ""
        StrLen $R6 "$R7"
        ${If} $R8 < $R6
          ; Copy from the start of $INSTDIR the length of $PROGRAMFILES64
          StrCpy $R5 "$R9" $R6
          ${If} "$R5" == "$R7"
            ; Remove the drive letter and colon from the $INSTDIR long path
            StrCpy $R5 "$R9" "" 2
            ${GetLocalAppDataFolder} $R3
            StrCpy $R5 "$R3\VirtualStore$R5"
            ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
            ${If} "$R5" != ""
            ${AndIf} ${FileExists} "$R5"
              RmDir /r "$R5"
            ${EndIf}
          ${EndIf}
        ${EndIf}
      ${EndIf}
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro CleanVirtualStoreCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call CleanVirtualStore
  !verbose pop
!macroend

!macro un.CleanVirtualStoreCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.CleanVirtualStore
  !verbose pop
!macroend

!macro un.CleanVirtualStore
  !ifndef un.CleanVirtualStore
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro CleanVirtualStore

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Cleans up some old logs in the Maintenance Service directory.
 *
 * @param   _OLD_REL_PATH
 *          The relative path to the profile directory from Local AppData.
 *          Calculated for the old update directory not based on a hash.
 *
 * $R7 = _OLD_REL_PATH
 * $R1 = taskBar ID hash located in registry at SOFTWARE\_OLD_REL_PATH\TaskBarIDs
 * $R6 = long path to $INSTDIR
 */
!macro CleanMaintenanceServiceLogs

  !ifndef ${_MOZFUNC_UN}CleanMaintenanceServiceLogs
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}GetCommonDirectory
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}CleanMaintenanceServiceLogs "!insertmacro ${_MOZFUNC_UN}CleanMaintenanceServiceLogsCall"

    Function ${_MOZFUNC_UN}CleanMaintenanceServiceLogs
      Exch $R7
      Push $R6
      Push $R1

      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR" $R6

      ${If} $R7 != "" ; _OLD_REL_PATH was passed
      ${AndIf} $R6 != "" ; We have the install dir path
        ; Get the taskbar ID hash for this installation path
        ReadRegStr $R1 HKLM "SOFTWARE\$R7\TaskBarIDs" $R6
        ${If} $R1 == ""
          ReadRegStr $R1 HKCU "SOFTWARE\$R7\TaskBarIDs" $R6
        ${EndIf}

        ${If} $R1 != ""
          ; Remove the secure log files that our updater may have created
          ; inside the maintenance service path. There are several files named
          ; with the install hash and an extension indicating the kind of file.
          ; so use a wildcard to delete them all.
          Delete "$PROGRAMFILES32\Mozilla Maintenance Service\UpdateLogs\$R1.*"

          ; If the UpdateLogs directory is now empty, then delete it.
          ; The Maintenance Service uninstaller should do this, but it may not
          ; be up to date enough because of bug 1665193, so doing this here as
          ; well lets us make sure it really happens.
          RmDir "$PROGRAMFILES32\Mozilla Maintenance Service\UpdateLogs"
        ${EndIf}
      ${EndIf}

      ClearErrors

      Pop $R1
      Pop $R6
      Exch $R7
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro CleanMaintenanceServiceLogsCall _OLD_REL_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_OLD_REL_PATH}"
  Call CleanMaintenanceServiceLogs
  !verbose pop
!macroend

!macro un.CleanMaintenanceServiceLogsCall _OLD_REL_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_OLD_REL_PATH}"
  Call un.CleanMaintenanceServiceLogs
  !verbose pop
!macroend

!macro un.CleanMaintenanceServiceLogs
  !ifndef un.CleanMaintenanceServiceLogs
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro CleanMaintenanceServiceLogs

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Removes relevant shortcuts from a given shortcuts log.
 *
 * $R3 = return value from ShellLink::GetShortCutWorkingDirectory
 * $R4 = counter for appending to Shortcut for enumerating the ini file entries
 * $R5 = return value from ShellLink::GetShortCutTarget
 * $R6 = the long path to the Start Menu Programs directory (e.g. $SMPROGRAMS)
 * $R7 = the return value from ReadINIStr for the relative path to the applications
 *       directory under the Start Menu Programs directory and the long path to this
 *       directory
 * $R8 = the return value from ReadINIStr for enumerating shortcuts
 * $R9 = [in] long path to the shortcuts ini file
 */
!macro DeleteShortcutsFromLog
  !ifndef ${_MOZFUNC_UN}DeleteShortcutsFromLog
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}GetCommonDirectory

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}DeleteShortcutsFromLog "!insertmacro ${_MOZFUNC_UN}DeleteShortcutsFromLogCall"

    Function ${_MOZFUNC_UN}DeleteShortcutsFromLog
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4
      Push $R3

      ${If} ${FileExists} "$R9"
        ; Delete Start Menu shortcuts for this application
        StrCpy $R4 -1
        ${Do}
          IntOp $R4 $R4 + 1 ; Increment the counter
          ClearErrors
          ReadINIStr $R8 "$R9" "STARTMENU" "Shortcut$R4"
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}

          ${If} ${FileExists} "$SMPROGRAMS\$R8"
            ShellLink::GetShortCutTarget "$SMPROGRAMS\$R8"
            Pop $R5
            ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
            ; Shortcuts created outside of the installer may
            ; have metadata set on them such that GetShortCutTarget
            ; returns the wrong path on 64-bit systems.
            ; GetShortCutWorkingDirectory is not _quite_ as good
            ; of a test (because it may be unset), but it's not
            ; subject to this bug, and is therefore the next best thing.
            ; https://phabricator.services.mozilla.com/D138197
            ; contains some additional background on this.
            ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8"
            Pop $R3
            ${If} "$INSTDIR\${FileMainEXE}" == "$R5"
            ${OrIf} "$INSTDIR" == "$R3"
              Delete "$SMPROGRAMS\$R8"
            ${EndIf}
          ${EndIf}
        ${Loop}
        ; There might also be a shortcut with a different name created by a
        ; previous version of the installer.
        ${If} ${FileExists} "$SMPROGRAMS\${BrandFullName}.lnk"
          ShellLink::GetShortCutTarget "$SMPROGRAMS\${BrandFullName}.lnk"
          Pop $R5
          ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
          ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8"
          Pop $R3
          ${If} "$INSTDIR\${FileMainEXE}" == "$R5"
          ${OrIf} "$INSTDIR" == "$R3"
            Delete "$SMPROGRAMS\${BrandFullName}.lnk"
          ${EndIf}
        ${EndIf}

        ; Delete Quick Launch shortcuts for this application
        StrCpy $R4 -1
        ${Do}
          IntOp $R4 $R4 + 1 ; Increment the counter
          ClearErrors
          ReadINIStr $R8 "$R9" "QUICKLAUNCH" "Shortcut$R4"
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}

          ${If} ${FileExists} "$QUICKLAUNCH\$R8"
            ShellLink::GetShortCutTarget "$QUICKLAUNCH\$R8"
            Pop $R5
            ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
            ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8"
            Pop $R3
            ${If} "$INSTDIR\${FileMainEXE}" == "$R5"
            ${OrIf} "$INSTDIR" == "$R3"
              Delete "$QUICKLAUNCH\$R8"
            ${EndIf}
          ${EndIf}
        ${Loop}
        ; There might also be a shortcut with a different name created by a
        ; previous version of the installer.
        ${If} ${FileExists} "$QUICKLAUNCH\${BrandFullName}.lnk"
          ShellLink::GetShortCutTarget "$QUICKLAUNCH\${BrandFullName}.lnk"
          Pop $R5
          ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
          ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8"
          Pop $R3
          ${If} "$INSTDIR\${FileMainEXE}" == "$R5"
          ${OrIf} "$INSTDIR" == "$R3"
            Delete "$QUICKLAUNCH\${BrandFullName}.lnk"
          ${EndIf}
        ${EndIf}

        ; Delete Desktop shortcuts for this application
        StrCpy $R4 -1
        ${Do}
          IntOp $R4 $R4 + 1 ; Increment the counter
          ClearErrors
          ReadINIStr $R8 "$R9" "DESKTOP" "Shortcut$R4"
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}

          ${If} ${FileExists} "$DESKTOP\$R8"
            ShellLink::GetShortCutTarget "$DESKTOP\$R8"
            Pop $R5
            ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
            ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8"
            Pop $R3
            ${If} "$INSTDIR\${FileMainEXE}" == "$R5"
            ${OrIf} "$INSTDIR" == "$R3"
              Delete "$DESKTOP\$R8"
            ${EndIf}
          ${EndIf}
        ${Loop}
        ; There might also be a shortcut with a different name created by a
        ; previous version of the installer.
        ${If} ${FileExists} "$DESKTOP\${BrandFullName}.lnk"
          ShellLink::GetShortCutTarget "$DESKTOP\${BrandFullName}.lnk"
          Pop $R5
          ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
          ; We don't bother checking working directory here because we know
          ; these shortcuts were only created by the installer, and thus
          ; not subject to the bug described above.
          ${If} "$INSTDIR\${FileMainEXE}" == "$R5"
            Delete "$DESKTOP\${BrandFullName}.lnk"
          ${EndIf}
        ${EndIf}

        ${${_MOZFUNC_UN}GetLongPath} "$SMPROGRAMS" $R6

        ; Delete Start Menu Programs shortcuts for this application
        ClearErrors
        ReadINIStr $R7 "$R9" "SMPROGRAMS" "RelativePathToDir"
        ${${_MOZFUNC_UN}GetLongPath} "$R6\$R7" $R7
        ${Unless} "$R7" == ""
          StrCpy $R4 -1
          ${Do}
            IntOp $R4 $R4 + 1 ; Increment the counter
            ClearErrors
            ReadINIStr $R8 "$R9" "SMPROGRAMS" "Shortcut$R4"
            ${If} ${Errors}
              ${ExitDo}
            ${EndIf}

            ${If} ${FileExists} "$R7\$R8"
              ShellLink::GetShortCutTarget "$R7\$R8"
              Pop $R5
              ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
              ${If} "$INSTDIR\${FileMainEXE}" == "$R5"
                Delete "$R7\$R8"
              ${EndIf}
            ${EndIf}
          ${Loop}

          ; Delete Start Menu Programs directories for this application
          ${Do}
            ClearErrors
            ${If} "$R6" == "$R7"
              ${ExitDo}
            ${EndIf}
            RmDir "$R7"
            ${If} ${Errors}
              ${ExitDo}
            ${EndIf}
            ${${_MOZFUNC_UN}GetParent} "$R7" $R7
          ${Loop}
        ${EndUnless}
      ${EndIf}

      ClearErrors

      Pop $R3
      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro DeleteShortcutsFromLogCall _SHORTCUTS_LOG
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push ${_SHORTCUTS_LOG}
  Call DeleteShortcutsFromLog
  !verbose pop
!macroend

!macro un.DeleteShortcutsFromLogCall _SHORTCUTS_LOG
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push ${_SHORTCUTS_LOG}
  Call un.DeleteShortcutsFromLog
  !verbose pop
!macroend

!macro un.DeleteShortcutsFromLog
  !ifndef un.DeleteShortcutsFromLog
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro DeleteShortcutsFromLog

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Deletes shortcuts and Start Menu directories under Programs as specified by
 * the shortcuts log ini files and on Windows 7 unpins TaskBar and Start Menu
 * shortcuts. The shortcuts will not be deleted if the shortcut target isn't for
 * this install location which is determined by the shortcut having a target of
 * $INSTDIR\${FileMainEXE} _or_ the working directory of the shortcut being
 * equal to $INSTDIR. The context (All Users or Current User) of the
 * $DESKTOP and $SMPROGRAMS constants depends on the
 * SetShellVarContext setting and must be set by the caller of this macro. There
 * is no All Users context for $QUICKLAUNCH but this will not cause a problem
 * since the macro will just continue past the $QUICKLAUNCH shortcut deletion
 * section on subsequent calls.
 *
 * The meat of this is farmed out to DeleteShortcutsFromLog to facilitate
 * the processing of multiple shortcuts logs.
 *
 * The ini file sections must have the following format (the order of the
 * sections in the ini file is not important):
 * [SMPROGRAMS] -- this section is optional and largely irrelevant these days
 * ; RelativePath is the directory relative from the Start Menu
 * ; Programs directory.
 * RelativePath=Mozilla App
 * ; Shortcut1 is the first shortcut, Shortcut2 is the second shortcut, and so
 * ; on. There must not be a break in the sequence of the numbers.
 * Shortcut1=Mozilla App.lnk
 * Shortcut2=Mozilla App (Safe Mode).lnk
 * [DESKTOP]
 * ; Shortcut1 is the first shortcut, Shortcut2 is the second shortcut, and so
 * ; on. There must not be a break in the sequence of the numbers.
 * Shortcut1=Mozilla App.lnk
 * Shortcut2=Mozilla App (Safe Mode).lnk
 * [QUICKLAUNCH]
 * ; Shortcut1 is the first shortcut, Shortcut2 is the second shortcut, and so
 * ; on. There must not be a break in the sequence of the numbers for the
 * ; suffix.
 * Shortcut1=Mozilla App.lnk
 * Shortcut2=Mozilla App (Safe Mode).lnk
 * [STARTMENU]
 * ; Shortcut1 is the first shortcut, Shortcut2 is the second shortcut, and so
 * ; on. There must not be a break in the sequence of the numbers for the
 * ; suffix.
 * Shortcut1=Mozilla App.lnk
 * Shortcut2=Mozilla App (Safe Mode).lnk
 *
 * $R4 = return value from ShellLink::GetShortCutWorkingDirectory
 * $R5 = return value from ShellLink::GetShortCutTarget and
 *       ApplicationID::UninstallPinnedItem
 * $R6 = find handle
 * $R7 = path to the $QUICKLAUNCH\User Pinned directory
 * $R8 = return filename from FindFirst / FindNext
 * $R9 = long path to the shortcut log ini file and path to the ProgramData
 *       directory that the application stores additional shortcut logs in
 *       (Typically c:\ProgramData\Mozilla-1de4eec8-1241-4177-a864-e594e8d1fb38)
 */
!macro DeleteShortcuts

  !ifndef ${_MOZFUNC_UN}DeleteShortcuts
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}DeleteShortcutsFromLog
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}DeleteShortcuts "!insertmacro ${_MOZFUNC_UN}DeleteShortcutsCall"

    Function ${_MOZFUNC_UN}DeleteShortcuts
      Push $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4

      ; We call ApplicationID::UninstallPinnedItem once per shortcut here
      ; (and explicitly not in DeleteShortcutsFromLog). Calling it again later
      ; would remove the association of side by side installations.
      ; Since shortcuts that are pinned can later be removed without removing
      ; the pinned shortcut unpin the pinned shortcuts for the application's
      ; main exe using the pinned shortcuts themselves.
      StrCpy $R7 "$QUICKLAUNCH\User Pinned"

      ${If} ${FileExists} "$R7\TaskBar"
        ; Delete TaskBar pinned shortcuts for the application's main exe
        FindFirst $R6 $R8 "$R7\TaskBar\*.lnk"
        ${Do}
          ${If} ${FileExists} "$R7\TaskBar\$R8"
            ShellLink::GetShortCutTarget "$R7\TaskBar\$R8"
            Pop $R5
            ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
            ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8"
            Pop $R4
            ${If} "$R5" == "$INSTDIR\${FileMainEXE}"
            ${OrIf} "$R4" == "$INSTDIR"
              ApplicationID::UninstallPinnedItem "$R7\TaskBar\$R8"
              Pop $R5
            ${EndIf}
          ${EndIf}
          ClearErrors
          FindNext $R6 $R8
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}
        ${Loop}
        FindClose $R6
      ${EndIf}

      ${If} ${FileExists} "$R7\StartMenu"
        ; Delete Start Menu pinned shortcuts for the application's main exe
        FindFirst $R6 $R8 "$R7\StartMenu\*.lnk"
        ${Do}
          ${If} ${FileExists} "$R7\StartMenu\$R8"
            ShellLink::GetShortCutTarget "$R7\StartMenu\$R8"
            Pop $R5
            ${${_MOZFUNC_UN}GetLongPath} "$R5" $R5
            ShellLink::GetShortCutWorkingDirectory "$SMPROGRAMS\$R8"
            Pop $R4
            ${If} "$R5" == "$INSTDIR\${FileMainEXE}"
            ${OrIf} "$R4" == "$INSTDIR"
                ApplicationID::UninstallPinnedItem "$R7\StartMenu\$R8"
                Pop $R5
            ${EndIf}
          ${EndIf}
          ClearErrors
          FindNext $R6 $R8
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}
        ${Loop}
        FindClose $R6
      ${EndIf}

      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" $R9
      ${${_MOZFUNC_UN}DeleteShortcutsFromLog} $R9

      ${${_MOZFUNC_UN}GetCommonDirectory} $R9
      ; Shortcut logs created by an application are in a different directory,
      ; and named after both the application and the user SID, eg:
      ; Firefox_S-1-5-21-1004336348-1177238915-682003330-512_shortcuts.ini
      FindFirst $R6 $R8 "$R9\*_shortcuts.ini"
      ${DoUntil} ${Errors}
        ${If} ${FileExists} "$R9\$R8"
          ${${_MOZFUNC_UN}DeleteShortcutsFromLog} "$R9\$R8"
        ${EndIf}
        ClearErrors
        FindNext $R6 $R8
      ${Loop}

      ClearErrors

      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Pop $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro DeleteShortcutsCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call DeleteShortcuts
  !verbose pop
!macroend

!macro un.DeleteShortcutsCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.DeleteShortcuts
  !verbose pop
!macroend

!macro un.DeleteShortcuts
  !ifndef un.DeleteShortcuts
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro DeleteShortcuts

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend


################################################################################
# Macros for parsing and updating the uninstall.log

/**
 * Updates the uninstall.log with new files added by software update.
 *
 * When modifying this macro be aware that LineFind uses all registers except
 * $R0-$R3 and TextCompareNoDetails uses all registers except $R0-$R9 so be
 * cautious. Callers of this macro are not affected.
 */
!macro UpdateUninstallLog

  !ifndef UpdateUninstallLog
    !insertmacro FileJoin
    !insertmacro LineFind
    !insertmacro TextCompareNoDetails
    !insertmacro TrimNewLines

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define UpdateUninstallLog "!insertmacro UpdateUninstallLogCall"

    Function UpdateUninstallLog
      Push $R3
      Push $R2
      Push $R1
      Push $R0

      ClearErrors

      GetFullPathName $R3 "$INSTDIR\uninstall"
      ${If} ${FileExists} "$R3\uninstall.update"
        ${LineFind} "$R3\uninstall.update" "" "1:-1" "CleanupUpdateLog"

        GetTempFileName $R2 "$R3"
        FileOpen $R1 "$R2" w
        ${TextCompareNoDetails} "$R3\uninstall.update" "$R3\uninstall.log" "SlowDiff" "CreateUpdateDiff"
        FileClose $R1

        IfErrors +2 0
        ${FileJoin} "$R3\uninstall.log" "$R2" "$R3\uninstall.log"

        ${DeleteFile} "$R2"
      ${EndIf}

      ClearErrors

      Pop $R0
      Pop $R1
      Pop $R2
      Pop $R3
    FunctionEnd

    ; This callback MUST use labels vs. relative line numbers.
    Function CleanupUpdateLog
      StrCpy $R2 "$R9" 12
      StrCmp "$R2" "EXECUTE ADD " +1 skip
      StrCpy $R9 "$R9" "" 12

      Push $R6
      Push $R5
      Push $R4
      StrCpy $R4 ""         ; Initialize to an empty string.
      StrCpy $R6 -1         ; Set the counter to -1 so it will start at 0.

      loop:
      IntOp $R6 $R6 + 1     ; Increment the counter.
      StrCpy $R5 $R9 1 $R6  ; Starting from the counter copy the next char.
      StrCmp $R5 "" copy    ; Are there no more chars?
      StrCmp $R5 "/" +1 +2  ; Is the char a /?
      StrCpy $R5 "\"        ; Replace the char with a \.

      StrCpy $R4 "$R4$R5"
      GoTo loop

      copy:
      StrCpy $R9 "File: \$R4"
      Pop $R6
      Pop $R5
      Pop $R4
      GoTo end

      skip:
      StrCpy $0 "SkipWrite"

      end:
      Push $0
    FunctionEnd

    Function CreateUpdateDiff
      ${TrimNewLines} "$9" $9
      ${If} $9 != ""
        FileWrite $R1 "$9$\r$\n"
      ${EndIf}

      Push 0
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro UpdateUninstallLogCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call UpdateUninstallLog
  !verbose pop
!macroend

/**
 * Copies files from a source directory to a destination directory with logging
 * to the uninstall.log. If any destination files are in use a reboot will be
 * necessary to complete the installation and the reboot flag (see IfRebootFlag
 * in the NSIS documentation).
 *
 * @param   _PATH_TO_SOURCE
 *          Source path to copy the files from. This must not end with a \.
 *
 * @param   _PATH_TO_DESTINATION
 *          Destination path to copy the files to. This must not end with a \.
 *
 * @param   _PREFIX_ERROR_CREATEDIR
 *          Prefix for the directory creation error message. The directory path
 *          will be inserted below this string.
 *
 * @param   _SUFFIX_ERROR_CREATEDIR
 *          Suffix for the directory creation error message. The directory path
 *          will be inserted above this string.
 *
 * $0  = destination file's parent directory used in the create_dir label
 * $R0 = copied value from $R6 (e.g. _PATH_TO_SOURCE)
 * $R1 = copied value from $R7 (e.g. _PATH_TO_DESTINATION)
 * $R2 = string length of the path to source
 * $R3 = relative path from the path to source
 * $R4 = copied value from $R8 (e.g. _PREFIX_ERROR_CREATEDIR)
 * $R5 = copied value from $R9 (e.g. _SUFFIX_ERROR_CREATEDIR)
 * note: the LocateNoDetails macro uses these registers so we copy the values
 *       to other registers.
 * $R6 = initially _PATH_TO_SOURCE and then set to "size" ($R6="" if directory,
 *       $R6="0" if file with /S=)"path\name" in callback
 * $R7 = initially _PATH_TO_DESTINATION and then set to "name" in callback
 * $R8 = initially _PREFIX_ERROR_CREATEDIR and then set to "path" in callback
 * $R9 = initially _SUFFIX_ERROR_CREATEDIR and then set to "path\name" in
 *       callback
 */
!macro CopyFilesFromDir

  !ifndef CopyFilesFromDir
    !insertmacro LocateNoDetails
    !insertmacro OnEndCommon
    !insertmacro WordReplace

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define CopyFilesFromDir "!insertmacro CopyFilesFromDirCall"

    Function CopyFilesFromDir
      Exch $R9
      Exch 1
      Exch $R8
      Exch 2
      Exch $R7
      Exch 3
      Exch $R6
      Push $R5
      Push $R4
      Push $R3
      Push $R2
      Push $R1
      Push $R0
      Push $0

      StrCpy $R0 "$R6"
      StrCpy $R1 "$R7"
      StrCpy $R4 "$R8"
      StrCpy $R5 "$R9"

      StrLen $R2 "$R0"

      ${LocateNoDetails} "$R0" "/L=FD" "CopyFileCallback"

      Pop $0
      Pop $R0
      Pop $R1
      Pop $R2
      Pop $R3
      Pop $R4
      Pop $R5
      Exch $R6
      Exch 3
      Exch $R7
      Exch 2
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    Function CopyFileCallback
      StrCpy $R3 $R8 "" $R2 ; $R3 always begins with a \.

      retry:
      ClearErrors
      StrCmp $R6 "" +1 copy_file
      IfFileExists "$R1$R3\$R7" end +1
      StrCpy $0 "$R1$R3\$R7"

      create_dir:
      ClearErrors
      CreateDirectory "$0"
      IfFileExists "$0" +1 err_create_dir  ; protect against looping.
      ${LogMsg} "Created Directory: $0"
      StrCmp $R6 "" end copy_file

      err_create_dir:
      ${LogMsg} "** ERROR Creating Directory: $0 **"
      MessageBox MB_RETRYCANCEL|MB_ICONQUESTION "$R4$\r$\n$\r$\n$0$\r$\n$\r$\n$R5" IDRETRY retry
      ${OnEndCommon}
      Quit

      copy_file:
      StrCpy $0 "$R1$R3"
      StrCmp "$0" "$INSTDIR" +2 +1
      IfFileExists "$0" +1 create_dir

      ClearErrors
      ${DeleteFile} "$R1$R3\$R7"
      IfErrors +1 dest_clear
      ClearErrors
      Rename "$R1$R3\$R7" "$R1$R3\$R7.moz-delete"
      IfErrors +1 reboot_delete

      ; file will replace destination file on reboot
      Rename "$R9" "$R9.moz-upgrade"
      CopyFiles /SILENT "$R9.moz-upgrade" "$R1$R3"
      Rename /REBOOTOK "$R1$R3\$R7.moz-upgrade" "$R1$R3\$R7"
      ${LogMsg} "Copied File: $R1$R3\$R7.moz-upgrade"
      ${LogMsg} "Delayed Install File (Reboot Required): $R1$R3\$R7"
      GoTo log_uninstall

      ; file will be deleted on reboot
      reboot_delete:
      CopyFiles /SILENT $R9 "$R1$R3"
      Delete /REBOOTOK "$R1$R3\$R7.moz-delete"
      ${LogMsg} "Installed File: $R1$R3\$R7"
      ${LogMsg} "Delayed Delete File (Reboot Required): $R1$R3\$R7.moz-delete"
      GoTo log_uninstall

      ; destination file doesn't exist - coast is clear
      dest_clear:
      CopyFiles /SILENT $R9 "$R1$R3"
      ${LogMsg} "Installed File: $R1$R3\$R7"

      log_uninstall:
      ; If the file is installed into the installation directory remove the
      ; installation directory's path from the file path when writing to the
      ; uninstall.log so it will be a relative path. This allows the same
      ; helper.exe to be used with zip builds if we supply an uninstall.log.
      ${WordReplace} "$R1$R3\$R7" "$INSTDIR" "" "+" $R3
      ${LogUninstall} "File: $R3"

      end:
      Push 0
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro CopyFilesFromDirCall _PATH_TO_SOURCE _PATH_TO_DESTINATION \
                            _PREFIX_ERROR_CREATEDIR _SUFFIX_ERROR_CREATEDIR
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_PATH_TO_SOURCE}"
  Push "${_PATH_TO_DESTINATION}"
  Push "${_PREFIX_ERROR_CREATEDIR}"
  Push "${_SUFFIX_ERROR_CREATEDIR}"
  Call CopyFilesFromDir
  !verbose pop
!macroend

/**
 * Parses the uninstall.log on install to first remove a previous installation's
 * files and then their directories if empty prior to installing.
 *
 * When modifying this macro be aware that LineFind uses all registers except
 * $R0-$R3 so be cautious. Callers of this macro are not affected.
 */
!macro OnInstallUninstall

  !ifndef OnInstallUninstall
    !insertmacro GetParent
    !insertmacro LineFind
    !insertmacro TrimNewLines

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define OnInstallUninstall "!insertmacro OnInstallUninstallCall"

    Function OnInstallUninstall
      Push $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4
      Push $R3
      Push $R2
      Push $R1
      Push $R0
      Push $TmpVal

      IfFileExists "$INSTDIR\uninstall\uninstall.log" +1 end

      ${LogHeader} "Removing Previous Installation"

      ; Copy the uninstall log file to a temporary file
      GetTempFileName $TmpVal
      CopyFiles /SILENT /FILESONLY "$INSTDIR\uninstall\uninstall.log" "$TmpVal"

      ; Delete files
      ${LineFind} "$TmpVal" "/NUL" "1:-1" "RemoveFilesCallback"

      ; Remove empty directories
      ${LineFind} "$TmpVal" "/NUL" "1:-1" "RemoveDirsCallback"

      ; Delete the temporary uninstall log file
      Delete /REBOOTOK "$TmpVal"

      ; Delete the uninstall log file
      Delete "$INSTDIR\uninstall\uninstall.log"

      end:
      ClearErrors

      Pop $TmpVal
      Pop $R0
      Pop $R1
      Pop $R2
      Pop $R3
      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Pop $R9
    FunctionEnd

    Function RemoveFilesCallback
      ${TrimNewLines} "$R9" $R9
      StrCpy $R1 "$R9" 5       ; Copy the first five chars

      StrCmp "$R1" "File:" +1 end
      StrCpy $R9 "$R9" "" 6    ; Copy string starting after the 6th char
      StrCpy $R0 "$R9" 1       ; Copy the first char

      StrCmp "$R0" "\" +1 end  ; If this isn't a relative path goto end
      StrCmp "$R9" "\install.log" end +1 ; Skip the install.log
      StrCmp "$R9" "\MapiProxy_InUse.dll" end +1 ; Skip the MapiProxy_InUse.dll
      StrCmp "$R9" "\mozMapi32_InUse.dll" end +1 ; Skip the mozMapi32_InUse.dll

      StrCpy $R1 "$INSTDIR$R9" ; Copy the install dir path and suffix it with the string
      IfFileExists "$R1" +1 end

      ClearErrors
      Delete "$R1"
      ${Unless} ${Errors}
        ${LogMsg} "Deleted File: $R1"
        Goto end
      ${EndUnless}

      ClearErrors
      Rename "$R1" "$R1.moz-delete"
      ${Unless} ${Errors}
        Delete /REBOOTOK "$R1.moz-delete"
        ${LogMsg} "Delayed Delete File (Reboot Required): $R1.moz-delete"
        GoTo end
      ${EndUnless}

      ; Check if the file exists in the source. If it does the new file will
      ; replace the existing file when the system is rebooted. If it doesn't
      ; the file will be deleted when the system is rebooted.
      ${Unless} ${FileExists} "$EXEDIR\core$R9"
      ${AndUnless}  ${FileExists} "$EXEDIR\optional$R9"
        Delete /REBOOTOK "$R1"
        ${LogMsg} "Delayed Delete File (Reboot Required): $R1"
      ${EndUnless}

      end:
      ClearErrors

      Push 0
    FunctionEnd

    ; Using locate will leave file handles open to some of the directories
    ; which will prevent the deletion of these directories. This parses the
    ; uninstall.log and uses the file entries to find / remove empty
    ; directories.
    Function RemoveDirsCallback
      ${TrimNewLines} "$R9" $R9
      StrCpy $R0 "$R9" 5          ; Copy the first five chars
      StrCmp "$R0" "File:" +1 end

      StrCpy $R9 "$R9" "" 6       ; Copy string starting after the 6th char
      StrCpy $R0 "$R9" 1          ; Copy the first char

      StrCpy $R1 "$INSTDIR$R9"    ; Copy the install dir path and suffix it with the string
      StrCmp "$R0" "\" loop end   ; If this isn't a relative path goto end

      loop:
      ${GetParent} "$R1" $R1         ; Get the parent directory for the path
      StrCmp "$R1" "$INSTDIR" end +1 ; If the directory is the install dir goto end

      IfFileExists "$R1" +1 loop  ; Only try to remove the dir if it exists
      ClearErrors
      RmDir "$R1"     ; Remove the dir
      IfErrors end +1  ; If we fail there is no use trying to remove its parent dir
      ${LogMsg} "Deleted Directory: $R1"
      GoTo loop

      end:
      ClearErrors

      Push 0
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro OnInstallUninstallCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call OnInstallUninstall
  !verbose pop
!macroend

/**
 * Parses the precomplete file to remove an installation's files and
 * directories.
 *
 * @param   _CALLBACK
 *          The function address of a callback function for progress or "false"
 *          if there is no callback function.
 *
 * $R3 = false if all files were deleted or moved to the tobedeleted directory.
 *       true if file(s) could not be moved to the tobedeleted directory.
 * $R4 = Path to temporary precomplete file.
 * $R5 = File handle for the temporary precomplete file.
 * $R6 = String returned from FileRead.
 * $R7 = First seven characters of the string returned from FileRead.
 * $R8 = Temporary file path used to rename files that are in use.
 * $R9 = _CALLBACK
 */
!macro RemovePrecompleteEntries

  !ifndef ${_MOZFUNC_UN}RemovePrecompleteEntries
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetParent
    !insertmacro ${_MOZFUNC_UN_TMP}TrimNewLines
    !insertmacro ${_MOZFUNC_UN_TMP}WordReplace
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}RemovePrecompleteEntries "!insertmacro ${_MOZFUNC_UN}RemovePrecompleteEntriesCall"

    Function ${_MOZFUNC_UN}RemovePrecompleteEntries
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4
      Push $R3

      ${If} ${FileExists} "$INSTDIR\precomplete"
        StrCpy $R3 "false"

        RmDir /r "$INSTDIR\${TO_BE_DELETED}"
        CreateDirectory "$INSTDIR\${TO_BE_DELETED}"
        GetTempFileName $R4 "$INSTDIR\${TO_BE_DELETED}"
        Delete "$R4"
        Rename "$INSTDIR\precomplete" "$R4"

        ClearErrors
        ; Rename and then remove files
        FileOpen $R5 "$R4" r
        ${Do}
          FileRead $R5 $R6
          ${If} ${Errors}
            ${Break}
          ${EndIf}

          ${${_MOZFUNC_UN}TrimNewLines} "$R6" $R6
          ; Replace all occurrences of "/" with "\".
          ${${_MOZFUNC_UN}WordReplace} "$R6" "/" "\" "+" $R6

          ; Copy the first 7 chars
          StrCpy $R7 "$R6" 7
          ${If} "$R7" == "remove "
            ; Copy the string starting after the 8th char
            StrCpy $R6 "$R6" "" 8
            ; Copy all but the last char to remove the double quote.
            StrCpy $R6 "$R6" -1
            ${If} ${FileExists} "$INSTDIR\$R6"
              ${Unless} "$R9" == "false"
                Call $R9
              ${EndUnless}

              ClearErrors
              Delete "$INSTDIR\$R6"
              ${If} ${Errors}
                GetTempFileName $R8 "$INSTDIR\${TO_BE_DELETED}"
                Delete "$R8"
                ClearErrors
                Rename "$INSTDIR\$R6" "$R8"
                ${Unless} ${Errors}
                  Delete /REBOOTOK "$R8"

                  ClearErrors
                ${EndUnless}
!ifdef __UNINSTALL__
                ${If} ${Errors}
                  Delete /REBOOTOK "$INSTDIR\$R6"
                  StrCpy $R3 "true"
                  ClearErrors
                ${EndIf}
!endif
              ${EndIf}
            ${EndIf}
          ${ElseIf} "$R7" == "rmdir $\""
            ; Copy the string starting after the 7th char.
            StrCpy $R6 "$R6" "" 7
            ; Copy all but the last two chars to remove the slash and the double quote.
            StrCpy $R6 "$R6" -2
            ${If} ${FileExists} "$INSTDIR\$R6"
              ; Ignore directory removal errors
              RmDir "$INSTDIR\$R6"
              ClearErrors
            ${EndIf}
          ${EndIf}
        ${Loop}
        FileClose $R5

        ; Delete the temporary precomplete file
        Delete /REBOOTOK "$R4"

        RmDir /r /REBOOTOK "$INSTDIR\${TO_BE_DELETED}"

        ${If} ${RebootFlag}
        ${AndIf} "$R3" == "false"
          ; Clear the reboot flag if all files were deleted or moved to the
          ; tobedeleted directory.
          SetRebootFlag false
        ${EndIf}
      ${EndIf}

      ClearErrors

      Pop $R3
      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro RemovePrecompleteEntriesCall _CALLBACK
  !verbose push
  Push "${_CALLBACK}"
  !verbose ${_MOZFUNC_VERBOSE}
  Call RemovePrecompleteEntries
  !verbose pop
!macroend

!macro un.RemovePrecompleteEntriesCall _CALLBACK
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_CALLBACK}"
  Call un.RemovePrecompleteEntries
  !verbose pop
!macroend

!macro un.RemovePrecompleteEntries
  !ifndef un.RemovePrecompleteEntries
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro RemovePrecompleteEntries

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Parses the uninstall.log to unregister dll's, remove files, and remove
 * empty directories for this installation.
 *
 * When modifying this macro be aware that LineFind uses all registers except
 * $R0-$R3 so be cautious. Callers of this macro are not affected.
 */
!macro un.ParseUninstallLog

  !ifndef un.ParseUninstallLog
    !insertmacro un.GetParent
    !insertmacro un.LineFind
    !insertmacro un.TrimNewLines

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define un.ParseUninstallLog "!insertmacro un.ParseUninstallLogCall"

    Function un.ParseUninstallLog
      Push $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4
      Push $R3
      Push $R2
      Push $R1
      Push $R0
      Push $TmpVal

      IfFileExists "$INSTDIR\uninstall\uninstall.log" +1 end

      ; Copy the uninstall log file to a temporary file
      GetTempFileName $TmpVal
      CopyFiles /SILENT /FILESONLY "$INSTDIR\uninstall\uninstall.log" "$TmpVal"

      ; Unregister DLL's
      ${un.LineFind} "$TmpVal" "/NUL" "1:-1" "un.UnRegDLLsCallback"

      ; Delete files
      ${un.LineFind} "$TmpVal" "/NUL" "1:-1" "un.RemoveFilesCallback"

      ; Remove empty directories
      ${un.LineFind} "$TmpVal" "/NUL" "1:-1" "un.RemoveDirsCallback"

      ; Delete the temporary uninstall log file
      Delete /REBOOTOK "$TmpVal"

      end:

      Pop $TmpVal
      Pop $R0
      Pop $R1
      Pop $R2
      Pop $R3
      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Pop $R9
    FunctionEnd

    Function un.RemoveFilesCallback
      ${un.TrimNewLines} "$R9" $R9
      StrCpy $R1 "$R9" 5

      StrCmp "$R1" "File:" +1 end
      StrCpy $R9 "$R9" "" 6
      StrCpy $R0 "$R9" 1

      StrCpy $R1 "$INSTDIR$R9"
      StrCmp "$R0" "\" +2 +1
      StrCpy $R1 "$R9"

      IfFileExists "$R1" +1 end
      Delete "$R1"
      IfErrors +1 end
      ClearErrors
      Rename "$R1" "$R1.moz-delete"
      IfErrors +1 +3
      Delete /REBOOTOK "$R1"
      GoTo end

      Delete /REBOOTOK "$R1.moz-delete"

      end:
      ClearErrors

      Push 0
    FunctionEnd

    Function un.UnRegDLLsCallback
      ${un.TrimNewLines} "$R9" $R9
      StrCpy $R1 "$R9" 7

      StrCmp $R1 "DLLReg:" +1 end
      StrCpy $R9 "$R9" "" 8
      StrCpy $R0 "$R9" 1

      StrCpy $R1 "$INSTDIR$R9"
      StrCmp $R0 "\" +2 +1
      StrCpy $R1 "$R9"

      ${UnregisterDLL} $R1

      end:
      ClearErrors

      Push 0
    FunctionEnd

    ; Using locate will leave file handles open to some of the directories
    ; which will prevent the deletion of these directories. This parses the
    ; uninstall.log and uses the file entries to find / remove empty
    ; directories.
    Function un.RemoveDirsCallback
      ${un.TrimNewLines} "$R9" $R9
      StrCpy $R0 "$R9" 5          ; Copy the first five chars
      StrCmp "$R0" "File:" +1 end

      StrCpy $R9 "$R9" "" 6       ; Copy string starting after the 6th char
      StrCpy $R0 "$R9" 1          ; Copy the first char

      StrCpy $R1 "$INSTDIR$R9"    ; Copy the install dir path and suffix it with the string
      StrCmp "$R0" "\" loop       ; If this is a relative path goto the loop
      StrCpy $R1 "$R9"            ; Already a full path so copy the string

      loop:
      ${un.GetParent} "$R1" $R1   ; Get the parent directory for the path
      StrCmp "$R1" "$INSTDIR" end ; If the directory is the install dir goto end

      ; We only try to remove empty directories but the Desktop, StartMenu, and
      ; QuickLaunch directories can be empty so guard against removing them.
      SetShellVarContext all          ; Set context to all users
      StrCmp "$R1" "$DESKTOP" end     ; All users desktop
      StrCmp "$R1" "$STARTMENU" end   ; All users start menu

      SetShellVarContext current      ; Set context to all users
      StrCmp "$R1" "$DESKTOP" end     ; Current user desktop
      StrCmp "$R1" "$STARTMENU" end   ; Current user start menu
      StrCmp "$R1" "$QUICKLAUNCH" end ; Current user quick launch

      IfFileExists "$R1" +1 +3  ; Only try to remove the dir if it exists
      ClearErrors
      RmDir "$R1"    ; Remove the dir
      IfErrors end   ; If we fail there is no use trying to remove its parent dir

      StrCmp "$R0" "\" loop end  ; Only loop when the path is relative to the install dir

      end:
      ClearErrors

      Push 0
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro un.ParseUninstallLogCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.ParseUninstallLog
  !verbose pop
!macroend

/**
 * Finds a valid Start Menu shortcut in the uninstall log and returns the
 * relative path from the Start Menu's Programs directory to the shortcut's
 * directory.
 *
 * When modifying this macro be aware that LineFind uses all registers except
 * $R0-$R3 so be cautious. Callers of this macro are not affected.
 *
 * @return  _REL_PATH_TO_DIR
 *          The relative path to the application's Start Menu directory from the
 *          Start Menu's Programs directory.
 */
!macro FindSMProgramsDir

  !ifndef FindSMProgramsDir
    !insertmacro GetParent
    !insertmacro LineFind
    !insertmacro TrimNewLines

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define FindSMProgramsDir "!insertmacro FindSMProgramsDirCall"

    Function FindSMProgramsDir
      Exch $R3
      Push $R2
      Push $R1
      Push $R0

      StrCpy $R3 ""
      ${If} ${FileExists} "$INSTDIR\uninstall\uninstall.log"
        ${LineFind} "$INSTDIR\uninstall\uninstall.log" "/NUL" "1:-1" "FindSMProgramsDirRelPath"
      ${EndIf}
      ClearErrors

      Pop $R0
      Pop $R1
      Pop $R2
      Exch $R3
    FunctionEnd

    ; This callback MUST use labels vs. relative line numbers.
    Function FindSMProgramsDirRelPath
      Push 0
      ${TrimNewLines} "$R9" $R9
      StrCpy $R4 "$R9" 5

      StrCmp "$R4" "File:" +1 end_FindSMProgramsDirRelPath
      StrCpy $R9 "$R9" "" 6
      StrCpy $R4 "$R9" 1

      StrCmp "$R4" "\" end_FindSMProgramsDirRelPath +1

      SetShellVarContext all
      ${GetLongPath} "$SMPROGRAMS" $R4
      StrLen $R2 "$R4"
      StrCpy $R1 "$R9" $R2
      StrCmp "$R1" "$R4" +1 end_FindSMProgramsDirRelPath
      IfFileExists "$R9" +1 end_FindSMProgramsDirRelPath
      ShellLink::GetShortCutTarget "$R9"
      Pop $R0
      StrCmp "$INSTDIR\${FileMainEXE}" "$R0" +1 end_FindSMProgramsDirRelPath
      ${GetParent} "$R9" $R3
      IntOp $R2 $R2 + 1
      StrCpy $R3 "$R3" "" $R2

      Pop $R4             ; Remove the previously pushed 0 from the stack and
      push "StopLineFind" ; push StopLineFind to stop finding more lines.

      end_FindSMProgramsDirRelPath:
      ClearErrors

    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro FindSMProgramsDirCall _REL_PATH_TO_DIR
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call FindSMProgramsDir
  Pop ${_REL_PATH_TO_DIR}
  !verbose pop
!macroend


################################################################################
# Macros for custom branding

/**
 * Sets BrandFullName and / or BrandShortName to values provided in the specified
 * ini file and defaults to BrandShortName and BrandFullName as defined in
 * branding.nsi when the associated ini file entry is not specified.
 *
 * ini file format:
 * [Branding]
 * BrandFullName=Custom Full Name
 * BrandShortName=Custom Short Name
 *
 * @param   _PATH_TO_INI
 *          Path to the ini file.
 *
 * $R6 = return value from ReadINIStr
 * $R7 = stores BrandShortName
 * $R8 = stores BrandFullName
 * $R9 = _PATH_TO_INI
 */
!macro SetBrandNameVars

  !ifndef ${_MOZFUNC_UN}SetBrandNameVars
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}WordReplace
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    ; Prevent declaring vars twice when the SetBrandNameVars macro is
    ; inserted into both the installer and uninstaller.
    !ifndef SetBrandNameVars
      Var BrandFullName
      Var BrandFullNameDA
      Var BrandShortName
      Var BrandProductName
    !endif

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}SetBrandNameVars "!insertmacro ${_MOZFUNC_UN}SetBrandNameVarsCall"

    Function ${_MOZFUNC_UN}SetBrandNameVars
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5

      StrCpy $R8 "${BrandFullName}"
      StrCpy $R7 "${BrandShortName}"
      StrCpy $R6 "${BrandProductName}"

      IfFileExists "$R9" +1 finish

      ClearErrors
      ReadINIStr $R5 $R9 "Branding" "BrandFullName"
      IfErrors +2 +1
      StrCpy $R8 "$R5"

      ClearErrors
      ReadINIStr $R5 $R9 "Branding" "BrandShortName"
      IfErrors +2 +1
      StrCpy $R7 "$R5"

      ClearErrors
      ReadINIStr $R5 $R9 "Branding" "BrandProductName"
      IfErrors +2 +1
      StrCpy $R6 "$R5"

      finish:
      StrCpy $BrandFullName "$R8"
      ${${_MOZFUNC_UN}WordReplace} "$R8" "&" "&&" "+" $R8
      StrCpy $BrandFullNameDA "$R8"
      StrCpy $BrandShortName "$R7"
      StrCpy $BrandProductName "$R6"

      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro SetBrandNameVarsCall _PATH_TO_INI
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_PATH_TO_INI}"
  Call SetBrandNameVars
  !verbose pop
!macroend

!macro un.SetBrandNameVarsCall _PATH_TO_INI
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_PATH_TO_INI}"
  Call un.SetBrandNameVars
  !verbose pop
!macroend

!macro un.SetBrandNameVars
  !ifndef un.SetBrandNameVars
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro SetBrandNameVars

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Replaces the wizard's header image with the one specified.
 *
 * @param   _PATH_TO_IMAGE
 *          Fully qualified path to the bitmap to use for the header image.
 *
 * $R8 = hwnd for the control returned from GetDlgItem.
 * $R9 = _PATH_TO_IMAGE
 */
!macro ChangeMUIHeaderImage

  !ifndef ${_MOZFUNC_UN}ChangeMUIHeaderImage
    Var hHeaderBitmap

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}ChangeMUIHeaderImage "!insertmacro ${_MOZFUNC_UN}ChangeMUIHeaderImageCall"

    Function ${_MOZFUNC_UN}ChangeMUIHeaderImage
      Exch $R9
      Push $R8

      GetDlgItem $R8 $HWNDPARENT 1046
      ${SetStretchedImageOLE} $R8 "$R9" $hHeaderBitmap
      ; There is no way to specify a show function for a custom page so hide
      ; and then show the control to force the bitmap to redraw.
      ShowWindow $R8 ${SW_HIDE}
      ShowWindow $R8 ${SW_SHOW}

      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro ChangeMUIHeaderImageCall _PATH_TO_IMAGE
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_PATH_TO_IMAGE}"
  Call ChangeMUIHeaderImage
  !verbose pop
!macroend

!macro un.ChangeMUIHeaderImageCall _PATH_TO_IMAGE
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_PATH_TO_IMAGE}"
  Call un.ChangeMUIHeaderImage
  !verbose pop
!macroend

!macro un.ChangeMUIHeaderImage
  !ifndef un.ChangeMUIHeaderImage
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro ChangeMUIHeaderImage

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Replaces the sidebar image on the wizard's welcome and finish pages.
 *
 * @param   _PATH_TO_IMAGE
 *          Fully qualified path to the bitmap to use for the header image.
 *
 * $R8 = hwnd for the bitmap control
 * $R9 = _PATH_TO_IMAGE
 */
!macro ChangeMUISidebarImage

  !ifndef ${_MOZFUNC_UN}ChangeMUISidebarImage
    Var hSidebarBitmap

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}ChangeMUISidebarImage "!insertmacro ${_MOZFUNC_UN}ChangeMUISidebarImageCall"

    Function ${_MOZFUNC_UN}ChangeMUISidebarImage
      Exch $R9
      Push $R8

      ; Make sure we're not about to leak an existing handle.
      ${If} $hSidebarBitmap <> 0
        System::Call "gdi32::DeleteObject(p $hSidebarBitmap)"
        StrCpy $hSidebarBitmap 0
      ${EndIf}
      ; The controls on the welcome and finish pages aren't in the dialog
      ; template, they're always created manually from the INI file, so we need
      ; to query it to find the right HWND.
      ReadINIStr $R8 "$PLUGINSDIR\ioSpecial.ini" "Field 1" "HWND"
      ${SetStretchedImageOLE} $R8 "$R9" $hSidebarBitmap

      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro ChangeMUISidebarImageCall _PATH_TO_IMAGE
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_PATH_TO_IMAGE}"
  Call ChangeMUISidebarImage
  !verbose pop
!macroend

!macro un.ChangeMUISidebarImageCall _PATH_TO_IMAGE
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_PATH_TO_IMAGE}"
  Call un.ChangeMUISidebarImage
  !verbose pop
!macroend

!macro un.ChangeMUISidebarImage
  !ifndef un.ChangeMUISidebarImage
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro ChangeMUISidebarImage

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

################################################################################
# User interface callback helper defines and macros

/* Install type defines */
!ifndef INSTALLTYPE_BASIC
  !define INSTALLTYPE_BASIC     1
!endif

!ifndef INSTALLTYPE_CUSTOM
  !define INSTALLTYPE_CUSTOM    2
!endif

/**
 * Checks whether to display the current page (e.g. if not performing a custom
 * install don't display the custom pages).
 */
!macro CheckCustomCommon

  !ifndef CheckCustomCommon
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define CheckCustomCommon "!insertmacro CheckCustomCommonCall"

    Function CheckCustomCommon

      ; Abort if not a custom install
      IntCmp $InstallType ${INSTALLTYPE_CUSTOM} +2 +1 +1
      Abort

    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro CheckCustomCommonCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call CheckCustomCommon
  !verbose pop
!macroend

/**
 * Unloads dll's and releases references when the installer and uninstaller
 * exit.
 */
!macro OnEndCommon

  !ifndef ${_MOZFUNC_UN}OnEndCommon
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}UnloadUAC
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}OnEndCommon "!insertmacro ${_MOZFUNC_UN}OnEndCommonCall"

    Function ${_MOZFUNC_UN}OnEndCommon

      ${${_MOZFUNC_UN}UnloadUAC}
      StrCmp $hHeaderBitmap "" +3 +1
      System::Call "gdi32::DeleteObject(i s)" $hHeaderBitmap
      StrCpy $hHeaderBitmap ""
      ; If ChangeMUISidebarImage was called, then we also need to clean up the
      ; GDI bitmap handle that it would have created.
      !ifdef ${_MOZFUNC_UN}ChangeMUISidebarImage
        StrCmp $hSidebarBitmap "" +3 +1
        System::Call "gdi32::DeleteObject(i s)" $hSidebarBitmap
        StrCpy $hSidebarBitmap ""
      !endif

      System::Free 0

    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro OnEndCommonCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call OnEndCommon
  !verbose pop
!macroend

!macro un.OnEndCommonCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.OnEndCommon
  !verbose pop
!macroend

!macro un.OnEndCommon
  !ifndef un.OnEndCommon
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro OnEndCommon

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Reads a flag option from the command line and sets a variable with its state,
 * if the option is present on the command line.
 *
 * @param   FULL_COMMAND_LINE
 *          The entire installer command line, such as from ${GetParameters}
 * @param   OPTION
 *          Name of the option to look for
 * @param   OUTPUT
 *          Variable/register to write the output to. Will be set to "0" if the
 *          option was present with the value "false", will be set to "1" if the
 *          option was present with another value, and will be untouched if the
 *          option was not on the command line at all.
 */
!macro InstallGetOption FULL_COMMAND_LINE OPTION OUTPUT
  Push $0
  ClearErrors
  ${GetOptions} ${FULL_COMMAND_LINE} "/${OPTION}" $0
  ${IfNot} ${Errors}
    ; Any valid command-line option triggers a silent installation.
    SetSilent silent

    ${If} $0 == "=false"
      StrCpy ${OUTPUT} "0"
    ${Else}
      StrCpy ${OUTPUT} "1"
    ${EndIf}
  ${EndIf}
  Pop $0
!macroend
!define InstallGetOption "!insertmacro InstallGetOption"

/**
 * Called from the installer's .onInit function not to be confused with the
 * uninstaller's .onInit or the uninstaller's un.onInit functions.
 *
 * @param   _WARN_UNSUPPORTED_MSG
 *          Message displayed when the Windows version is not supported.
 *
 * $R4 = keeps track of whether a custom install path was specified on either
 *       the command line or in an INI file
 * $R5 = return value from the GetSize macro
 * $R6 = general string values, return value from GetTempFileName, return
 *       value from the GetSize macro
 * $R7 = full path to the configuration ini file
 * $R8 = used for OS Version and Service Pack detection and the return value
 *       from the GetParameters macro
 * $R9 = _WARN_UNSUPPORTED_MSG
 */
!macro InstallOnInitCommon

  !ifndef InstallOnInitCommon
    !insertmacro ElevateUAC
    !insertmacro GetOptions
    !insertmacro GetParameters
    !insertmacro GetSize

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define InstallOnInitCommon "!insertmacro InstallOnInitCommonCall"

    Function InstallOnInitCommon
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4

      ; 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 .R8"
      ${If} "$R8" == "0"
        MessageBox MB_OK|MB_ICONSTOP "$R9"
        ; Nothing initialized so no need to call OnEndCommon
        Quit
      ${EndIf}

      ; Windows 8.1/Server 2012 R2 and lower are not supported.
      ${Unless} ${AtLeastWin10}
        MessageBox MB_OK|MB_ICONSTOP "$R9"
        ; Nothing initialized so no need to call OnEndCommon
        Quit
      ${EndUnless}

      !ifdef HAVE_64BIT_BUILD
        SetRegView 64
      !endif

      StrCpy $R4 0 ; will be set to 1 if a custom install path is set

      ${GetParameters} $R8
      ${If} $R8 != ""
        ; Default install type
        StrCpy $InstallType ${INSTALLTYPE_BASIC}

        ${Unless} ${Silent}
          ; NSIS should check for /S for us, but we've had issues with it such
          ; as bug 506867 in the past, so we'll check for it ourselves also.
          ClearErrors
          ${GetOptions} "$R8" "/S" $R7
          ${Unless} ${Errors}
            SetSilent silent
          ${Else}
            ; NSIS dropped support for the deprecated -ms argument, but we don't
            ; want to break backcompat, so we'll check for it here too.
            ClearErrors
            ${GetOptions} "$R8" "-ms" $R7
            ${Unless} ${Errors}
              SetSilent silent
            ${EndUnless}
          ${EndUnless}
        ${EndUnless}

        ; Support for specifying an installation configuration file.
        ClearErrors
        ${GetOptions} "$R8" "/INI=" $R7
        ${Unless} ${Errors}
          ; The configuration file must also exist
          ${If} ${FileExists} "$R7"
            ; Any valid command-line option triggers a silent installation.
            SetSilent silent

            ReadINIStr $R8 $R7 "Install" "InstallDirectoryName"
            ${If} $R8 != ""
              StrCpy $R4 1
              !ifdef HAVE_64BIT_BUILD
                StrCpy $INSTDIR "$PROGRAMFILES64\$R8"
              !else
                StrCpy $INSTDIR "$PROGRAMFILES32\$R8"
              !endif
            ${Else}
              ReadINIStr $R8 $R7 "Install" "InstallDirectoryPath"
              ${If} $R8 != ""
                StrCpy $R4 1
                StrCpy $INSTDIR "$R8"
              ${EndIf}
            ${EndIf}

            ReadINIStr $R8 $R7 "Install" "DesktopShortcut"
            ${If} $R8 == "false"
              StrCpy $AddDesktopSC "0"
            ${Else}
              StrCpy $AddDesktopSC "1"
            ${EndIf}

            ReadINIStr $R8 $R7 "Install" "StartMenuShortcuts"
            ${If} $R8 == "false"
              StrCpy $AddStartMenuSC "0"
            ${Else}
              StrCpy $AddStartMenuSC "1"
            ${EndIf}

            ; We still accept the plural version for backwards compatibility,
            ; but the singular version takes priority.
            ClearErrors
            ReadINIStr $R8 $R7 "Install" "StartMenuShortcut"
            ${If} $R8 == "false"
              StrCpy $AddStartMenuSC "0"
            ${ElseIfNot} ${Errors}
              StrCpy $AddStartMenuSC "1"
            ${EndIf}

            !ifdef MOZ_PRIVATE_BROWSING
              ReadINIStr $R8 $R7 "Install" "PrivateBrowsingShortcut"
              ${If} $R8 == "false"
                StrCpy $AddPrivateBrowsingSC "0"
              ${ElseIfNot} ${Errors}
                StrCpy $AddPrivateBrowsingSC "1"
              ${EndIf}
            !endif

            ReadINIStr $R8 $R7 "Install" "TaskbarShortcut"
            ${If} $R8 == "false"
              StrCpy $AddTaskbarSC "0"
            ${Else}
              StrCpy $AddTaskbarSC "1"
            ${EndIf}

            ReadINIStr $R8 $R7 "Install" "MaintenanceService"
            ${If} $R8 == "false"
              StrCpy $InstallMaintenanceService "0"
            ${Else}
              StrCpy $InstallMaintenanceService "1"
            ${EndIf}

            ReadINIStr $R8 $R7 "Install" "RegisterDefaultAgent"
            ${If} $R8 == "false"
              StrCpy $RegisterDefaultAgent "0"
            ${Else}
              StrCpy $RegisterDefaultAgent "1"
            ${EndIf}

            !ifdef MOZ_OPTIONAL_EXTENSIONS
              ReadINIStr $R8 $R7 "Install" "OptionalExtensions"
              ${If} $R8 == "false"
                StrCpy $InstallOptionalExtensions "0"
              ${Else}
                StrCpy $InstallOptionalExtensions "1"
              ${EndIf}
            !endif

            !ifndef NO_STARTMENU_DIR
              ReadINIStr $R8 $R7 "Install" "StartMenuDirectoryName"
              ${If} $R8 != ""
                StrCpy $StartMenuDir "$R8"
              ${EndIf}
            !endif
          ${EndIf}
        ${EndUnless}

        ; Check for individual command line parameters after evaluating the INI
        ; file, because these should override the INI entires.
        ${GetParameters} $R8
        ${GetOptions} $R8 "/InstallDirectoryName=" $R7
        ${If} $R7 != ""
          StrCpy $R4 1
          !ifdef HAVE_64BIT_BUILD
            StrCpy $INSTDIR "$PROGRAMFILES64\$R7"
          !else
            StrCpy $INSTDIR "$PROGRAMFILES32\$R7"
          !endif
        ${Else}
          ${GetOptions} $R8 "/InstallDirectoryPath=" $R7
          ${If} $R7 != ""
            StrCpy $R4 1
            StrCpy $INSTDIR "$R7"
          ${EndIf}
        ${EndIf}

        ${InstallGetOption} $R8 "DesktopShortcut" $AddDesktopSC
        ${InstallGetOption} $R8 "StartMenuShortcuts" $AddStartMenuSC
        ; We still accept the plural version for backwards compatibility,
        ; but the singular version takes priority.
        ${InstallGetOption} $R8 "StartMenuShortcut" $AddStartMenuSC
        !ifdef MOZ_PRIVATE_BROWSING
          ${InstallGetOption} $R8 "PrivateBrowsingShortcut" $AddPrivateBrowsingSC
        !endif
        ${InstallGetOption} $R8 "TaskbarShortcut" $AddTaskbarSC
        ${InstallGetOption} $R8 "MaintenanceService" $InstallMaintenanceService
        ${InstallGetOption} $R8 "RegisterDefaultAgent" $RegisterDefaultAgent
        !ifdef MOZ_OPTIONAL_EXTENSIONS
          ${InstallGetOption} $R8 "OptionalExtensions" $InstallOptionalExtensions
        !endif

        ; Installing the service always requires elevated privileges.
        ${If} $InstallMaintenanceService == "1"
          ${ElevateUAC}
        ${EndIf}
      ${EndIf}

      ${If} $R4 == 1
        ; Any valid command-line option triggers a silent installation.
        SetSilent silent

        ; Quit if we are unable to create the installation directory or we are
        ; unable to write to a file in the installation directory.
        ClearErrors
        ${If} ${FileExists} "$INSTDIR"
          GetTempFileName $R6 "$INSTDIR"
          FileOpen $R5 "$R6" w
          FileWrite $R5 "Write Access Test"
          FileClose $R5
          Delete $R6
          ${If} ${Errors}
            ; Attempt to elevate and then try again.
            ${ElevateUAC}
            GetTempFileName $R6 "$INSTDIR"
            FileOpen $R5 "$R6" w
            FileWrite $R5 "Write Access Test"
            FileClose $R5
            Delete $R6
            ${If} ${Errors}
              ; Nothing initialized so no need to call OnEndCommon
              Quit
            ${EndIf}
          ${EndIf}
        ${Else}
          CreateDirectory "$INSTDIR"
          ${If} ${Errors}
            ; Attempt to elevate and then try again.
            ${ElevateUAC}
            CreateDirectory "$INSTDIR"
            ${If} ${Errors}
              ; Nothing initialized so no need to call OnEndCommon
              Quit
            ${EndIf}
          ${EndIf}
        ${EndIf}
      ${Else}
        ; If we weren't given a custom path parameter, then try to elevate now.
        ; We'll check the user's permission level later on to determine the
        ; default install path (which will be the real install path for /S).
        ; If an INI file is used, we try to elevate down that path when needed.
        ${ElevateUAC}
      ${EndIf}

      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro InstallOnInitCommonCall _WARN_UNSUPPORTED_MSG
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_WARN_UNSUPPORTED_MSG}"
  Call InstallOnInitCommon
  !verbose pop
!macroend

/**
 * Called from the uninstaller's .onInit function not to be confused with the
 * installer's .onInit or the uninstaller's un.onInit functions.
 */
!macro UninstallOnInitCommon

  !ifndef UninstallOnInitCommon
    !insertmacro ElevateUAC
    !insertmacro GetLongPath
    !insertmacro GetOptions
    !insertmacro GetParameters
    !insertmacro GetParent
    !insertmacro UnloadUAC
    !insertmacro UpdateShortcutAppModelIDs
    !insertmacro UpdateUninstallLog

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define UninstallOnInitCommon "!insertmacro UninstallOnInitCommonCall"

    Function UninstallOnInitCommon
      ; Prevents breaking apps that don't use SetBrandNameVars
      !ifdef SetBrandNameVars
        ${SetBrandNameVars} "$EXEDIR\distribution\setup.ini"
      !endif

      ; Prevent launching the application when a reboot is required and this
      ; executable is the main application executable
      IfFileExists "$EXEDIR\${FileMainEXE}.moz-upgrade" +1 +4
      MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2
      Reboot
      Quit ; Nothing initialized so no need to call OnEndCommon

      ${GetParent} "$EXEDIR" $INSTDIR
      ${GetLongPath} "$INSTDIR" $INSTDIR
      IfFileExists "$INSTDIR\${FileMainEXE}" +2 +1
      Quit ; Nothing initialized so no need to call OnEndCommon

!ifmacrodef InitHashAppModelId
      ; setup the application model id registration value
      !ifdef AppName
      ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
      !endif
!endif

      ; Prevents breaking apps that don't use SetBrandNameVars
      !ifdef SetBrandNameVars
        ${SetBrandNameVars} "$INSTDIR\distribution\setup.ini"
      !endif

      ; Application update uses a directory named tobedeleted in the $INSTDIR to
      ; delete files on OS reboot when they are in use. Try to delete this
      ; directory if it exists.
      ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
        RmDir /r "$INSTDIR\${TO_BE_DELETED}"
      ${EndIf}

      ; Prevent all operations (e.g. set as default, postupdate, etc.) when a
      ; reboot is required and the executable launched is helper.exe
      IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +4
      MessageBox MB_YESNO|MB_ICONEXCLAMATION "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2
      Reboot
      Quit ; Nothing initialized so no need to call OnEndCommon

      !ifdef HAVE_64BIT_BUILD
        SetRegView 64
      !endif

      ${GetParameters} $R0

      ${Unless} ${Silent}
        ; Manually check for /S in the command line due to Bug 506867
        ClearErrors
        ${GetOptions} "$R0" "/S" $R2
        ${Unless} ${Errors}
          SetSilent silent
        ${Else}
          ; Support for the deprecated -ms command line argument.
          ClearErrors
          ${GetOptions} "$R0" "-ms" $R2
          ${Unless} ${Errors}
            SetSilent silent
          ${EndUnless}
        ${EndUnless}
      ${EndUnless}

      StrCmp "$R0" "" continue +1

      ; Require elevation if the user can elevate
      hideshortcuts:
      ClearErrors
      ${GetOptions} "$R0" "/HideShortcuts" $R2
      IfErrors showshortcuts +1
!ifndef NONADMIN_ELEVATE
      ${ElevateUAC}
!endif
      ${HideShortcuts}
      GoTo finish

      ; Require elevation if the user can elevate
      showshortcuts:
      ClearErrors
      ${GetOptions} "$R0" "/ShowShortcuts" $R2
      IfErrors defaultappuser +1
!ifndef NONADMIN_ELEVATE
      ${ElevateUAC}
!endif
      ${ShowShortcuts}
      GoTo finish

      ; Require elevation if the the StartMenuInternet registry keys require
      ; updating and the user can elevate
      defaultappuser:
      ClearErrors
      ${GetOptions} "$R0" "/SetAsDefaultAppUser" $R2
      IfErrors defaultappglobal +1
      ${SetAsDefaultAppUser}
      GoTo finish

      ; Require elevation if the user can elevate
      defaultappglobal:
      ClearErrors
      ${GetOptions} "$R0" "/SetAsDefaultAppGlobal" $R2
      IfErrors postupdate +1
      ${ElevateUAC}
      ${SetAsDefaultAppGlobal}
      GoTo finish

      ; Do not attempt to elevate. The application launching this executable is
      ; responsible for elevation if it is required.
      postupdate:
      ${WordReplace} "$R0" "$\"" "" "+" $R0
      ClearErrors
      ${GetOptions} "$R0" "/PostUpdate" $R2
      IfErrors continue +1
      ; If the uninstall.log does not exist don't perform post update
      ; operations. This prevents updating the registry for zip builds.
      IfFileExists "$EXEDIR\uninstall.log" +2 +1
      Quit ; Nothing initialized so no need to call OnEndCommon
      ${PostUpdate}
      ClearErrors
      ${GetOptions} "$R0" "/UninstallLog=" $R2
      IfErrors updateuninstalllog +1
      StrCmp "$R2" "" finish +1
      GetFullPathName $R3 "$R2"
      IfFileExists "$R3" +1 finish
      Delete "$INSTDIR\uninstall\*wizard*"
      Delete "$INSTDIR\uninstall\uninstall.log"
      CopyFiles /SILENT /FILESONLY "$R3" "$INSTDIR\uninstall\"
      ${GetParent} "$R3" $R4
      Delete "$R3"
      RmDir "$R4"
      GoTo finish

      ; Do not attempt to elevate. The application launching this executable is
      ; responsible for elevation if it is required.
      updateuninstalllog:
      ${UpdateUninstallLog}

      finish:
      ${UnloadUAC}
      ${RefreshShellIcons}
      Quit ; Nothing initialized so no need to call OnEndCommon

      continue:

      ; If the uninstall.log does not exist don't perform uninstall
      ; operations. This prevents running the uninstaller for zip builds.
      IfFileExists "$INSTDIR\uninstall\uninstall.log" +2 +1
      Quit ; Nothing initialized so no need to call OnEndCommon

      ; When silent, try to avoid elevation if we have a chance to succeed.  We
      ; can succeed when we can write to (hence delete from) the install
      ; directory and when we can clean up all registry entries.  Now, the
      ; installer when elevated writes privileged registry entries for the use
      ; of the Maintenance Service, even when the service is not and will not be
      ; installed.  (In fact, even when a service installed in the future will
      ; never update the installation, for example due to not being in a
      ; privileged location.)  In practice this means we can only truly silently
      ; remove an unelevated install: an elevated installer writing to an
      ; unprivileged install directory will still write privileged registry
      ; entries, requiring an elevated uninstaller to completely clean up.
      ;
      ; This avoids a wrinkle, whereby an uninstaller which runs unelevated will
      ; never itself launch the Maintenance Service uninstaller, because it will
      ; fail to remove its own service registration (removing the relevant
      ; registry key would require elevation).  Therefore the check for the
      ; service being unused will fail, which will prevent running the service
      ; uninstaller.  That's both subtle and possibly leaves the service
      ; registration hanging around, which might be a security risk.
      ;
      ; That is why we look for a privileged service registration for this
      ; installation when deciding to elevate, and elevate unconditionally if we
      ; find one, regardless of the result of the write check that would avoid
      ; elevation.

      ; The reason for requiring elevation, or "" for not required.
      StrCpy $R4 ""

      ${IfNot} ${Silent}
        ; In normal operation, require elevation if the user can elevate so that
        ; we are most likely to succeed.
        StrCpy $R4 "not silent"
      ${EndIf}

      GetTempFileName $R6 "$INSTDIR"
      FileOpen $R5 "$R6" w
      FileWrite $R5 "Write Access Test"
      FileClose $R5
      Delete $R6
      ${If} ${Errors}
        StrCpy $R4 "write"
      ${EndIf}

      !ifdef MOZ_MAINTENANCE_SERVICE
        ; We don't necessarily have $MaintCertKey, so use temporary registers.
        ServicesHelper::PathToUniqueRegistryPath "$INSTDIR"
        Pop $R5

        ${If} $R5 != ""
          ; Always use the 64bit registry for certs on 64bit systems.
          ${If} ${RunningX64}
          ${OrIf} ${IsNativeARM64}
            SetRegView 64
          ${EndIf}

          EnumRegKey $R6 HKLM $R5 0
          ClearErrors

          ${If} ${RunningX64}
          ${OrIf} ${IsNativeARM64}
            SetRegView lastused
          ${EndIf}

          ${IfNot} "$R6" == ""
            StrCpy $R4 "mms"
          ${EndIf}
        ${EndIf}
      !endif

      ${If} "$R4" != ""
        ; In the future, we might not try to elevate to remain truly silent.  Or
        ; we might add a command line arguments to specify behaviour.  One
        ; reason to not do that immediately is that we have no great way to
        ; signal that we exited without taking action.
        ${ElevateUAC}
      ${EndIf}

      ; Now we've elevated, try the write access test again.
      ClearErrors
      GetTempFileName $R6 "$INSTDIR"
      FileOpen $R5 "$R6" w
      FileWrite $R5 "Write Access Test"
      FileClose $R5
      Delete $R6
      ${If} ${Errors}
        ; Nothing initialized so no need to call OnEndCommon
        Quit
      ${EndIf}

      !ifdef MOZ_MAINTENANCE_SERVICE
        ; And verify that if we need to, we're going to clean up the registry
        ; correctly.
        ${If} "$R4" == "mms"
          WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" "Write Test"
          ${If} ${Errors}
            ; Nothing initialized so no need to call OnEndCommon
            Quit
          ${Endif}
          DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
        ${EndIf}
      !endif

      ; If we made it this far then this installer is being used as an uninstaller.
      WriteUninstaller "$EXEDIR\uninstaller.exe"

      ${If} ${Silent}
        StrCpy $R1 "$\"$EXEDIR\uninstaller.exe$\" /S"
      ${Else}
        StrCpy $R1 "$\"$EXEDIR\uninstaller.exe$\""
      ${EndIf}

      ; When the uninstaller is launched it copies itself to the temp directory
      ; so it won't be in use so it can delete itself.
      ExecWait $R1
      ${DeleteFile} "$EXEDIR\uninstaller.exe"
      ${UnloadUAC}
      SetErrorLevel 0
      Quit ; Nothing initialized so no need to call OnEndCommon

    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro UninstallOnInitCommonCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call UninstallOnInitCommon
  !verbose pop
!macroend

/**
 * Called from the uninstaller's un.onInit function not to be confused with the
 * installer's .onInit or the uninstaller's .onInit functions.
 */
!macro un.UninstallUnOnInitCommon

  !ifndef un.UninstallUnOnInitCommon
    !insertmacro un.GetLongPath
    !insertmacro un.GetParent
    !insertmacro un.SetBrandNameVars

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define un.UninstallUnOnInitCommon "!insertmacro un.UninstallUnOnInitCommonCall"

    Function un.UninstallUnOnInitCommon
      ${un.GetParent} "$INSTDIR" $INSTDIR
      ${un.GetLongPath} "$INSTDIR" $INSTDIR
      ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}"
        Abort
      ${EndUnless}

      !ifdef HAVE_64BIT_BUILD
        SetRegView 64
      !endif

      ; Prevents breaking apps that don't use SetBrandNameVars
      !ifdef un.SetBrandNameVars
        ${un.SetBrandNameVars} "$INSTDIR\distribution\setup.ini"
      !endif

      ; Initialize $hHeaderBitmap to prevent redundant changing of the bitmap if
      ; the user clicks the back button
      StrCpy $hHeaderBitmap ""
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro un.UninstallUnOnInitCommonCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.UninstallUnOnInitCommon
  !verbose pop
!macroend

/**
 * Called from the MUI leaveOptions function to set the value of $INSTDIR.
 */
!macro LeaveOptionsCommon

  !ifndef LeaveOptionsCommon
    !insertmacro CanWriteToInstallDir
    !insertmacro GetLongPath

!ifndef NO_INSTDIR_FROM_REG
    !insertmacro GetSingleInstallPath
!endif

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define LeaveOptionsCommon "!insertmacro LeaveOptionsCommonCall"

    Function LeaveOptionsCommon
      Push $R9

      StrCpy $R9 "false"

!ifndef NO_INSTDIR_FROM_REG
      SetShellVarContext all      ; Set SHCTX to HKLM

      ${If} ${IsNativeAMD64}
      ${OrIf} ${IsNativeARM64}
        SetRegView 64
        ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
        SetRegView lastused
      ${EndIf}

      StrCmp "$R9" "false" +1 finish_get_install_dir

      SetRegView 32
      ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
      SetRegView lastused

      StrCmp "$R9" "false" +1 finish_get_install_dir

      SetShellVarContext current  ; Set SHCTX to HKCU
      ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9

      finish_get_install_dir:
      StrCmp "$R9" "false" +2 +1
      StrCpy $INSTDIR "$R9"
!endif

      ; 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).
      ${CanWriteToInstallDir} $R9
      ${If} "$R9" == "false"
        ; NOTE: This SetShellVarContext isn't directly needed anymore, but to
        ; leave the state consistent with earlier code I'm leaving it here.
        SetShellVarContext all      ; Set SHCTX to All Users
        ${GetLocalAppDataFolder} $R9
        StrCpy $INSTDIR "$R9\${BrandFullName}\"
        ${CanWriteToInstallDir} $R9
      ${EndIf}

      IfFileExists "$INSTDIR" +3 +1
      Pop $R9
      Return

      ; Always display the long path if the path already exists.
      ${GetLongPath} "$INSTDIR" $INSTDIR

      ; The call to GetLongPath returns a long path without a trailing
      ; back-slash. Append a \ to the path to prevent the directory
      ; name from being appended when using the NSIS create new folder.
      ; http://www.nullsoft.com/free/nsis/makensis.htm#InstallDir
      StrCpy $INSTDIR "$INSTDIR\"

      Pop $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro LeaveOptionsCommonCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call LeaveOptionsCommon
  !verbose pop
!macroend

/**
 * Called from the MUI preDirectory function to verify there is enough disk
 * space for the installation and the installation directory is writable.
 *
 * $R9 = returned value from CheckDiskSpace and CanWriteToInstallDir macros
 */
!macro PreDirectoryCommon

  !ifndef PreDirectoryCommon
    !insertmacro CanWriteToInstallDir
    !insertmacro CheckDiskSpace

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define PreDirectoryCommon "!insertmacro PreDirectoryCommonCall"

    Function PreDirectoryCommon
      Push $R9

      IntCmp $InstallType ${INSTALLTYPE_CUSTOM} end +1 +1
      ${CanWriteToInstallDir} $R9
      StrCmp "$R9" "false" end +1
      ${CheckDiskSpace} $R9
      StrCmp "$R9" "false" end +1
      Abort

      end:

      Pop $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro PreDirectoryCommonCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call PreDirectoryCommon
  !verbose pop
!macroend

/**
 * Called from the MUI leaveDirectory function
 *
 * @param   _WARN_DISK_SPACE
 *          Message displayed when there isn't enough disk space to perform the
 *          installation.
 * @param   _WARN_WRITE_ACCESS
 *          Message displayed when the installer does not have write access to
 *          $INSTDIR.
 *
 * $R7 = returned value from CheckDiskSpace and CanWriteToInstallDir macros
 * $R8 = _WARN_DISK_SPACE
 * $R9 = _WARN_WRITE_ACCESS
 */
!macro LeaveDirectoryCommon

  !ifndef LeaveDirectoryCommon
    !insertmacro CheckDiskSpace
    !insertmacro CanWriteToInstallDir

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define LeaveDirectoryCommon "!insertmacro LeaveDirectoryCommonCall"

    Function LeaveDirectoryCommon
      Exch $R9
      Exch 1
      Exch $R8
      Push $R7

      ${CanWriteToInstallDir} $R7
      ${If} $R7 == "false"
        MessageBox MB_OK|MB_ICONEXCLAMATION "$R9"
        Abort
      ${EndIf}

      ${CheckDiskSpace} $R7
      ${If} $R7 == "false"
        MessageBox MB_OK|MB_ICONEXCLAMATION "$R8"
        Abort
      ${EndIf}

      Pop $R7
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro LeaveDirectoryCommonCall _WARN_DISK_SPACE _WARN_WRITE_ACCESS
  !verbose push
  Push "${_WARN_DISK_SPACE}"
  Push "${_WARN_WRITE_ACCESS}"
  !verbose ${_MOZFUNC_VERBOSE}
  Call LeaveDirectoryCommon
  !verbose pop
!macroend


################################################################################
# Install Section common macros.

/**
 * Performs common cleanup operations prior to the actual installation.
 * This macro should be called first when installation starts.
 */
!macro InstallStartCleanupCommon

  !ifndef InstallStartCleanupCommon
    !insertmacro CleanVirtualStore
    !insertmacro EndUninstallLog
    !insertmacro OnInstallUninstall

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define InstallStartCleanupCommon "!insertmacro InstallStartCleanupCommonCall"

    Function InstallStartCleanupCommon

      ; Remove files not removed by parsing the uninstall.log
      Delete "$INSTDIR\install_wizard.log"
      Delete "$INSTDIR\install_status.log"

      RmDir /r "$INSTDIR\updates"
      Delete "$INSTDIR\updates.xml"
      Delete "$INSTDIR\active-update.xml"

      ; Remove files from the uninstall directory.
      ${If} ${FileExists} "$INSTDIR\uninstall"
        Delete "$INSTDIR\uninstall\*wizard*"
        Delete "$INSTDIR\uninstall\uninstall.ini"
        Delete "$INSTDIR\uninstall\cleanup.log"
        Delete "$INSTDIR\uninstall\uninstall.update"
        ${OnInstallUninstall}
      ${EndIf}

      ; Since we write to the uninstall.log in this directory during the
      ; installation create the directory if it doesn't already exist.
      IfFileExists "$INSTDIR\uninstall" +2 +1
      CreateDirectory "$INSTDIR\uninstall"

      ; Application update uses a directory named tobedeleted in the $INSTDIR to
      ; delete files on OS reboot when they are in use. Try to delete this
      ; directory if it exists.
      ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
        RmDir /r "$INSTDIR\${TO_BE_DELETED}"
      ${EndIf}

      ; Remove files that may be left behind by the application in the
      ; VirtualStore directory.
      ${CleanVirtualStore}
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro InstallStartCleanupCommonCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call InstallStartCleanupCommon
  !verbose pop
!macroend

/**
 * Performs common cleanup operations after the actual installation.
 * This macro should be called last during the installation.
 */
!macro InstallEndCleanupCommon

  !ifndef InstallEndCleanupCommon
    !insertmacro EndUninstallLog

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define InstallEndCleanupCommon "!insertmacro InstallEndCleanupCommonCall"

    Function InstallEndCleanupCommon

      ; Close the file handle to the uninstall.log
      ${EndUninstallLog}

    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro InstallEndCleanupCommonCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call InstallEndCleanupCommon
  !verbose pop
!macroend


################################################################################
# UAC Related Macros

/**
 * Provides UAC elevation support (requires the UAC plugin).
 *
 * $0 = return values from calls to the UAC plugin (always uses $0)
 * $R9 = return values from GetParameters and GetOptions macros
 */
!macro ElevateUAC

  !ifndef ${_MOZFUNC_UN}ElevateUAC
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetOptions
    !insertmacro ${_MOZFUNC_UN_TMP}GetParameters
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}ElevateUAC "!insertmacro ${_MOZFUNC_UN}ElevateUACCall"

    Function ${_MOZFUNC_UN}ElevateUAC
      Push $R9
      Push $0

!ifndef NONADMIN_ELEVATE
      UAC::IsAdmin
      ; If the user is not an admin already
      ${If} "$0" != "1"
        UAC::SupportsUAC
        ; If the system supports UAC
        ${If} "$0" == "1"
          UAC::GetElevationType
          ; If the user account has a split token
          ${If} "$0" == "3"
            UAC::RunElevated
            UAC::Unload
            ; Nothing besides UAC initialized so no need to call OnEndCommon
            Quit
          ${EndIf}
        ${EndIf}
      ${Else}
        ${GetParameters} $R9
        ${If} $R9 != ""
          ClearErrors
          ${GetOptions} "$R9" "/UAC:" $0
          ; If the command line contains /UAC then we need to initialize
          ; the UAC plugin to use UAC::ExecCodeSegment to execute code in
          ; the non-elevated context.
          ${Unless} ${Errors}
            UAC::RunElevated
          ${EndUnless}
        ${EndIf}
      ${EndIf}
!else
      UAC::IsAdmin
      ; If the user is not an admin already
      ${If} "$0" != "1"
        UAC::SupportsUAC
        ; If the system supports UAC require that the user elevate
        ${If} "$0" == "1"
          UAC::GetElevationType
          ; If the user account has a split token
          ${If} "$0" == "3"
            UAC::RunElevated
            ${If} "$0" == "0" ; Was elevation successful
              UAC::Unload
              ; Nothing besides UAC initialized so no need to call OnEndCommon
              Quit
            ${EndIf}
            ; Unload UAC since the elevation request was not successful and
            ; install anyway.
            UAC::Unload
          ${EndIf}
        ${Else}
          ; Check if UAC is enabled. If the user has turned UAC on or off
          ; without rebooting this value will be incorrect. This is an
          ; edgecase that we have to live with when trying to allow
          ; installing when the user doesn't have privileges such as a public
          ; computer while trying to also achieve UAC elevation. When this
          ; happens the user will be presented with the runas dialog if the
          ; value is 1 and won't be presented with the UAC dialog when the
          ; value is 0.
          ReadRegDWord $R9 HKLM "SOFTWARE\Microsoft\Windows\CurrentVersion\Policies\System" "EnableLUA"
          ${If} "$R9" == "1"
            ; This will display the UAC version of the runas dialog which
            ; requires a password for an existing user account.
            UAC::RunElevated
            ${If} "$0" == "0" ; Was elevation successful
              UAC::Unload
              ; Nothing besides UAC initialized so no need to call OnEndCommon
              Quit
            ${EndIf}
            ; Unload UAC since the elevation request was not successful and
            ; install anyway.
            UAC::Unload
          ${EndIf}
        ${EndIf}
      ${Else}
        ClearErrors
        ${${_MOZFUNC_UN}GetParameters} $R9
        ${${_MOZFUNC_UN}GetOptions} "$R9" "/UAC:" $R9
        ; If the command line contains /UAC then we need to initialize the UAC
        ; plugin to use UAC::ExecCodeSegment to execute code in the
        ; non-elevated context.
        ${Unless} ${Errors}
          UAC::RunElevated
        ${EndUnless}
      ${EndIf}
!endif

      ClearErrors

      Pop $0
      Pop $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro ElevateUACCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call ElevateUAC
  !verbose pop
!macroend

!macro un.ElevateUACCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.ElevateUAC
  !verbose pop
!macroend

!macro un.ElevateUAC
  !ifndef un.ElevateUAC
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro ElevateUAC

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Unloads the UAC plugin so the NSIS plugins can be removed when the installer
 * and uninstaller exit.
 *
 * $R9 = return values from GetParameters and GetOptions macros
 */
!macro UnloadUAC

  !ifndef ${_MOZFUNC_UN}UnloadUAC
    !define _MOZFUNC_UN_TMP_UnloadUAC ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP_UnloadUAC}GetOptions
    !insertmacro ${_MOZFUNC_UN_TMP_UnloadUAC}GetParameters
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP_UnloadUAC}
    !undef _MOZFUNC_UN_TMP_UnloadUAC

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}UnloadUAC "!insertmacro ${_MOZFUNC_UN}UnloadUACCall"

    Function ${_MOZFUNC_UN}UnloadUAC
      Push $R9

      ClearErrors
      ${${_MOZFUNC_UN}GetParameters} $R9
      ${${_MOZFUNC_UN}GetOptions} "$R9" "/UAC:" $R9
      ; If the command line contains /UAC then we need to unload the UAC plugin
      IfErrors +2 +1
      UAC::Unload

      ClearErrors

      Pop $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro UnloadUACCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call UnloadUAC
  !verbose pop
!macroend

!macro un.UnloadUACCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.UnloadUAC
  !verbose pop
!macroend

!macro un.UnloadUAC
  !ifndef un.UnloadUAC
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro UnloadUAC

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend


################################################################################
# Macros for uninstall.log and install.log logging
#
# Since these are used by other macros they should be inserted first. All of
# these macros can be easily inserted using the _LoggingCommon macro.

/**
 * Adds all logging macros in the correct order in one fell swoop as well as
 * the vars for the install.log and uninstall.log file handles.
 */
!macro _LoggingCommon
  Var /GLOBAL fhInstallLog
  Var /GLOBAL fhUninstallLog
  !insertmacro StartInstallLog
  !insertmacro EndInstallLog
  !insertmacro StartUninstallLog
  !insertmacro EndUninstallLog
!macroend
!define _LoggingCommon "!insertmacro _LoggingCommon"

/**
 * Creates a file named install.log in the install directory (e.g. $INSTDIR)
 * and adds the installation started message to the install.log for this
 * installation. This also adds the fhInstallLog and fhUninstallLog vars used
 * for logging.
 *
 * $fhInstallLog = filehandle for $INSTDIR\install.log
 *
 * @param   _APP_NAME
 *          Typically the BrandFullName
 * @param   _AB_CD
 *          The locale identifier
 * @param   _APP_VERSION
 *          The application version
 * @param   _GRE_VERSION
 *          The Gecko Runtime Engine version
 *
 * $R6 = _APP_NAME
 * $R7 = _AB_CD
 * $R8 = _APP_VERSION
 * $R9 = _GRE_VERSION
 */
!macro StartInstallLog

  !ifndef StartInstallLog
    !insertmacro GetTime

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define StartInstallLog "!insertmacro StartInstallLogCall"

    Function StartInstallLog
      Exch $R9
      Exch 1
      Exch $R8
      Exch 2
      Exch $R7
      Exch 3
      Exch $R6
      Push $R5
      Push $R4
      Push $R3
      Push $R2
      Push $R1
      Push $R0
      Push $9

      ${DeleteFile} "$INSTDIR\install.log"
      FileOpen $fhInstallLog "$INSTDIR\install.log" w
      FileWriteWord $fhInstallLog "65279"

      ${GetTime} "" "L" $9 $R0 $R1 $R2 $R3 $R4 $R5
      FileWriteUTF16LE $fhInstallLog "$R6 Installation Started: $R1-$R0-$9 $R3:$R4:$R5"
      ${WriteLogSeparator}

      ${LogHeader} "Installation Details"
      ${LogMsg} "Install Dir: $INSTDIR"
      ${LogMsg} "Locale     : $R7"
      ${LogMsg} "App Version: $R8"
      ${LogMsg} "GRE Version: $R9"

      ${If} ${IsWin10}
        ${LogMsg} "OS Name    : Windows 10"
      ${ElseIf} ${AtLeastWin10}
        ${LogMsg} "OS Name    : Above Windows 10"
      ${Else}
        ${LogMsg} "OS Name    : Unable to detect"
      ${EndIf}

      ${LogMsg} "Target CPU : ${ARCH}"

      Pop $9
      Pop $R0
      Pop $R1
      Pop $R2
      Pop $R3
      Pop $R4
      Pop $R5
      Exch $R6
      Exch 3
      Exch $R7
      Exch 2
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro StartInstallLogCall _APP_NAME _AB_CD _APP_VERSION _GRE_VERSION
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_APP_NAME}"
  Push "${_AB_CD}"
  Push "${_APP_VERSION}"
  Push "${_GRE_VERSION}"
  Call StartInstallLog
  !verbose pop
!macroend

/**
 * Writes the installation finished message to the install.log and closes the
 * file handles to the install.log and uninstall.log
 *
 * @param   _APP_NAME
 *
 * $R9 = _APP_NAME
 */
!macro EndInstallLog

  !ifndef EndInstallLog
    !insertmacro GetTime

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define EndInstallLog "!insertmacro EndInstallLogCall"

    Function EndInstallLog
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5
      Push $R4
      Push $R3
      Push $R2

      ${WriteLogSeparator}
      ${GetTime} "" "L" $R2 $R3 $R4 $R5 $R6 $R7 $R8
      FileWriteUTF16LE $fhInstallLog "$R9 Installation Finished: $R4-$R3-$R2 $R6:$R7:$R8$\r$\n"
      FileClose $fhInstallLog

      Pop $R2
      Pop $R3
      Pop $R4
      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro EndInstallLogCall _APP_NAME
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_APP_NAME}"
  Call EndInstallLog
  !verbose pop
!macroend

/**
 * Opens the file handle to the uninstall.log.
 *
 * $fhUninstallLog = filehandle for $INSTDIR\uninstall\uninstall.log
 */
!macro StartUninstallLog

  !ifndef StartUninstallLog
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define StartUninstallLog "!insertmacro StartUninstallLogCall"

    Function StartUninstallLog
      FileOpen $fhUninstallLog "$INSTDIR\uninstall\uninstall.log" w
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro StartUninstallLogCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call StartUninstallLog
  !verbose pop
!macroend

/**
 * Closes the file handle to the uninstall.log.
 */
!macro EndUninstallLog

  !ifndef EndUninstallLog

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define EndUninstallLog "!insertmacro EndUninstallLogCall"

    Function EndUninstallLog
      FileClose $fhUninstallLog
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro EndUninstallLogCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call EndUninstallLog
  !verbose pop
!macroend

/**
 * Adds a section header to the human readable log.
 *
 * @param   _HEADER
 *          The header text to write to the log.
 */
!macro LogHeader _HEADER
  ${WriteLogSeparator}
  FileWriteUTF16LE $fhInstallLog "${_HEADER}"
  ${WriteLogSeparator}
!macroend
!define LogHeader "!insertmacro LogHeader"

/**
 * Adds a section message to the human readable log.
 *
 * @param   _MSG
 *          The message text to write to the log.
 */
!macro LogMsg _MSG
  FileWriteUTF16LE $fhInstallLog "  ${_MSG}$\r$\n"
!macroend
!define LogMsg "!insertmacro LogMsg"

/**
 * Adds an uninstall entry to the uninstall log.
 *
 * @param   _MSG
 *          The message text to write to the log.
 */
!macro LogUninstall _MSG
  FileWrite $fhUninstallLog "${_MSG}$\r$\n"
!macroend
!define LogUninstall "!insertmacro LogUninstall"

/**
 * Adds a section divider to the human readable log.
 */
!macro WriteLogSeparator
  FileWriteUTF16LE $fhInstallLog "$\r$\n----------------------------------------\
                                  ---------------------------------------$\r$\n"
!macroend
!define WriteLogSeparator "!insertmacro WriteLogSeparator"


################################################################################
# Macros for managing the shortcuts log ini file

/**
 * Adds the most commonly used shortcut logging macros for the installer in one
 * fell swoop.
 */
!macro _LoggingShortcutsCommon
  !insertmacro LogDesktopShortcut
  !insertmacro LogQuickLaunchShortcut
  !insertmacro LogSMProgramsShortcut
!macroend
!define _LoggingShortcutsCommon "!insertmacro _LoggingShortcutsCommon"

/**
 * Creates the shortcuts log ini file with a UTF-16LE BOM if it doesn't exist.
 */
!macro initShortcutsLog
  Push $R9

  IfFileExists "$INSTDIR\uninstall\${SHORTCUTS_LOG}" +4 +1
  FileOpen $R9 "$INSTDIR\uninstall\${SHORTCUTS_LOG}" w
  FileWriteWord $R9 "65279"
  FileClose $R9

  Pop $R9
!macroend
!define initShortcutsLog "!insertmacro initShortcutsLog"

/**
 * Adds shortcut entries to the shortcuts log ini file. This macro is primarily
 * a helper used by the LogDesktopShortcut, LogQuickLaunchShortcut, and
 * LogSMProgramsShortcut macros but it can be used by other code if desired. If
 * the value already exists the the value is not written to the file.
 *
 * @param   _SECTION_NAME
 *          The section name to write to in the shortcut log ini file
 * @param   _FILE_NAME
 *          The shortcut's file name
 *
 * $R6 = return value from ReadIniStr for the shortcut file name
 * $R7 = counter for supporting multiple shortcuts in the same location
 * $R8 = _SECTION_NAME
 * $R9 = _FILE_NAME
 */
!macro LogShortcut

  !ifndef LogShortcut
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define LogShortcut "!insertmacro LogShortcutCall"

    Function LogShortcut
      Exch $R9
      Exch 1
      Exch $R8
      Push $R7
      Push $R6

      ClearErrors

      !insertmacro initShortcutsLog

      StrCpy $R6 ""
      StrCpy $R7 -1

      StrCmp "$R6" "$R9" +5 +1 ; if the shortcut already exists don't add it
      IntOp $R7 $R7 + 1 ; increment the counter
      ReadIniStr $R6 "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "$R8" "Shortcut$R7"
      IfErrors +1 -3
      WriteINIStr "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "$R8" "Shortcut$R7" "$R9"

      ClearErrors

      Pop $R6
      Pop $R7
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro LogShortcutCall _SECTION_NAME _FILE_NAME
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_SECTION_NAME}"
  Push "${_FILE_NAME}"
  Call LogShortcut
  !verbose pop
!macroend

/**
 * Adds a Desktop shortcut entry to the shortcuts log ini file.
 *
 * @param   _FILE_NAME
 *          The shortcut file name (e.g. shortcut.lnk)
 */
!macro LogDesktopShortcut

  !ifndef LogDesktopShortcut
    !insertmacro LogShortcut

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define LogDesktopShortcut "!insertmacro LogDesktopShortcutCall"

    Function LogDesktopShortcut
      Call LogShortcut
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro LogDesktopShortcutCall _FILE_NAME
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "DESKTOP"
  Push "${_FILE_NAME}"
  Call LogDesktopShortcut
  !verbose pop
!macroend

/**
 * Adds a QuickLaunch shortcut entry to the shortcuts log ini file.
 *
 * @param   _FILE_NAME
 *          The shortcut file name (e.g. shortcut.lnk)
 */
!macro LogQuickLaunchShortcut

  !ifndef LogQuickLaunchShortcut
    !insertmacro LogShortcut

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define LogQuickLaunchShortcut "!insertmacro LogQuickLaunchShortcutCall"

    Function LogQuickLaunchShortcut
      Call LogShortcut
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro LogQuickLaunchShortcutCall _FILE_NAME
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "QUICKLAUNCH"
  Push "${_FILE_NAME}"
  Call LogQuickLaunchShortcut
  !verbose pop
!macroend

/**
 * Adds a Start Menu shortcut entry to the shortcuts log ini file.
 *
 * @param   _FILE_NAME
 *          The shortcut file name (e.g. shortcut.lnk)
 */
!macro LogStartMenuShortcut

  !ifndef LogStartMenuShortcut
    !insertmacro LogShortcut

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define LogStartMenuShortcut "!insertmacro LogStartMenuShortcutCall"

    Function LogStartMenuShortcut
      Call LogShortcut
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro LogStartMenuShortcutCall _FILE_NAME
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "STARTMENU"
  Push "${_FILE_NAME}"
  Call LogStartMenuShortcut
  !verbose pop
!macroend

/**
 * Adds a Start Menu Programs shortcut entry to the shortcuts log ini file.
 *
 * @param   _FILE_NAME
 *          The shortcut file name (e.g. shortcut.lnk)
 */
!macro LogSMProgramsShortcut

  !ifndef LogSMProgramsShortcut
    !insertmacro LogShortcut

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define LogSMProgramsShortcut "!insertmacro LogSMProgramsShortcutCall"

    Function LogSMProgramsShortcut
      Call LogShortcut
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro LogSMProgramsShortcutCall _FILE_NAME
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "SMPROGRAMS"
  Push "${_FILE_NAME}"
  Call LogSMProgramsShortcut
  !verbose pop
!macroend

/**
 * Adds the relative path from the Start Menu Programs directory for the
 * application's Start Menu directory if it is different from the existing value
 * to the shortcuts log ini file.
 *
 * @param   _REL_PATH_TO_DIR
 *          The relative path from the Start Menu Programs directory to the
 *          program's directory.
 *
 * $R9 = _REL_PATH_TO_DIR
 */
!macro LogSMProgramsDirRelPath _REL_PATH_TO_DIR
  Push $R9

  !insertmacro initShortcutsLog

  ReadINIStr $R9 "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "SMPROGRAMS" "RelativePathToDir"
  StrCmp "$R9" "${_REL_PATH_TO_DIR}" +2 +1
  WriteINIStr "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "SMPROGRAMS" "RelativePathToDir" "${_REL_PATH_TO_DIR}"

  Pop $R9
!macroend
!define LogSMProgramsDirRelPath "!insertmacro LogSMProgramsDirRelPath"

/**
 * Copies the value for the relative path from the Start Menu programs directory
 * (e.g. $SMPROGRAMS) to the Start Menu directory as it is stored in the
 * shortcuts log ini file to the variable specified in the first parameter.
 */
!macro GetSMProgramsDirRelPath _VAR
  ReadINIStr ${_VAR} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" "SMPROGRAMS" \
             "RelativePathToDir"
!macroend
!define GetSMProgramsDirRelPath "!insertmacro GetSMProgramsDirRelPath"

/**
 * Copies the shortcuts log ini file path to the variable specified in the
 * first parameter.
 */
!macro GetShortcutsLogPath _VAR
  StrCpy ${_VAR} "$INSTDIR\uninstall\${SHORTCUTS_LOG}"
!macroend
!define GetShortcutsLogPath "!insertmacro GetShortcutsLogPath"


################################################################################
# Macros for managing specific Windows version features

/**
 * Sets the permitted layered service provider (LSP) categories
 * for the application. Consumers should call this after an
 * installation log section has completed since this macro will log the results
 * to the installation log along with a header.
 *
 * !IMPORTANT - When calling this macro from an uninstaller do not specify a
 *              parameter. The paramter is hardcoded with 0x00000000 to remove
 *              the LSP category for the application when performing an
 *              uninstall.
 *
 * @param   _LSP_CATEGORIES
 *          The permitted LSP categories for the application. When called by an
 *          uninstaller this will always be 0x00000000.
 *
 * $R5 = error code popped from the stack for the WSCSetApplicationCategory call
 * $R6 = return value from the WSCSetApplicationCategory call
 * $R7 = string length for the long path to the main application executable
 * $R8 = long path to the main application executable
 * $R9 = _LSP_CATEGORIES
 */
!macro SetAppLSPCategories

  !ifndef ${_MOZFUNC_UN}SetAppLSPCategories
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}SetAppLSPCategories "!insertmacro ${_MOZFUNC_UN}SetAppLSPCategoriesCall"

    Function ${_MOZFUNC_UN}SetAppLSPCategories
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5

      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR\${FileMainEXE}" $R8
      StrLen $R7 "$R8"

      ; Remove existing categories by setting the permitted categories to
      ; 0x00000000 since new categories are ANDed with existing categories. If
      ; the param value stored in $R9 is 0x00000000 then skip the removal since
      ; the categories  will be removed by the second call to
      ; WSCSetApplicationCategory.
      StrCmp "$R9" "0x00000000" +2 +1
      System::Call "Ws2_32::WSCSetApplicationCategory(w R8, i R7, w n, i 0,\
                                                      i 0x00000000, i n, *i) i"

      ; Set the permitted LSP categories
      System::Call "Ws2_32::WSCSetApplicationCategory(w R8, i R7, w n, i 0,\
                                                      i R9, i n, *i .s) i.R6"
      Pop $R5

!ifndef NO_LOG
      ${LogHeader} "Setting Permitted LSP Categories"
      StrCmp "$R6" 0 +3 +1
      ${LogMsg} "** ERROR Setting LSP Categories: $R5 **"
      GoTo +2
      ${LogMsg} "Permitted LSP Categories: $R9"
!endif

      ClearErrors

      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro SetAppLSPCategoriesCall _LSP_CATEGORIES
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_LSP_CATEGORIES}"
  Call SetAppLSPCategories
  !verbose pop
!macroend

!macro un.SetAppLSPCategoriesCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "0x00000000"
  Call un.SetAppLSPCategories
  !verbose pop
!macroend

!macro un.SetAppLSPCategories
  !ifndef un.SetAppLSPCategories
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro SetAppLSPCategories

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Checks if any pinned TaskBar lnk files point to the executable's path passed
 * to the macro.
 *
 * @param   _EXE_PATH
 *          The executable path
 * @return  _RESULT
 *          false if no pinned shotcuts were found for this install location.
 *          true if pinned shotcuts were found for this install location.
 *
 * $R5 = stores whether a TaskBar lnk file has been found for the executable
 * $R6 = long path returned from GetShortCutTarget and GetLongPath
 * $R7 = file name returned from FindFirst and FindNext
 * $R8 = find handle for FindFirst and FindNext
 * $R9 = _EXE_PATH and _RESULT
 */
!macro IsPinnedToTaskBar

  !ifndef IsPinnedToTaskBar
    !insertmacro GetLongPath

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define IsPinnedToTaskBar "!insertmacro IsPinnedToTaskBarCall"

    Function IsPinnedToTaskBar
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5

      StrCpy $R5 "false"

      ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar"
        FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\TaskBar\*.lnk"
        ${Do}
          ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar\$R7"
            ShellLink::GetShortCutTarget "$QUICKLAUNCH\User Pinned\TaskBar\$R7"
            Pop $R6
            ${GetLongPath} "$R6" $R6
            ${If} "$R6" == "$R9"
              StrCpy $R5 "true"
              ${ExitDo}
            ${EndIf}
          ${EndIf}
          ClearErrors
          FindNext $R8 $R7
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}
        ${Loop}
        FindClose $R8
      ${EndIf}

      ClearErrors

      StrCpy $R9 $R5

      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro IsPinnedToTaskBarCall _EXE_PATH _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_EXE_PATH}"
  Call IsPinnedToTaskBar
  Pop ${_RESULT}
  !verbose pop
!macroend

/**
 * Checks if any pinned Start Menu lnk files point to the executable's path
 * passed to the macro.
 *
 * @param   _EXE_PATH
 *          The executable path
 * @return  _RESULT
 *          false if no pinned shotcuts were found for this install location.
 *          true if pinned shotcuts were found for this install location.
 *
 * $R5 = stores whether a Start Menu lnk file has been found for the executable
 * $R6 = long path returned from GetShortCutTarget and GetLongPath
 * $R7 = file name returned from FindFirst and FindNext
 * $R8 = find handle for FindFirst and FindNext
 * $R9 = _EXE_PATH and _RESULT
 */
!macro IsPinnedToStartMenu

  !ifndef IsPinnedToStartMenu
    !insertmacro GetLongPath

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define IsPinnedToStartMenu "!insertmacro IsPinnedToStartMenuCall"

    Function IsPinnedToStartMenu
      Exch $R9
      Push $R8
      Push $R7
      Push $R6
      Push $R5

      StrCpy $R5 "false"

      ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu"
        FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\StartMenu\*.lnk"
        ${Do}
          ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu\$R7"
            ShellLink::GetShortCutTarget "$QUICKLAUNCH\User Pinned\StartMenu\$R7"
            Pop $R6
            ${GetLongPath} "$R6" $R6
            ${If} "$R6" == "$R9"
              StrCpy $R5 "true"
              ${ExitDo}
            ${EndIf}
          ${EndIf}
          ClearErrors
          FindNext $R8 $R7
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}
        ${Loop}
        FindClose $R8
      ${EndIf}

      ClearErrors

      StrCpy $R9 $R5

      Pop $R5
      Pop $R6
      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro IsPinnedToStartMenuCall _EXE_PATH _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_EXE_PATH}"
  Call IsPinnedToStartMenu
  Pop ${_RESULT}
  !verbose pop
!macroend

/**
 * Gets the number of pinned shortcut lnk files pinned to the Task Bar.
 *
 * @return  _RESULT
 *          number of pinned shortcut lnk files.
 *
 * $R7 = file name returned from FindFirst and FindNext
 * $R8 = find handle for FindFirst and FindNext
 * $R9 = _RESULT
 */
!macro PinnedToTaskBarLnkCount

  !ifndef PinnedToTaskBarLnkCount
    !insertmacro GetLongPath

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define PinnedToTaskBarLnkCount "!insertmacro PinnedToTaskBarLnkCountCall"

    Function PinnedToTaskBarLnkCount
      Push $R9
      Push $R8
      Push $R7

      StrCpy $R9 0

      ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar"
        FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\TaskBar\*.lnk"
        ${Do}
          ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\TaskBar\$R7"
            IntOp $R9 $R9 + 1
          ${EndIf}
          ClearErrors
          FindNext $R8 $R7
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}
        ${Loop}
        FindClose $R8
      ${EndIf}

      ClearErrors

      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro PinnedToTaskBarLnkCountCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call PinnedToTaskBarLnkCount
  Pop ${_RESULT}
  !verbose pop
!macroend

/**
 * Gets the number of pinned shortcut lnk files pinned to the Start Menu.
 *
 * @return  _RESULT
 *          number of pinned shortcut lnk files.
 *
 * $R7 = file name returned from FindFirst and FindNext
 * $R8 = find handle for FindFirst and FindNext
 * $R9 = _RESULT
 */
!macro PinnedToStartMenuLnkCount

  !ifndef PinnedToStartMenuLnkCount
    !insertmacro GetLongPath

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define PinnedToStartMenuLnkCount "!insertmacro PinnedToStartMenuLnkCountCall"

    Function PinnedToStartMenuLnkCount
      Push $R9
      Push $R8
      Push $R7

      StrCpy $R9 0

      ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu"
        FindFirst $R8 $R7 "$QUICKLAUNCH\User Pinned\StartMenu\*.lnk"
        ${Do}
          ${If} ${FileExists} "$QUICKLAUNCH\User Pinned\StartMenu\$R7"
            IntOp $R9 $R9 + 1
          ${EndIf}
          ClearErrors
          FindNext $R8 $R7
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}
        ${Loop}
        FindClose $R8
      ${EndIf}

      ClearErrors

      Pop $R7
      Pop $R8
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro PinnedToStartMenuLnkCountCall _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call PinnedToStartMenuLnkCount
  Pop ${_RESULT}
  !verbose pop
!macroend

/**
 * Update Start Menu / TaskBar lnk files that point to the executable's path
 * passed to the macro and all other shortcuts installed by the application with
 * the current application user model ID. Requires ApplicationID.
 *
 * NOTE: this does not update Desktop shortcut application user model ID due to
 *       bug 633728.
 *
 * @param   _EXE_PATH
 *          The main application executable path
 * @param   _APP_ID
 *          The application user model ID for the current install
 * @return  _RESULT
 *          false if no pinned shotcuts were found for this install location.
 *          true if pinned shotcuts were found for this install location.
 */
!macro UpdateShortcutAppModelIDs

  !ifndef UpdateShortcutAppModelIDs
    !insertmacro GetLongPath

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define UpdateShortcutAppModelIDs "!insertmacro UpdateShortcutAppModelIDsCall"

    Function UpdateShortcutAppModelIDs
      ; stack: path, appid
      Exch $R9 ; stack: $R9, appid | $R9 = path
      Exch 1   ; stack: appid, $R9
      Exch $R8 ; stack: $R8, $R9   | $R8 = appid
      Push $R7 ; stack: $R7, $R8, $R9
      Push $R6
      Push $R5
      Push $R4
      Push $R3 ; stack: $R3, $R5, $R6, $R7, $R8, $R9
      Push $R2

      ; $R9 = main application executable path
      ; $R8 = appid
      ; $R7 = path to the application's start menu programs directory
      ; $R6 = path to the shortcut log ini file
      ; $R5 = shortcut filename
      ; $R4 = GetShortCutTarget result

      StrCpy $R3 "false"

      ; installed shortcuts
      ${${_MOZFUNC_UN}GetLongPath} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" $R6
      ${If} ${FileExists} "$R6"
        ; Update the Start Menu shortcuts' App ID for this application
        StrCpy $R2 -1
        ${Do}
          IntOp $R2 $R2 + 1 ; Increment the counter
          ClearErrors
          ReadINIStr $R5 "$R6" "STARTMENU" "Shortcut$R2"
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}

          ${If} ${FileExists} "$SMPROGRAMS\$R5"
            ShellLink::GetShortCutTarget "$SMPROGRAMS\$$R5"
            Pop $R4
            ${GetLongPath} "$R4" $R4
            ${If} "$R4" == "$R9" ; link path == install path
              ApplicationID::Set "$SMPROGRAMS\$R5" "$R8" "true"
              Pop $R4
            ${EndIf}
          ${EndIf}
        ${Loop}

        ; Update the Quick Launch shortcuts' App ID for this application
        StrCpy $R2 -1
        ${Do}
          IntOp $R2 $R2 + 1 ; Increment the counter
          ClearErrors
          ReadINIStr $R5 "$R6" "QUICKLAUNCH" "Shortcut$R2"
          ${If} ${Errors}
            ${ExitDo}
          ${EndIf}

          ${If} ${FileExists} "$QUICKLAUNCH\$R5"
            ShellLink::GetShortCutTarget "$QUICKLAUNCH\$R5"
            Pop $R4
            ${GetLongPath} "$R4" $R4
            ${If} "$R4" == "$R9" ; link path == install path
              ApplicationID::Set "$QUICKLAUNCH\$R5" "$R8" "true"
              Pop $R4
            ${EndIf}
          ${EndIf}
        ${Loop}

        ; Update the Start Menu Programs shortcuts' App ID for this application
        ClearErrors
        ReadINIStr $R7 "$R6" "SMPROGRAMS" "RelativePathToDir"
        ${Unless} ${Errors}
          ${${_MOZFUNC_UN}GetLongPath} "$SMPROGRAMS\$R7" $R7
          ${Unless} "$R7" == ""
            StrCpy $R2 -1
            ${Do}
              IntOp $R2 $R2 + 1 ; Increment the counter
              ClearErrors
              ReadINIStr $R5 "$R6" "SMPROGRAMS" "Shortcut$R2"
              ${If} ${Errors}
                ${ExitDo}
              ${EndIf}

              ${If} ${FileExists} "$R7\$R5"
                ShellLink::GetShortCutTarget "$R7\$R5"
                Pop $R4
                ${GetLongPath} "$R4" $R4
                ${If} "$R4" == "$R9" ; link path == install path
                  ApplicationID::Set "$R7\$R5" "$R8" "true"
                  Pop $R4
                ${EndIf}
              ${EndIf}
            ${Loop}
          ${EndUnless}
        ${EndUnless}
      ${EndIf}

      StrCpy $R7 "$QUICKLAUNCH\User Pinned"
      StrCpy $R3 "false"

      ; $R9 = main application executable path
      ; $R8 = appid
      ; $R7 = user pinned path
      ; $R6 = find handle
      ; $R5 = found filename
      ; $R4 = GetShortCutTarget result

      ; TaskBar links
      FindFirst $R6 $R5 "$R7\TaskBar\*.lnk"
      ${Do}
        ${If} ${FileExists} "$R7\TaskBar\$R5"
          ShellLink::GetShortCutTarget "$R7\TaskBar\$R5"
          Pop $R4
          ${If} "$R4" == "$R9" ; link path == install path
            ApplicationID::Set "$R7\TaskBar\$R5" "$R8" "true"
            Pop $R4 ; pop Set result off the stack
            StrCpy $R3 "true"
          ${EndIf}
        ${EndIf}
        ClearErrors
        FindNext $R6 $R5
        ${If} ${Errors}
          ${ExitDo}
        ${EndIf}
      ${Loop}
      FindClose $R6

      ; Start menu links
      FindFirst $R6 $R5 "$R7\StartMenu\*.lnk"
      ${Do}
        ${If} ${FileExists} "$R7\StartMenu\$R5"
          ShellLink::GetShortCutTarget "$R7\StartMenu\$R5"
          Pop $R4
          ${If} "$R4" == "$R9" ; link path == install path
            ApplicationID::Set "$R7\StartMenu\$R5" "$R8" "true"
            Pop $R4 ; pop Set result off the stack
            StrCpy $R3 "true"
          ${EndIf}
        ${EndIf}
        ClearErrors
        FindNext $R6 $R5
        ${If} ${Errors}
          ${ExitDo}
        ${EndIf}
      ${Loop}
      FindClose $R6

      ClearErrors

      StrCpy $R9 $R3

      Pop $R2
      Pop $R3  ; stack: $R4, $R5, $R6, $R7, $R8, $R9
      Pop $R4  ; stack: $R5, $R6, $R7, $R8, $R9
      Pop $R5  ; stack: $R6, $R7, $R8, $R9
      Pop $R6  ; stack: $R7, $R8, $R9
      Pop $R7  ; stack: $R8, $R9
      Exch $R8 ; stack: appid, $R9 | $R8 = old $R8
      Exch 1   ; stack: $R9, appid
      Exch $R9 ; stack: path, appid | $R9 = old $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro UpdateShortcutAppModelIDsCall _EXE_PATH _APP_ID _RESULT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_APP_ID}"
  Push "${_EXE_PATH}"
  Call UpdateShortcutAppModelIDs
  Pop ${_RESULT}
  !verbose pop
!macroend

!macro IsUserAdmin
  ; Copied from: http://nsis.sourceforge.net/IsUserAdmin
  Function IsUserAdmin
    Push $R0
    Push $R1
    Push $R2

    ClearErrors
    UserInfo::GetName
    IfErrors Win9x
    Pop $R1
    UserInfo::GetAccountType
    Pop $R2

    StrCmp $R2 "Admin" 0 Continue
    StrCpy $R0 "true"
    Goto Done

    Continue:

    StrCmp $R2 "" Win9x
    StrCpy $R0 "false"
    Goto Done

    Win9x:
    StrCpy $R0 "true"

    Done:
    Pop $R2
    Pop $R1
    Exch $R0
  FunctionEnd
!macroend

/**
 * Retrieve if present or generate and store a 64 bit hash of an install path
 * using the City Hash algorithm.  On return the resulting id is saved in the
 * $AppUserModelID variable declared by inserting this macro. InitHashAppModelId
 * will attempt to load from HKLM/_REG_PATH first, then HKCU/_REG_PATH. If found
 * in either it will return the hash it finds. If not found it will generate a
 * new hash and attempt to store the hash in HKLM/_REG_PATH, then HKCU/_REG_PATH.
 * Subsequent calls will then retreive the stored hash value. On any failure,
 * $AppUserModelID will be set to an empty string.
 *
 * A second string that will be used for Private Browsing mode is also generated by
 * taking the regular $AppUserModelID and appending ";PrivateBrowsingAUMID" to it.
 * This is stored in $AppUserModelIDPrivate upon successful completion of this
 * function.
 *
 * Registry format: root/_REG_PATH/"_EXE_PATH" = "hash"
 *
 * @param   _EXE_PATH
 *          The main application executable path
 * @param   _REG_PATH
 *          The HKLM/HKCU agnostic registry path where the key hash should
 *          be stored. ex: "Software\Mozilla\Firefox\TaskBarIDs"
 * @result  (Var) $AppUserModelID and $AppUserModelIDPrivate contain the
 *          app model id and the private app model id, respectively.
 */
!macro InitHashAppModelId
  !ifndef ${_MOZFUNC_UN}InitHashAppModelId
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !ifndef InitHashAppModelId
      Var AppUserModelID
      Var AppUserModelIDPrivate
    !endif

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}InitHashAppModelId "!insertmacro ${_MOZFUNC_UN}InitHashAppModelIdCall"

    Function ${_MOZFUNC_UN}InitHashAppModelId
      ; stack: apppath, regpath
      Exch $R9 ; stack: $R9, regpath | $R9 = apppath
      Exch 1   ; stack: regpath, $R9
      Exch $R8 ; stack: $R8, $R9   | $R8 = regpath
      Push $R7

      ${${_MOZFUNC_UN}GetLongPath} "$R9" $R9
      ; Always create a new AppUserModelID and overwrite the existing one
      ; for the current installation path.
      CityHash::GetCityHash64 "$R9"
      Pop $AppUserModelID
      ${If} $AppUserModelID == "error"
        GoTo end
      ${EndIf}
      ClearErrors
      WriteRegStr HKLM "$R8" "$R9" "$AppUserModelID"
      ${If} ${Errors}
        ClearErrors
        WriteRegStr HKCU "$R8" "$R9" "$AppUserModelID"
        ${If} ${Errors}
          StrCpy $AppUserModelID "error"
        ${EndIf}
      ${EndIf}

      end:
      ${If} "$AppUserModelID" == "error"
        StrCpy $AppUserModelID ""
        StrCpy $AppUserModelIDPrivate ""
      ${Else}
        StrCpy $AppUserModelIDPrivate "$AppUserModelID;PrivateBrowsingAUMID"
      ${EndIf}

      ClearErrors
      Pop $R7
      Exch $R8
      Exch 1
      Exch $R9
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro InitHashAppModelIdCall _EXE_PATH _REG_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_REG_PATH}"
  Push "${_EXE_PATH}"
  Call InitHashAppModelId
  !verbose pop
!macroend

!macro un.InitHashAppModelIdCall _EXE_PATH _REG_PATH
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_REG_PATH}"
  Push "${_EXE_PATH}"
  Call un.InitHashAppModelId
  !verbose pop
!macroend

!macro un.InitHashAppModelId
  !ifndef un.InitHashAppModelId
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro InitHashAppModelId

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Try to locate the default profile of this install from AppUserModelID
 * using installs.ini.
 * FIXME This could instead use the Install<AUMID> entries in profiles.ini?
 *
 * - `SetShellVarContext current` must be called before this macro so
 *   $APPDATA gets the current user's data.
 * - InitHashAppModelId must have been called before this macro to set
 *   $AppUserModelID
 *
 * @result: Path of the profile directory (not checked for existence),
 *          or "" if not found, left on top of the stack.
 */
!macro FindInstallSpecificProfileMaybeUn _MOZFUNC_UN
  Push $R0
  Push $0
  Push $1
  Push $2

  StrCpy $R0 ""
  ; Look for an install-specific profile, which might be listed as
  ; either a relative or an absolute path (installs.ini doesn't say which).
  ${If} ${FileExists} "$APPDATA\Mozilla\Firefox\installs.ini"
    ClearErrors
    ReadINIStr $1 "$APPDATA\Mozilla\Firefox\installs.ini" "$AppUserModelID" "Default"
    ${IfNot} ${Errors}
      ${${_MOZFUNC_UN}GetLongPath} "$APPDATA\Mozilla\Firefox\$1" $2
      ${If} ${FileExists} $2
        StrCpy $R0 $2
      ${Else}
        ${${_MOZFUNC_UN}GetLongPath} "$1" $2
        ${If} ${FileExists} $2
          StrCpy $R0 $2
        ${EndIf}
      ${EndIf}
    ${EndIf}
  ${EndIf}

  Pop $2
  Pop $1
  Pop $0
  Exch $R0
!macroend
!define FindInstallSpecificProfile "!insertmacro FindInstallSpecificProfileMaybeUn ''"
!define un.FindInstallSpecificProfile "!insertmacro FindInstallSpecificProfileMaybeUn un."

/**
 * Copy the post-signing data, which was left alongside the installer
 * by the self-extractor stub, into the global location for this data.
 *
 * If the post-signing data file doesn't exist, or is empty, "0" is
 * pushed on the stack, and nothing is copied.
 * Otherwise the first line of the post-signing data (including newline,
 * if any) is pushed on the stack.
 */
!macro CopyPostSigningData
  !ifndef CopyPostSigningData
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define CopyPostSigningData "Call CopyPostSigningData"

    Function CopyPostSigningData
      Push $0   ; Stack: old $0
      Push $1   ; Stack: $1, old $0

      ${LineRead} "$EXEDIR\postSigningData" "1" $0
      ${If} ${Errors}
        ClearErrors
        StrCpy $0 "0"
      ${Else}
        CopyFiles /SILENT "$EXEDIR\postSigningData" "$INSTDIR"
      ${Endif}

      Pop $1    ; Stack: old $0
      Exch $0   ; Stack: postSigningData
    FunctionEnd

    !verbose pop
  !endif
!macroend

################################################################################
# Helpers for taskbar progress

!ifndef CLSCTX_INPROC_SERVER
  !define CLSCTX_INPROC_SERVER  1
!endif

!define CLSID_ITaskbarList {56fdf344-fd6d-11d0-958a-006097c9a090}
!define IID_ITaskbarList3 {ea1afb91-9e28-4b86-90e9-9e9f8a5eefaf}
!define ITaskbarList3->SetProgressValue $ITaskbarList3->9
!define ITaskbarList3->SetProgressState $ITaskbarList3->10

/**
 * Creates a single uninitialized object of the ITaskbarList class with a
 * reference to the ITaskbarList3 interface. This object can be used to set
 * progress and state on the installer's taskbar icon using the helper macros
 * in this section.
 */
!macro ITBL3Create

  !ifndef ${_MOZFUNC_UN}ITBL3Create
    Var ITaskbarList3

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}ITBL3Create "!insertmacro ${_MOZFUNC_UN}ITBL3CreateCall"

    Function ${_MOZFUNC_UN}ITBL3Create
      ; Setting to 0 allows the helper macros to detect when the object was not
      ; created.
      StrCpy $ITaskbarList3 0
      ; Don't create when running silently.
      ${Unless} ${Silent}
        System::Call "ole32::CoCreateInstance(g '${CLSID_ITaskbarList}', \
                                              i 0, \
                                              i ${CLSCTX_INPROC_SERVER}, \
                                              g '${IID_ITaskbarList3}', \
                                              *i .s)"
        Pop $ITaskbarList3
      ${EndUnless}
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro ITBL3CreateCall
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call ITBL3Create
  !verbose pop
!macroend

!macro un.ITBL3CreateCall _PATH_TO_IMAGE
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Call un.ITBL3Create
  !verbose pop
!macroend

!macro un.ITBL3Create
  !ifndef un.ITBL3Create
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro ITBL3Create

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Sets the percentage completed on the taskbar process icon progress indicator.
 *
 * @param   _COMPLETED
 *          The proportion of the operation that has been completed in relation
 *          to _TOTAL.
 * @param   _TOTAL
 *          The value _COMPLETED will have when the operation has completed.
 *
 * $R8 = _COMPLETED
 * $R9 = _TOTAL
 */
!macro ITBL3SetProgressValueCall _COMPLETED _TOTAL
  Push ${_COMPLETED}
  Push ${_TOTAL}
  ${CallArtificialFunction} ITBL3SetProgressValue_
!macroend

!define ITBL3SetProgressValue "!insertmacro ITBL3SetProgressValueCall"
!define un.ITBL3SetProgressValue "!insertmacro ITBL3SetProgressValueCall"

!macro ITBL3SetProgressValue_
  Exch $R9
  Exch 1
  Exch $R8
  ${If} $ITaskbarList3 <> 0
    System::Call "${ITaskbarList3->SetProgressValue}(i$HWNDPARENT, l$R8, l$R9)"
  ${EndIf}
  Exch $R8
  Exch 1
  Exch $R9
!macroend

; Normal state / no progress bar
!define TBPF_NOPROGRESS       0x00000000
; Marquee style progress bar
!define TBPF_INDETERMINATE    0x00000001
; Standard progress bar
!define TBPF_NORMAL           0x00000002
; Red taskbar button to indicate an error occurred
!define TBPF_ERROR            0x00000004
; Yellow taskbar button to indicate user attention (input) is required to
; resume progress
!define TBPF_PAUSED           0x00000008

/**
 * Sets the state on the taskbar process icon progress indicator.
 *
 * @param   _STATE
 *          The state to set on the taskbar icon progress indicator. Only one of
 *          the states defined above should be specified.
 *
 * $R9 = _STATE
 */
!macro ITBL3SetProgressStateCall _STATE
  Push ${_STATE}
  ${CallArtificialFunction} ITBL3SetProgressState_
!macroend

!define ITBL3SetProgressState "!insertmacro ITBL3SetProgressStateCall"
!define un.ITBL3SetProgressState "!insertmacro ITBL3SetProgressStateCall"

!macro ITBL3SetProgressState_
  Exch $R9
  ${If} $ITaskbarList3 <> 0
    System::Call "${ITaskbarList3->SetProgressState}(i$HWNDPARENT, i$R9)"
  ${EndIf}
  Exch $R9
!macroend

################################################################################
# Helpers for the new user interface

!ifndef MAXDWORD
  !define MAXDWORD 0xffffffff
!endif

!ifndef DT_WORDBREAK
  !define DT_WORDBREAK 0x0010
!endif
!ifndef DT_SINGLELINE
  !define DT_SINGLELINE 0x0020
!endif
!ifndef DT_NOCLIP
  !define DT_NOCLIP 0x0100
!endif
!ifndef DT_CALCRECT
  !define DT_CALCRECT 0x0400
!endif
!ifndef DT_EDITCONTROL
  !define DT_EDITCONTROL 0x2000
!endif
!ifndef DT_RTLREADING
  !define DT_RTLREADING 0x00020000
!endif
!ifndef DT_NOFULLWIDTHCHARBREAK
  !define DT_NOFULLWIDTHCHARBREAK 0x00080000
!endif

!define /ifndef GWL_STYLE -16
!define /ifndef GWL_EXSTYLE -20

!ifndef WS_EX_NOINHERITLAYOUT
  !define WS_EX_NOINHERITLAYOUT 0x00100000
!endif

!ifndef PBS_MARQUEE
  !define PBS_MARQUEE 0x08
!endif

!ifndef PBM_SETRANGE32
  !define PBM_SETRANGE32 0x406
!endif
!ifndef PBM_GETRANGE
  !define PBM_GETRANGE 0x407
!endif

!ifndef SHACF_FILESYSTEM
  !define SHACF_FILESYSTEM 1
!endif

!define MOZ_LOADTRANSPARENT ${LR_LOADFROMFILE}|${LR_LOADTRANSPARENT}|${LR_LOADMAP3DCOLORS}

; Extend nsDialogs.nsh to support creating centered labels if it is already
; included
!ifmacrodef __NSD_DefineControl
!insertmacro __NSD_DefineControl LabelCenter
!define __NSD_LabelCenter_CLASS STATIC
!define __NSD_LabelCenter_STYLE ${DEFAULT_STYLES}|${SS_NOTIFY}|${SS_CENTER}
!define __NSD_LabelCenter_EXSTYLE ${WS_EX_TRANSPARENT}
!endif

/**
 * Draws an image file (BMP, GIF, or JPG) onto a bitmap control, with scaling.
 * Adapted from https://stackoverflow.com/a/13405711/1508094
 *
 * @param CONTROL bitmap control created by NSD_CreateBitmap
 * @param IMAGE path to image file to draw to the bitmap
 * @param HANDLE output bitmap handle which must be freed via NSD_FreeImage
 *               after nsDialogs::Show has been called
 */
!macro __SetStretchedImageOLE CONTROL IMAGE HANDLE
  !ifndef IID_IPicture
    !define IID_IPicture {7BF80980-BF32-101A-8BBB-00AA00300CAB}
  !endif
  !ifndef SRCCOPY
    !define SRCCOPY 0xCC0020
  !endif
  !ifndef HALFTONE
    !define HALFTONE 4
  !endif
  !ifndef IMAGE_BITMAP
    !define IMAGE_BITMAP 0
  !endif

  Push $0 ; HANDLE
  Push $1 ; memory DC
  Push $2 ; IPicture created from IMAGE
  Push $3 ; HBITMAP obtained from $2
  Push $4 ; BITMAPINFO obtained from $3
  Push $5 ; width of IMAGE
  Push $6 ; height of IMAGE
  Push $7 ; width of CONTROL
  Push $8 ; height of CONTROL
  Push $R0 ; CONTROL

  StrCpy $R0 ${CONTROL} ; in case ${CONTROL} is $0
  StrCpy $7 ""
  StrCpy $8 ""

  System::Call '*(i, i, i, i) i.s'
  Pop $0

  ${If} $0 <> 0
    System::Call 'user32::GetClientRect(i R0, i r0)'
    System::Call '*$0(i, i, i .s, i .s)'
    System::Free $0
    Pop $7
    Pop $8
  ${EndIf}

  ${If} $7 > 0
  ${AndIf} $8 > 0
    System::Call 'oleaut32::OleLoadPicturePath(w"${IMAGE}",i0,i0,i0,g"${IID_IPicture}",*i.r2)i.r1'
    ${If} $1 = 0
      System::Call 'user32::GetDC(i0)i.s'
      System::Call 'gdi32::CreateCompatibleDC(iss)i.r1'
      System::Call 'gdi32::CreateCompatibleBitmap(iss,ir7,ir8)i.r0'
      System::Call 'user32::ReleaseDC(i0,is)'
      System::Call '$2->3(*i.r3)i.r4' ; IPicture->get_Handle
      ${If} $4 = 0
        System::Call 'gdi32::SetStretchBltMode(ir1,i${HALFTONE})'
        System::Call '*(&i40,&i1024)i.r4' ; BITMAP / BITMAPINFO
        System::Call 'gdi32::GetObject(ir3,i24,ir4)'
        System::Call 'gdi32::SelectObject(ir1,ir0)i.s'
        System::Call '*$4(i40,i.r5,i.r6,i0,i,i.s)' ; Grab size and bits-ptr AND init as BITMAPINFOHEADER
        System::Call 'gdi32::GetDIBits(ir1,ir3,i0,i0,i0,ir4,i0)' ; init BITMAPINFOHEADER
        System::Call 'gdi32::GetDIBits(ir1,ir3,i0,i0,i0,ir4,i0)' ; init BITMAPINFO
        System::Call 'gdi32::StretchDIBits(ir1,i0,i0,ir7,ir8,i0,i0,ir5,ir6,is,ir4,i0,i${SRCCOPY})'
        System::Call 'gdi32::SelectObject(ir1,is)'
        System::Free $4
        SendMessage $R0 ${STM_SETIMAGE} ${IMAGE_BITMAP} $0
      ${EndIf}
      System::Call 'gdi32::DeleteDC(ir1)'
      System::Call '$2->2()' ; IPicture->release()
    ${EndIf}
  ${EndIf}

  Pop $R0
  Pop $8
  Pop $7
  Pop $6
  Pop $5
  Pop $4
  Pop $3
  Pop $2
  Pop $1
  Exch $0
  Pop ${HANDLE}
!macroend
!define SetStretchedImageOLE "!insertmacro __SetStretchedImageOLE"

/**
 * Display a task dialog box with custom strings and button labels.
 *
 * The task dialog is highly customizable. The specific style being used here
 * is similar to a MessageBox except that the button text is customizable.
 * MessageBox-style buttons are used instead of command link buttons; this can
 * be made configurable if command buttons are needed.
 *
 * See https://msdn.microsoft.com/en-us/library/windows/desktop/bb760544.aspx
 * for the TaskDialogIndirect function's documentation, and links to definitions
 * of the TASKDIALOGCONFIG and TASKDIALOG_BUTTON structures it uses.
 *
 * @param INSTRUCTION  Dialog header string; use empty string if unneeded
 * @param CONTENT      Secondary message string; use empty string if unneeded
 * @param BUTTON1      Text for the first button, the one selected by default
 * @param BUTTON2      Text for the second button
 *
 * @return One of the following values will be left on the stack:
 *         1001 if the first button was clicked
 *         1002 if the second button was clicked
 *         2 (IDCANCEL) if the dialog was closed
 *         0 on error
 */
!macro _ShowTaskDialog INSTRUCTION CONTENT BUTTON1 BUTTON2
  !ifndef SIZEOF_TASKDIALOGCONFIG_32BIT
    !define SIZEOF_TASKDIALOGCONFIG_32BIT 96
  !endif
  !ifndef TDF_ALLOW_DIALOG_CANCELLATION
    !define TDF_ALLOW_DIALOG_CANCELLATION 0x0008
  !endif
  !ifndef TDF_RTL_LAYOUT
    !define TDF_RTL_LAYOUT 0x02000
  !endif
  !ifndef TD_WARNING_ICON
    !define TD_WARNING_ICON 0x0FFFF
  !endif

  Push $0 ; return value
  Push $1 ; TASKDIALOGCONFIG struct
  Push $2 ; TASKDIALOG_BUTTON array
  Push $3 ; dwFlags member of the TASKDIALOGCONFIG

  StrCpy $3 ${TDF_ALLOW_DIALOG_CANCELLATION}
  !ifdef ${AB_CD}_rtl
    IntOp $3 $3 | ${TDF_RTL_LAYOUT}
  !endif

  ; Build an array of two TASKDIALOG_BUTTON structs
  System::Call "*(i 1001, \
                  w '${BUTTON1}', \
                  i 1002, \
                  w '${BUTTON2}' \
                  ) p.r2"
  ; Build a TASKDIALOGCONFIG struct
  System::Call "*(i ${SIZEOF_TASKDIALOGCONFIG_32BIT}, \
                  p $HWNDPARENT, \
                  p 0, \
                  i $3, \
                  i 0, \
                  w '$(INSTALLER_WIN_CAPTION)', \
                  p ${TD_WARNING_ICON}, \
                  w '${INSTRUCTION}', \
                  w '${CONTENT}', \
                  i 2, \
                  p r2, \
                  i 1001, \
                  i 0, \
                  p 0, \
                  i 0, \
                  p 0, \
                  p 0, \
                  p 0, \
                  p 0, \
                  p 0, \
                  p 0, \
                  p 0, \
                  p 0, \
                  i 0 \
                  ) p.r1"
  System::Call "comctl32::TaskDialogIndirect(p r1, *i 0 r0, p 0, p 0)"
  System::Free $1
  System::Free $2

  Pop $3
  Pop $2
  Pop $1
  Exch $0
!macroend
!define ShowTaskDialog "!insertmacro _ShowTaskDialog"

/**
 * Removes a single style from a control.
 *
 * _HANDLE the handle of the control
 * _STYLE  the style to remove
 */
!macro _RemoveStyle _HANDLE _STYLE
  Push $0

  System::Call 'user32::GetWindowLongW(i ${_HANDLE}, i ${GWL_STYLE}) i .r0'
  IntOp $0 $0 | ${_STYLE}
  IntOp $0 $0 - ${_STYLE}
  System::Call 'user32::SetWindowLongW(i ${_HANDLE}, i ${GWL_STYLE}, i r0)'

  Pop $0
!macroend
!define RemoveStyle "!insertmacro _RemoveStyle"

/**
 * Adds a single extended style to a control.
 *
 * _HANDLE  the handle of the control
 * _EXSTYLE the extended style to add
 */
!macro _AddExStyle _HANDLE _EXSTYLE
  Push $0

  System::Call 'user32::GetWindowLongW(i ${_HANDLE}, i ${GWL_EXSTYLE}) i .r0'
  IntOp $0 $0 | ${_EXSTYLE}
  System::Call 'user32::SetWindowLongW(i ${_HANDLE}, i ${GWL_EXSTYLE}, i r0)'

  Pop $0
!macroend
!define AddExStyle "!insertmacro _AddExStyle"

/**
 * Removes a single extended style from a control.
 *
 * _HANDLE  the handle of the control
 * _EXSTYLE the extended style to remove
 */
!macro _RemoveExStyle _HANDLE _EXSTYLE
  Push $0

  System::Call 'user32::GetWindowLongW(i ${_HANDLE}, i ${GWL_EXSTYLE}) i .r0'
  IntOp $0 $0 | ${_EXSTYLE}
  IntOp $0 $0 - ${_EXSTYLE}
  System::Call 'user32::SetWindowLongW(i ${_HANDLE}, i ${GWL_EXSTYLE}, i r0)'

  Pop $0
!macroend
!define RemoveExStyle "!insertmacro _RemoveExStyle"

/**
 * Set the necessary styles to configure the given window as right-to-left
 *
 * _HANDLE the handle of the control to configure
 */
!macro _MakeWindowRTL _HANDLE
  !define /ifndef WS_EX_RIGHT 0x00001000
  !define /ifndef WS_EX_LEFT 0x00000000
  !define /ifndef WS_EX_RTLREADING 0x00002000
  !define /ifndef WS_EX_LTRREADING 0x00000000
  !define /ifndef WS_EX_LAYOUTRTL 0x00400000

  ${AddExStyle} ${_HANDLE} ${WS_EX_LAYOUTRTL}
  ${RemoveExStyle} ${_HANDLE} ${WS_EX_RTLREADING}
  ${RemoveExStyle} ${_HANDLE} ${WS_EX_RIGHT}
  ${AddExStyle} ${_HANDLE} ${WS_EX_LEFT}|${WS_EX_LTRREADING}
!macroend
!define MakeWindowRTL "!insertmacro _MakeWindowRTL"

/**
 * Gets the extent of the specified text in pixels for sizing a control.
 *
 * _TEXT       the text to get the text extent for
 * _FONT       the font to use when getting the text extent
 * _RES_WIDTH  return value - control width for the text
 * _RES_HEIGHT return value - control height for the text
 */
!macro GetTextExtentCall _TEXT _FONT _RES_WIDTH _RES_HEIGHT
  Push "${_TEXT}"
  Push "${_FONT}"
  ${CallArtificialFunction} GetTextExtent_
  Pop ${_RES_WIDTH}
  Pop ${_RES_HEIGHT}
!macroend

!define GetTextExtent "!insertmacro GetTextExtentCall"
!define un.GetTextExtent "!insertmacro GetTextExtentCall"

!macro GetTextExtent_
  Exch $0 ; font
  Exch 1
  Exch $1 ; text
  Push $2
  Push $3
  Push $4
  Push $5
  Push $6
  Push $7

  ; Reuse the existing NSIS control which is used for BrandingText instead of
  ; creating a new control.
  GetDlgItem $2 $HWNDPARENT 1028

  System::Call 'user32::GetDC(i r2) i .r3'
  System::Call 'gdi32::SelectObject(i r3, i r0)'

  StrLen $4 "$1"

  System::Call '*(i, i) i .r5'
  System::Call 'gdi32::GetTextExtentPoint32W(i r3, t$\"$1$\", i r4, i r5)'
  System::Call '*$5(i .r6, i .r7)'
  System::Free $5

  System::Call 'user32::ReleaseDC(i r2, i r3)'

  StrCpy $1 $7
  StrCpy $0 $6

  Pop $7
  Pop $6
  Pop $5
  Pop $4
  Pop $3
  Pop $2
  Exch $1 ; return height
  Exch 1
  Exch $0 ; return width
!macroend

/**
 * Gets the width and the height of a control in pixels.
 *
 * _HANDLE     the handle of the control
 * _RES_WIDTH  return value - control width for the text
 * _RES_HEIGHT return value - control height for the text
 */
!macro GetDlgItemWidthHeightCall _HANDLE _RES_WIDTH _RES_HEIGHT
  Push "${_HANDLE}"
  ${CallArtificialFunction} GetDlgItemWidthHeight_
  Pop ${_RES_WIDTH}
  Pop ${_RES_HEIGHT}
!macroend

!define GetDlgItemWidthHeight "!insertmacro GetDlgItemWidthHeightCall"
!define un.GetDlgItemWidthHeight "!insertmacro GetDlgItemWidthHeightCall"

!macro GetDlgItemWidthHeight_
  Exch $0 ; handle for the control
  Push $1
  Push $2

  System::Call '*(i, i, i, i) i .r2'
  ; The left and top values will always be 0 so the right and bottom values
  ; will be the width and height.
  System::Call 'user32::GetClientRect(i r0, i r2)'
  System::Call '*$2(i, i, i .r0, i .r1)'
  System::Free $2

  Pop $2
  Exch $1 ; return height
  Exch 1
  Exch $0 ; return width
!macroend

/**
 * Gets the number of pixels from the beginning of the dialog to the end of a
 * control in a RTL friendly manner.
 *
 * _HANDLE the handle of the control
 * _RES_PX return value - pixels from the beginning of the dialog to the end of
 *         the control
 */
!macro GetDlgItemEndPXCall _HANDLE _RES_PX
  Push "${_HANDLE}"
  ${CallArtificialFunction} GetDlgItemEndPX_
  Pop ${_RES_PX}
!macroend

!define GetDlgItemEndPX "!insertmacro GetDlgItemEndPXCall"
!define un.GetDlgItemEndPX "!insertmacro GetDlgItemEndPXCall"

!macro GetDlgItemEndPX_
  Exch $0 ; handle of the control
  Push $1
  Push $2

  ; #32770 is the dialog class
  FindWindow $1 "#32770" "" $HWNDPARENT
  System::Call '*(i, i, i, i) i .r2'
  System::Call 'user32::GetWindowRect(i r0, i r2)'
  System::Call 'user32::MapWindowPoints(i 0, i r1,i r2, i 2)'
  System::Call '*$2(i, i, i .r0, i)'
  System::Free $2

  Pop $2
  Pop $1
  Exch $0 ; pixels from the beginning of the dialog to the end of the control
!macroend

/**
 * Gets the number of pixels from the top of a dialog to the bottom of a control
 *
 * _CONTROL the handle of the control
 * _RES_PX  return value - pixels from the top of the dialog to the bottom
 *          of the control
 */
!macro GetDlgItemBottomPXCall _CONTROL _RES_PX
  Push "${_CONTROL}"
  ${CallArtificialFunction} GetDlgItemBottomPX_
  Pop ${_RES_PX}
!macroend

!define GetDlgItemBottomPX "!insertmacro GetDlgItemBottomPXCall"
!define un.GetDlgItemBottomPX "!insertmacro GetDlgItemBottomPXCall"

!macro GetDlgItemBottomPX_
  Exch $0 ; handle of the control
  Push $1
  Push $2

  ; #32770 is the dialog class
  FindWindow $1 "#32770" "" $HWNDPARENT
  System::Call '*(i, i, i, i) i .r2'
  System::Call 'user32::GetWindowRect(i r0, i r2)'
  System::Call 'user32::MapWindowPoints(i 0, i r1, i r2, i 2)'
  System::Call '*$2(i, i, i, i .r0)'
  System::Free $2

  Pop $2
  Pop $1
  Exch $0 ; pixels from the top of the dialog to the bottom of the control
!macroend

/**
 * Gets the width and height for sizing a control that has the specified text.
 * The control's height and width will be dynamically determined for the maximum
 * width specified.
 *
 * _TEXT       the text
 * _FONT       the font to use when getting the width and height
 * _MAX_WIDTH  the maximum width for the control in pixels
 * _RES_WIDTH  return value - control width for the text in pixels.
 *             This might be larger than _MAX_WIDTH if that constraint couldn't
 *             be satisfied, e.g. a single word that couldn't be broken up is
 *             longer than _MAX_WIDTH by itself.
 * _RES_HEIGHT return value - control height for the text in pixels
 */
!macro GetTextWidthHeight

  !ifndef ${_MOZFUNC_UN}GetTextWidthHeight
    !define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
    !insertmacro ${_MOZFUNC_UN_TMP}WordFind
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
    !undef _MOZFUNC_UN_TMP

    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !define ${_MOZFUNC_UN}GetTextWidthHeight "!insertmacro ${_MOZFUNC_UN}GetTextWidthHeightCall"

    Function ${_MOZFUNC_UN}GetTextWidthHeight
      ; Stack contents after each instruction (top of the stack on the left):
      ;          _MAX_WIDTH _FONT _TEXT
      Exch $0  ; $0 _FONT _TEXT
      Exch 1   ; _FONT $0 _TEXT
      Exch $1  ; $1 $0 _TEXT
      Exch 2   ; _TEXT $0 $1
      Exch $2  ; $2 $0 $1
      ; That's all the parameters, now save our scratch registers.
      Push $3  ; handle to a temporary control for drawing the text into
      Push $4  ; DC handle
      Push $5  ; string length of the text argument
      Push $6  ; RECT struct to call DrawText with
      Push $7  ; width returned from DrawText
      Push $8  ; height returned from DrawText
      Push $9  ; flags to pass to DrawText

      StrCpy $9 "${DT_NOCLIP}|${DT_CALCRECT}|${DT_WORDBREAK}"
      !ifdef ${AB_CD}_rtl
        StrCpy $9 "$9|${DT_RTLREADING}"
      !endif

      ; Reuse the existing NSIS control which is used for BrandingText instead
      ; of creating a new control.
      GetDlgItem $3 $HWNDPARENT 1028

      System::Call 'user32::GetDC(i r3) i .r4'
      System::Call 'gdi32::SelectObject(i r4, i r1)'

      StrLen $5 "$2" ; text length
      System::Call '*(i, i, i r0, i) i .r6'

      System::Call 'user32::DrawTextW(i r4, t $\"$2$\", i r5, i r6, i r9)'
      System::Call '*$6(i, i, i .r7, i .r8)'
      System::Free $6

      System::Call 'user32::ReleaseDC(i r3, i r4)'

      StrCpy $1 $8
      StrCpy $0 $7

      ; Restore the values that were in our scratch registers.
      Pop $9
      Pop $8
      Pop $7
      Pop $6
      Pop $5
      Pop $4
      Pop $3
      ; Restore our parameter registers and return our results.
      ; Stack contents after each instruction (top of the stack on the left):
      ;         $2 $0 $1
      Pop $2  ; $0 $1
      Exch 1  ; $1 $0
      Exch $1 ; _RES_HEIGHT $0
      Exch 1  ; $0 _RES_HEIGHT
      Exch $0 ; _RES_WIDTH _RES_HEIGHT
    FunctionEnd

    !verbose pop
  !endif
!macroend

!macro GetTextWidthHeightCall _TEXT _FONT _MAX_WIDTH _RES_WIDTH _RES_HEIGHT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_TEXT}"
  Push "${_FONT}"
  Push "${_MAX_WIDTH}"
  Call GetTextWidthHeight
  Pop ${_RES_WIDTH}
  Pop ${_RES_HEIGHT}
  !verbose pop
!macroend

!macro un.GetTextWidthHeightCall _TEXT _FONT _MAX_WIDTH _RES_WIDTH _RES_HEIGHT
  !verbose push
  !verbose ${_MOZFUNC_VERBOSE}
  Push "${_TEXT}"
  Push "${_FONT}"
  Push "${_MAX_WIDTH}"
  Call un.GetTextWidthHeight
  Pop ${_RES_WIDTH}
  Pop ${_RES_HEIGHT}
  !verbose pop
!macroend

!macro un.GetTextWidthHeight
  !ifndef un.GetTextWidthHeight
    !verbose push
    !verbose ${_MOZFUNC_VERBOSE}
    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN "un."

    !insertmacro GetTextWidthHeight

    !undef _MOZFUNC_UN
    !define _MOZFUNC_UN
    !verbose pop
  !endif
!macroend

/**
 * Convert a number of dialog units to a number of pixels.
 *
 * _DU    Number of dialog units to convert
 * _AXIS  Which axis you want to convert a value along, X or Y
 * _RV    Register or variable to return the number of pixels in
 */
!macro _DialogUnitsToPixels _DU _AXIS _RV
  Push $0
  Push $1

  ; The dialog units value might be a string ending with a 'u',
  ; so convert it to a number.
  IntOp $0 "${_DU}" + 0

  !if ${_AXIS} == 'Y'
    System::Call '*(i 0, i 0, i 0, i r0) i .r1'
    System::Call 'user32::MapDialogRect(p $HWNDPARENT, p r1)'
    System::Call '*$1(i, i, i, i . r0)'
  !else if ${_AXIS} == 'X'
    System::Call '*(i 0, i 0, i r0, i 0) i .r1'
    System::Call 'user32::MapDialogRect(p $HWNDPARENT, p r1)'
    System::Call '*$1(i, i, i . r0, i)'
  !else
    !error "Invalid axis ${_AXIS} passed to DialogUnitsToPixels; please use X or Y"
  !endif
  System::Free $1

  Pop $1
  Exch $0
  Pop ${_RV}
!macroend
!define DialogUnitsToPixels "!insertmacro _DialogUnitsToPixels"

/**
 * Convert a given left coordinate for a dialog control to flip the control to
 * the other side of the dialog if we're using an RTL locale.
 *
 * _LEFT_DU   Number of dialog units to convert
 * _WIDTH     Width of the control in either pixels or DU's
 *            If the string has a 'u' on the end, it will be interpreted as
 *            dialog units, otherwise it will be interpreted as pixels.
 * _RV        Register or variable to return converted coordinate, in pixels
 */
!macro _ConvertLeftCoordForRTLCall _LEFT_DU _WIDTH _RV
  Push "${_LEFT_DU}"
  Push "${_WIDTH}"
  ${CallArtificialFunction} _ConvertLeftCoordForRTL
  Pop ${_RV}
!macroend

!define ConvertLeftCoordForRTL "!insertmacro _ConvertLeftCoordForRTLCall"
!define un.ConvertLeftCoordForRTL "!insertmacro _ConvertLeftCoordForRTLCall"

!macro _ConvertLeftCoordForRTL
  ; Stack contents after each instruction (top of the stack on the left):
  ;         _WIDTH _LEFT_DU
  Exch $0 ; $0 _LEFT_DU
  Exch 1  ; _LEFT_DU $0
  Exch $1 ; $1 $0
  ; That's all the parameters, now save our scratch registers.
  Push $2 ; width of the entire dialog, in pixels
  Push $3 ; _LEFT_DU converted to pixels
  Push $4 ; temp string search result

  !ifndef ${AB_CD}_rtl
    StrCpy $0 "$1"
  !else
    ${GetDlgItemWidthHeight} $HWNDPARENT $2 $3
    ${DialogUnitsToPixels} $1 X $3

    ClearErrors
    ${${_MOZFUNC_UN}WordFind} "$0" "u" "E+1{" $4
    ${IfNot} ${Errors}
      ${DialogUnitsToPixels} $4 X $0
    ${EndIf}

    IntOp $1 $2 - $3
    IntOp $1 $1 - $0
    StrCpy $0 $1
  !endif

  ; Restore the values that were in our scratch registers.
  Pop $4
  Pop $3
  Pop $2
  ; Restore our parameter registers and return our result.
  ; Stack contents after each instruction (top of the stack on the left):
  ;         $1 $0
  Pop $1  ; $0
  Exch $0 ; _RV
!macroend

/**
 * Gets the elapsed time in seconds between two values in milliseconds stored as
 * an int64. The caller will typically get the millisecond values using
 * GetTickCount with a long return value as follows.
 * System::Call "kernel32::GetTickCount()l .s"
 * Pop $varname
 *
 * _START_TICK_COUNT
 * _FINISH_TICK_COUNT
 * _RES_ELAPSED_SECONDS return value - elapsed time between _START_TICK_COUNT
 *                      and _FINISH_TICK_COUNT in seconds.
 */
!macro GetSecondsElapsedCall _START_TICK_COUNT _FINISH_TICK_COUNT _RES_ELAPSED_SECONDS
  Push "${_START_TICK_COUNT}"
  Push "${_FINISH_TICK_COUNT}"
  ${CallArtificialFunction} GetSecondsElapsed_
  Pop ${_RES_ELAPSED_SECONDS}
!macroend

!define GetSecondsElapsed "!insertmacro GetSecondsElapsedCall"
!define un.GetSecondsElapsed "!insertmacro GetSecondsElapsedCall"

!macro GetSecondsElapsed_
  Exch $0 ; finish tick count
  Exch 1
  Exch $1 ; start tick count

  System::Int64Op $0 - $1
  Pop $0
  ; Discard the top bits of the int64 by bitmasking with MAXDWORD
  System::Int64Op $0 & ${MAXDWORD}
  Pop $0

  ; Convert from milliseconds to seconds
  System::Int64Op $0 / 1000
  Pop $0

  Pop $1
  Exch $0 ; return elapsed seconds
!macroend

/**
 * Create a process to execute a command line. If it is successfully created,
 * wait on it with WaitForInputIdle, to avoid exiting the current process too
 * early (exiting early can cause the created process's windows to be opened in
 * the background).
 *
 * CMDLINE Is the command line to execute, like the argument to Exec
 */
!define ExecAndWaitForInputIdle "!insertmacro ExecAndWaitForInputIdle_"
!define CREATE_DEFAULT_ERROR_MODE 0x04000000
!macro ExecAndWaitForInputIdle_ CMDLINE
  ; derived from https://stackoverflow.com/a/13960786/3444805 by Anders Kjersem
  Push $0
  Push $1
  Push $2

  ; Command line
  StrCpy $0 "${CMDLINE}"

  ; STARTUPINFO
  System::Alloc 68
  Pop $1
  ; fill in STARTUPINFO.cb (first field) with sizeof(STARTUPINFO)
  System::Call "*$1(i 68)"

  ; PROCESS_INFORMATION
  System::Call "*(i, i, i, i) i . r2"

  ; CREATE_DEFAULT_ERROR_MODE follows NSIS myCreateProcess used in Exec
  System::Call "kernel32::CreateProcessW(i 0, t r0, i 0, i 0, i 0, i ${CREATE_DEFAULT_ERROR_MODE}, i 0, i 0, i r1, i r2) i . r0"

  System::Free $1
  ${If} $0 <> 0
    System::Call "*$2(i . r0, i . r1)"
    ; $0: hProcess, $1: hThread
    System::Call "user32::WaitForInputIdle(i $0, i 10000)"
    System::Call "kernel32::CloseHandle(i $0)"
    System::Call "kernel32::CloseHandle(i $1)"
  ${EndIf}
  System::Free $2

  Pop $2
  Pop $1
  Pop $0
!macroend

Function WriteRegQWORD
          ; Stack contents:
          ; VALUE, VALUE_NAME, SUBKEY, ROOTKEY
  Exch $3 ; $3, VALUE_NAME, SUBKEY, ROOTKEY
  Exch 1  ; VALUE_NAME, $3, SUBKEY, ROOTKEY
  Exch $2 ; $2, $3, SUBKEY, ROOTKEY
  Exch 2  ; SUBKEY, $3, $2, ROOTKEY
  Exch $1 ; $1, $3, $2, ROOTKEY
  Exch 3  ; ROOTKEY, $3, $2, $1
  Exch $0 ; $0, $3, $2, $1
  System::Call "advapi32::RegSetKeyValueW(p r0, w r1, w r2, i 11, *l r3, i 8) i.r0"
  ${IfNot} $0 = 0
    SetErrors
  ${EndIf}
  Pop $0
  Pop $3
  Pop $2
  Pop $1
FunctionEnd
!macro WriteRegQWORD ROOTKEY SUBKEY VALUE_NAME VALUE
  ${If} "${ROOTKEY}" == "HKCR"
    Push 0x80000000
  ${ElseIf} "${ROOTKEY}" == "HKCU"
    Push 0x80000001
  ${ElseIf} "${ROOTKEY}" == "HKLM"
    Push 0x80000002
  ${Endif}
  Push "${SUBKEY}"
  Push "${VALUE_NAME}"
  System::Int64Op ${VALUE} + 0 ; The result is pushed on the stack
  Call WriteRegQWORD
!macroend
!define WriteRegQWORD "!insertmacro WriteRegQWORD"

Function ReadRegQWORD
          ; Stack contents:
          ; VALUE_NAME, SUBKEY, ROOTKEY
  Exch $2 ; $2, SUBKEY, ROOTKEY
  Exch 1  ; SUBKEY, $2, ROOTKEY
  Exch $1 ; $1, $2, ROOTKEY
  Exch 2  ; ROOTKEY, $2, $1
  Exch $0 ; $0, $2, $1
  System::Call "advapi32::RegGetValueW(p r0, w r1, w r2, i 0x48, p 0, *l s, *i 8) i.r0"
  ${IfNot} $0 = 0
    SetErrors
  ${EndIf}
          ; VALUE, $0, $2, $1
  Exch 3  ; $1, $0, $2, VALUE
  Pop $1  ; $0, $2, VALUE
  Pop $0  ; $2, VALUE
  Pop $2  ; VALUE
FunctionEnd
!macro ReadRegQWORD DEST ROOTKEY SUBKEY VALUE_NAME
  ${If} "${ROOTKEY}" == "HKCR"
    Push 0x80000000
  ${ElseIf} "${ROOTKEY}" == "HKCU"
    Push 0x80000001
  ${ElseIf} "${ROOTKEY}" == "HKLM"
    Push 0x80000002
  ${Endif}
  Push "${SUBKEY}"
  Push "${VALUE_NAME}"
  Call ReadRegQWORD
  Pop ${DEST}
!macroend
!define ReadRegQWORD "!insertmacro ReadRegQWORD"


[0.172QuellennavigatorsProjekt 2026-06-05]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik