Bug 1369255 - Part 1: Add a prompt in the stub installer for running profile cleanup. r=rstrong
authorMatt Howell <mhowell@mozilla.com>
Wed, 12 Jul 2017 10:29:38 -0700
changeset 373059 165e54eeea49
parent 373058 91622c96ce5a
child 373060 647d1f09980c
push id32288
push userarchaeopteryx@coole-files.de
push dateSat, 05 Aug 2017 09:55:48 +0000
treeherdermozilla-central@933a04a91ce3 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersrstrong
bugs1369255
milestone57.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1369255 - Part 1: Add a prompt in the stub installer for running profile cleanup. r=rstrong MozReview-Commit-ID: CLeVEAQe5qr
browser/installer/windows/nsis/defines.nsi.in
browser/installer/windows/nsis/stub.nsi
browser/locales/en-US/installer/nsisstrings.properties
other-licenses/nsis/Plugins/nsJSON.dll
toolkit/mozapps/installer/windows/nsis/common.nsh
toolkit/mozapps/installer/windows/nsis/makensis.mk
--- a/browser/installer/windows/nsis/defines.nsi.in
+++ b/browser/installer/windows/nsis/defines.nsi.in
@@ -116,19 +116,16 @@ VIAddVersionKey "ProductVersion"  "${App
 # It isn't possible to get the size of the installation prior to downloading
 # so the stub installer uses an estimate. The size is derived from the size of
 # the complete installer, the size of the extracted complete installer, and at
 # least 15 MB additional for working room.
 !define APPROXIMATE_REQUIRED_SPACE_MB "145"
 
 # Control positions in Dialog Units so they are placed correctly with
 # non-default DPI settings
-!define OPTIONS_ITEM_EDGE_DU 90u
-!define OPTIONS_ITEM_WIDTH_DU 356u
-!define OPTIONS_SUBITEM_EDGE_DU 119u
-!define OPTIONS_SUBITEM_WIDTH_DU 327u
+!define PROFILE_CLEANUP_LABEL_TOP_DU 39u
 !define NOW_INSTALLING_TOP_DU 70u
 !define INSTALL_BLURB_TOP_DU 137u
 !define INSTALL_FOOTER_TOP_DU -48u
 !define INSTALL_FOOTER_WIDTH_DU 250u
 !define PROGRESS_BAR_TOP_DU 112u
 !define APPNAME_BMP_EDGE_DU 19u
 !define APPNAME_BMP_TOP_DU 12u
--- a/browser/installer/windows/nsis/stub.nsi
+++ b/browser/installer/windows/nsis/stub.nsi
@@ -31,20 +31,22 @@ Var CheckboxSetAsDefault
 Var CheckboxShortcuts
 Var CheckboxSendPing
 Var CheckboxInstallMaintSvc
 Var DroplistArch
 Var LabelBlurb
 Var BgBitmapImage
 Var HwndBgBitmapControl
 Var CurrentBlurbIdx
+Var CheckboxCleanupProfile
 
 Var FontInstalling
 Var FontBlurb
 Var FontFooter
+Var FontCheckbox
 
 Var CanWriteToInstallDir
 Var HasRequiredSpaceAvailable
 Var IsDownloadFinished
 Var DownloadSizeBytes
 Var HalfOfDownload
 Var DownloadReset
 Var ExistingTopDir
@@ -84,22 +86,25 @@ Var ExistingVersion
 Var ExistingBuildID
 Var DownloadedBytes
 Var DownloadRetryCount
 Var OpenedDownloadPage
 Var DownloadServerIP
 Var PostSigningData
 Var PreviousInstallDir
 Var PreviousInstallArch
+Var ProfileCleanupPromptType
+Var ProfileCleanupHeaderString
+Var ProfileCleanupButtonString
 
 ; Uncomment the following to prevent pinging the metrics server when testing
 ; the stub installer
 ;!define STUB_DEBUG
 
-!define StubURLVersion "v7"
+!define StubURLVersion "v8"
 
 ; Successful install exit code
 !define ERR_SUCCESS 0
 
 /**
  * The following errors prefixed with ERR_DOWNLOAD apply to the download phase.
  */
 ; The download was cancelled by the user
@@ -279,16 +284,17 @@ ChangeUI all "nsisui.exe"
 !else
   LoadLanguageFile "locale.nlf"
 !endif
 
 !include "nsisstrings.nlf"
 
 Caption "$(INSTALLER_WIN_CAPTION)"
 
+Page custom createProfileCleanup
 Page custom createInstall ; Download / Installation page
 
 Function .onInit
   ; Remove the current exe directory from the search order.
   ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
   System::Call 'kernel32::SetDllDirectoryW(w "")'
 
   StrCpy $LANGUAGE 0
@@ -423,16 +429,17 @@ Function .onInit
   StrCpy $OptionsPhaseSeconds "0"
   StrCpy $EndPreInstallPhaseTickCount "0"
   StrCpy $EndInstallPhaseTickCount "0"
   StrCpy $InitialInstallRequirementsCode ""
   StrCpy $IsDownloadFinished ""
   StrCpy $FirefoxLaunchCode "0"
   StrCpy $CheckboxShortcuts "1"
   StrCpy $CheckboxSendPing "1"
+  StrCpy $CheckboxCleanupProfile "0"
 !ifdef MOZ_MAINTENANCE_SERVICE
   ; We can only install the maintenance service if the user is an admin.
   Call IsUserAdmin
   Pop $0
   ${If} "$0" == "true"
     StrCpy $CheckboxInstallMaintSvc "1"
   ${Else}
     StrCpy $CheckboxInstallMaintSvc "0"
@@ -457,16 +464,17 @@ Function .onInit
 
   ${If} $0 == ""
     StrCpy $0 "$(^Font)"
   ${EndIf}
 
   CreateFont $FontInstalling "$0" "28" "400"
   CreateFont $FontBlurb      "$0" "15" "400"
   CreateFont $FontFooter     "$0" "13" "400"
+  CreateFont $FontCheckbox   "$0" "10" "400"
 
   InitPluginsDir
   File /oname=$PLUGINSDIR\bgstub.bmp "bgstub.bmp"
 FunctionEnd
 
 ; .onGUIInit isn't needed except for RTL locales
 !ifdef ${AB_CD}_rtl
 Function .onGUIInit
@@ -493,28 +501,29 @@ Function .onUserAbort
   ${NSD_KillTimer} FinishInstall
   ${NSD_KillTimer} FinishProgressBar
   ${NSD_KillTimer} DisplayDownloadError
   ${NSD_KillTimer} NextBlurb
   ${NSD_KillTimer} ClearBlurb
 
   ${If} "$IsDownloadFinished" != ""
     Call DisplayDownloadError
-    ; Aborting the abort will allow SendPing which is called by
-    ; DisplayDownloadError to hide the installer window and close the installer
-    ; after it sends the metrics ping.
-    Abort
+  ${Else}
+    Call SendPing
   ${EndIf}
+
+  ; Aborting the abort will allow SendPing which is called by
+  ; DisplayDownloadError to hide the installer window and close the installer
+  ; after it sends the metrics ping.
+  Abort
 FunctionEnd
 
 Function SendPing
   HideWindow
-  ; Try to send a ping if a download was attempted
   ${If} $CheckboxSendPing == 1
-  ${AndIf} $IsDownloadFinished != ""
     ; Get the tick count for the completion of all phases.
     System::Call "kernel32::GetTickCount()l .s"
     Pop $EndFinishPhaseTickCount
 
     ; When the value of $IsDownloadFinished is false the download was started
     ; but didn't finish. In this case the tick count stored in
     ; $EndFinishPhaseTickCount is used to determine how long the download was
     ; in progress.
@@ -721,38 +730,206 @@ Function SendPing
                       $\nExisting Build ID = $ExistingBuildID \
                       $\nNew Version = $R5 \
                       $\nNew Build ID = $R6 \
                       $\nDefault Install Dir = $R7 \
                       $\nHas Admin = $R8 \
                       $\nDefault Status = $R2 \
                       $\nSet As Sefault Status = $R3 \
                       $\nDownload Server IP = $DownloadServerIP \
-                      $\nPost-Signing Data = $PostSigningData"
+                      $\nPost-Signing Data = $PostSigningData \
+                      $\nProfile cleanup prompt shown = $ProfileCleanupPromptType \
+                      $\nDid profile cleanup = $CheckboxCleanupProfile"
     ; The following will exit the installer
     SetAutoClose true
     StrCpy $R9 "2"
     Call RelativeGotoPage
 !else
     ${NSD_CreateTimer} OnPing ${DownloadIntervalMS}
-    InetBgDL::Get "${BaseURLStubPing}/${StubURLVersion}${StubURLVersionAppend}/${Channel}/${UpdateChannel}/${AB_CD}/$R0/$R1/$5/$6/$7/$8/$9/$ExitCode/$FirefoxLaunchCode/$DownloadRetryCount/$DownloadedBytes/$DownloadSizeBytes/$IntroPhaseSeconds/$OptionsPhaseSeconds/$0/$1/$DownloadFirstTransferSeconds/$2/$3/$4/$InitialInstallRequirementsCode/$OpenedDownloadPage/$ExistingProfile/$ExistingVersion/$ExistingBuildID/$R5/$R6/$R7/$R8/$R2/$R3/$DownloadServerIP/$PostSigningData" \
+    InetBgDL::Get "${BaseURLStubPing}/${StubURLVersion}${StubURLVersionAppend}/${Channel}/${UpdateChannel}/${AB_CD}/$R0/$R1/$5/$6/$7/$8/$9/$ExitCode/$FirefoxLaunchCode/$DownloadRetryCount/$DownloadedBytes/$DownloadSizeBytes/$IntroPhaseSeconds/$OptionsPhaseSeconds/$0/$1/$DownloadFirstTransferSeconds/$2/$3/$4/$InitialInstallRequirementsCode/$OpenedDownloadPage/$ExistingProfile/$ExistingVersion/$ExistingBuildID/$R5/$R6/$R7/$R8/$R2/$R3/$DownloadServerIP/$PostSigningData/$ProfileCleanupPromptType/$CheckboxCleanupProfile" \
                   "$PLUGINSDIR\_temp" /END
 !endif
   ${Else}
     ${If} "$IsDownloadFinished" == "false"
       ; Cancel the download in progress
       InetBgDL::Get /RESET /END
     ${EndIf}
     ; The following will exit the installer
     SetAutoClose true
     StrCpy $R9 "2"
     Call RelativeGotoPage
   ${EndIf}
 FunctionEnd
 
+Function createProfileCleanup
+  Call ShouldPromptForProfileCleanup
+  ${Select} $ProfileCleanupPromptType
+  ${Case} 0
+    StrCpy $CheckboxCleanupProfile 0
+    Abort ; Skip this page
+  ${Case} 1
+    StrCpy $ProfileCleanupHeaderString $(STUB_CLEANUP_REINSTALL_HEADER)
+    StrCpy $ProfileCleanupButtonString $(STUB_CLEANUP_REINSTALL_BUTTON)
+  ${Case} 2
+    StrCpy $ProfileCleanupHeaderString $(STUB_CLEANUP_PAVEOVER_HEADER)
+    StrCpy $ProfileCleanupButtonString $(STUB_CLEANUP_PAVEOVER_BUTTON)
+  ${EndSelect}
+
+  nsDialogs::Create /NOUNLOAD 1018
+  Pop $Dialog
+
+  SetCtlColors $HWNDPARENT ${FOOTER_CONTROL_TEXT_COLOR_NORMAL} ${FOOTER_BKGRD_COLOR}
+
+  ; Since the text color for controls is set in this Dialog the foreground and
+  ; background colors of the Dialog must also be hardcoded.
+  SetCtlColors $Dialog ${COMMON_TEXT_COLOR_NORMAL} ${COMMON_BKGRD_COLOR}
+
+  FindWindow $7 "#32770" "" $HWNDPARENT
+  ${GetDlgItemWidthHeight} $HWNDPARENT $8 $9
+
+  ; Resize the Dialog to fill the entire window
+  System::Call 'user32::MoveWindow(i$Dialog,i0,i0,i $8,i $9,i0)'
+
+  GetDlgItem $0 $HWNDPARENT 1 ; Install button
+  ShowWindow $0 ${SW_HIDE}
+  EnableWindow $0 0
+
+  GetDlgItem $0 $HWNDPARENT 3 ; Back button
+  ShowWindow $0 ${SW_HIDE}
+  EnableWindow $0 0
+
+  GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
+  ; Hide the Cancel button, but don't disable it (or else it won't be possible
+  ; to close the window)
+  ShowWindow $0 ${SW_HIDE}
+
+  GetDlgItem $0 $HWNDPARENT 10 ; Default browser checkbox
+  ; Hiding and then disabling allows Esc to still exit the installer
+  ShowWindow $0 ${SW_HIDE}
+  EnableWindow $0 0
+
+  GetDlgItem $0 $HWNDPARENT 11 ; Footer text
+  ShowWindow $0 ${SW_HIDE}
+  EnableWindow $0 0
+
+  ${GetDlgItemWidthHeight} $HWNDPARENT $R1 $R2
+  ${GetTextWidthHeight} $ProfileCleanupHeaderString $FontInstalling $R1 $R1 $R2
+  ${NSD_CreateLabelCenter} 0 ${PROFILE_CLEANUP_LABEL_TOP_DU} 100% $R2 \
+    $ProfileCleanupHeaderString
+  Pop $0
+  SendMessage $0 ${WM_SETFONT} $FontInstalling 0
+  SetCtlColors $0 ${INSTALL_BLURB_TEXT_COLOR} transparent
+
+  ${GetDlgItemBottomDU} $Dialog $0 $1
+  IntOp $1 $1 + 10 ; add a bit of padding between the header and the button
+  ${GetTextExtent} $ProfileCleanupButtonString $FontFooter $R1 $R2
+  ; Add some padding to both dimensions of the button.
+  IntOp $R1 $R1 + 100
+  IntOp $R2 $R2 + 10
+  ; Now that we know the size and the Y coordinate for the button, we can find
+  ; the correct X coordinate to get it properly centered.
+  ${GetDlgItemWidthHeight} $HWNDPARENT $R3 $R4
+  IntOp $R5 $R1 / 2
+  IntOp $R3 $R3 / 2
+  IntOp $R3 $R3 - $R5
+  ; We need a custom button because the default ones get drawn underneath the
+  ; background image we're about to insert.
+  ${NSD_CreateButton} $R3 $1 $R1 $R2 $ProfileCleanupButtonString
+  Pop $0
+  SendMessage $0 ${WM_SETFONT} $FontFooter 0
+  ${NSD_OnClick} $0 gotoInstallPage
+  ${NSD_SetFocus} $0
+
+  ; For the checkbox, first we need to know the width of the checkbox itself,
+  ; since it can vary with the display scaling and the theme.
+  System::Call 'User32::GetSystemMetrics(i 71) i .r1' ; 71 == SM_CXMENUCHECK
+  ; Now get the width of the label test, if it were all on one line.
+  ${GetTextExtent} $(STUB_CLEANUP_CHECKBOX_LABEL) $FontCheckbox $R1 $R2
+  ${GetDlgItemWidthHeight} $HWNDPARENT $R3 $R4
+  ; Add the checkbox width to the text width, then figure out how many lines
+  ; we're going to need in order to display that text in our dialog.
+  IntOp $R1 $R1 + $1
+  IntOp $R1 $R1 + 5
+  StrCpy $R5 $R1
+  StrCpy $R6 $R2
+  IntOp $R3 $R3 - 150 ; leave some padding on the sides of the dialog
+  ${While} $R1 > $R3
+    StrCpy $R5 $R3
+    IntOp $R2 $R2 + $R6
+    IntOp $R1 $R1 - $R3
+  ${EndWhile}
+  ${GetDlgItemBottomDU} $Dialog $0 $1
+  ; Now that we know the size for the checkbox, center it in the dialog.
+  ${GetDlgItemWidthHeight} $HWNDPARENT $R3 $R4
+  IntOp $R6 $R5 / 2
+  IntOp $R3 $R3 / 2
+  IntOp $R3 $R3 - $R6
+  IntOp $1 $1 + 20 ; add a bit of padding between the button and the checkbox
+  ${NSD_CreateCheckbox} $R3 $1 $R5 $R2 $(STUB_CLEANUP_CHECKBOX_LABEL)
+  Pop $CheckboxCleanupProfile
+  SendMessage $CheckboxCleanupProfile ${WM_SETFONT} $FontCheckbox 0
+  ; The uxtheme must be disabled on checkboxes in order to override the system
+  ; colors and have a transparent background.
+  System::Call 'uxtheme::SetWindowTheme(i $CheckboxCleanupProfile, w " ", w " ")'
+  SetCtlColors $CheckboxCleanupProfile ${INSTALL_BLURB_TEXT_COLOR} transparent
+  ; Setting the background color to transparent isn't enough to actually make a
+  ; checkbox background transparent, you also have to set the right style.
+  ${NSD_AddExStyle} $CheckboxCleanupProfile ${WS_EX_TRANSPARENT}
+  ; For some reason, clicking on the checkbox causes its text to be redrawn
+  ; one pixel to the side of where it was, but without clearing away the
+  ; existing text first, so it looks like the weight increases when you click.
+  ; Hack around this by manually hiding and then re-showing the textbox when
+  ; it gets clicked on.
+  ${NSD_OnClick} $CheckboxCleanupProfile RedrawWindow
+  ${NSD_Check} $CheckboxCleanupProfile
+
+  ${GetTextWidthHeight} "$(STUB_BLURB_FOOTER2)" $FontFooter \
+    ${INSTALL_FOOTER_WIDTH_DU} $R1 $R2
+  !ifdef ${AB_CD}_rtl
+    nsDialogs::CreateControl STATIC ${DEFAULT_STYLES}|${SS_NOTIFY} \
+      ${WS_EX_TRANSPARENT} 30u ${INSTALL_FOOTER_TOP_DU} ${INSTALL_FOOTER_WIDTH_DU} \
+       "$R2u" "$(STUB_BLURB_FOOTER2)"
+  !else
+    nsDialogs::CreateControl STATIC ${DEFAULT_STYLES}|${SS_NOTIFY}|${SS_RIGHT} \
+      ${WS_EX_TRANSPARENT} 175u ${INSTALL_FOOTER_TOP_DU} ${INSTALL_FOOTER_WIDTH_DU} \
+      "$R2u" "$(STUB_BLURB_FOOTER2)"
+  !endif
+  Pop $0
+  SendMessage $0 ${WM_SETFONT} $FontFooter 0
+  SetCtlColors $0 ${INSTALL_BLURB_TEXT_COLOR} transparent
+
+  ${NSD_CreateBitmap} 0 0 100% 100% ""
+  Pop $HwndBgBitmapControl
+  ${NSD_SetStretchedImage} $HwndBgBitmapControl $PLUGINSDIR\bgstub.bmp $BgBitmapImage
+  ; transparent bg on control prevents flicker on redraw
+  SetCtlColors $HwndBgBitmapControl ${INSTALL_BLURB_TEXT_COLOR} transparent
+
+  LockWindow off
+  nsDialogs::Show
+
+  ${NSD_FreeImage} $BgBitmapImage
+FunctionEnd
+
+Function RedrawWindow
+  Pop $0
+  ShowWindow $0 ${SW_HIDE}
+  ShowWindow $0 ${SW_SHOW}
+FunctionEnd
+
+Function gotoInstallPage
+  ; Eat the parameter that NSD_OnClick always passes but that we don't need.
+  Pop $0
+
+  ; Save the state of the checkbox before it's destroyed.
+  ${NSD_GetState} $CheckboxCleanupProfile $CheckboxCleanupProfile
+
+  StrCpy $R9 1
+  Call RelativeGotoPage
+FunctionEnd
+
 Function createInstall
   SetShellVarContext all ; Set SHCTX to All Users
   ; If the user doesn't have write access to the installation directory set
   ; the installation directory to a subdirectory of the All Users application
   ; directory and if the user can't write to that location set the installation
   ; directory to a subdirectory of the users local application directory
   ; (e.g. non-roaming).
   Call CanWrite
@@ -828,18 +1005,16 @@ Function createInstall
 
   ${NSD_CreateLabelCenter} 0% ${INSTALL_BLURB_TOP_DU} 100% 60u "$(STUB_BLURB1)"
   Pop $LabelBlurb
   SendMessage $LabelBlurb ${WM_SETFONT} $FontBlurb 0
   SetCtlColors $LabelBlurb ${INSTALL_BLURB_TEXT_COLOR} transparent
 
   StrCpy $CurrentBlurbIdx "0"
 
-  ; In some locales, the footer message may be too long to fit on one line.
-  ; Figure out how much height it needs and give it that much.
   ${GetTextWidthHeight} "$(STUB_BLURB_FOOTER2)" $FontFooter \
     ${INSTALL_FOOTER_WIDTH_DU} $R1 $R2
   !ifdef ${AB_CD}_rtl
     nsDialogs::CreateControl STATIC ${DEFAULT_STYLES}|${SS_NOTIFY} \
       ${WS_EX_TRANSPARENT} 30u ${INSTALL_FOOTER_TOP_DU} ${INSTALL_FOOTER_WIDTH_DU} "$R2u" \
       "$(STUB_BLURB_FOOTER2)"
   !else
     nsDialogs::CreateControl STATIC ${DEFAULT_STYLES}|${SS_NOTIFY}|${SS_RIGHT} \
@@ -861,17 +1036,17 @@ Function createInstall
   ${NSD_SetStretchedImage} $HwndBgBitmapControl $PLUGINSDIR\bgstub.bmp $BgBitmapImage
   ; transparent bg on control prevents flicker on redraw
   SetCtlColors $HwndBgBitmapControl ${INSTALL_BLURB_TEXT_COLOR} transparent
 
   GetDlgItem $0 $HWNDPARENT 1 ; Install button
   EnableWindow $0 0
   ShowWindow $0 ${SW_HIDE}
 
-  GetDlgItem $0 $HWNDPARENT 3 ; Back button used for Options
+  GetDlgItem $0 $HWNDPARENT 3 ; Back button
   EnableWindow $0 0
   ShowWindow $0 ${SW_HIDE}
 
   GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
   ; Focus the Cancel button otherwise it isn't possible to tab to it since it is
   ; the only control that can be tabbed to.
   ${NSD_SetFocus} $0
   ; Kill the Cancel button's focus so pressing enter won't cancel the install.
@@ -1470,27 +1645,36 @@ Function LaunchApp
   StrCpy $FirefoxLaunchCode "2"
 
   ; Set the current working directory to the installation directory
   SetOutPath "$INSTDIR"
   ClearErrors
   ${GetParameters} $0
   ${GetOptions} "$0" "/UAC:" $1
   ${If} ${Errors}
-    Exec "$\"$INSTDIR\${FileMainEXE}$\""
+    ${If} $CheckboxCleanupProfile == 1
+      Exec "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration"
+    ${Else}
+      Exec "$\"$INSTDIR\${FileMainEXE}$\""
+    ${EndIf}
   ${Else}
+    StrCpy $R1 $CheckboxCleanupProfile
     GetFunctionAddress $0 LaunchAppFromElevatedProcess
     UAC::ExecCodeSegment $0
   ${EndIf}
 FunctionEnd
 
 Function LaunchAppFromElevatedProcess
   ; Set the current working directory to the installation directory
   SetOutPath "$INSTDIR"
-  Exec "$\"$INSTDIR\${FileMainEXE}$\""
+  ${If} $R1 == 1
+    Exec "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration"
+  ${Else}
+    Exec "$\"$INSTDIR\${FileMainEXE}$\""
+  ${EndIf}
 FunctionEnd
 
 Function CopyPostSigningData
   ${LineRead} "$EXEDIR\postSigningData" "1" $PostSigningData
   ${If} ${Errors}
     ClearErrors
     StrCpy $PostSigningData "0"
   ${Else}
@@ -1524,10 +1708,168 @@ Function DisplayDownloadError
 
   Call SendPing
 FunctionEnd
 
 Function OpenManualDownloadURL
   ExecShell "open" "${URLManualDownload}${URLManualDownloadAppend}"
 FunctionEnd
 
+Function ShouldPromptForProfileCleanup
+  Call GetLatestReleasedVersion
+
+  ; This will be our return value.
+  StrCpy $ProfileCleanupPromptType 0
+
+  ; Only consider installations of the same architecture we're installing.
+  ${If} $DroplistArch == "$(VERSION_64BIT)"
+    SetRegView 64
+  ${Else}
+    SetRegView 32
+  ${EndIf}
+
+  ; Make sure $APPDATA is the user's AppData and not ProgramData.
+  ; We'll set this back to all at the end of the function.
+  SetShellVarContext current
+
+  ; Check each Profile section in profiles.ini until we find the default profile.
+  StrCpy $R0 ""
+  ${If} ${FileExists} "$APPDATA\Mozilla\Firefox\profiles.ini"
+    StrCpy $0 0
+    ${Do}
+      ClearErrors
+      ; Check if the section exists by reading a value that must be present.
+      ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Path"
+      ${If} ${Errors}
+        ; We've run out of profile sections.
+        ${Break}
+      ${EndIf}
+
+      ClearErrors
+      ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Default"
+      ${IfNot} ${Errors}
+      ${AndIf} $1 == "1"
+        ; We've found the default profile
+        ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Path"
+        ReadINIStr $2 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "IsRelative"
+        ${If} $2 == "1"
+          StrCpy $R0 "$APPDATA\Mozilla\Firefox\$1"
+        ${Else}
+          StrCpy $R0 "$1"
+        ${EndIf}
+        GetFullPathName $R0 $R0
+        ${Break}
+      ${EndIf}
+
+      IntOp $0 $0 + 1
+    ${Loop}
+  ${EndIf}
+
+  ${If} $R0 == ""
+    ; No profile to clean up, so don't show the cleanup prompt.
+    GoTo end
+  ${EndIf}
+
+  ; We have at least one profile present. If we don't have any installations,
+  ; then we need to show the re-install prompt. We'll say there's an
+  ; installation present if HKCR\FirefoxURL* exists and points to a real path.
+  StrCpy $0 0
+  StrCpy $R9 ""
+  ${Do}
+    ClearErrors
+    EnumRegKey $1 HKCR "" $0
+    ${If} ${Errors}
+    ${OrIf} $1 == ""
+      ${Break}
+    ${EndIf}
+    ${WordFind} "$1" "-" "+1{" $2
+    ${If} $2 == "FirefoxURL"
+      ClearErrors
+      ReadRegStr $2 HKCR "$1\DefaultIcon" ""
+      ${IfNot} ${Errors}
+        ${GetPathFromString} $2 $1
+        ${If} ${FileExists} $1
+          StrCpy $R9 $1
+          ${Break}
+        ${EndIf}
+      ${EndIf}
+    ${EndIf}
+    IntOp $0 $0 + 1
+  ${Loop}
+  ${If} $R9 == ""
+    StrCpy $ProfileCleanupPromptType 1
+    GoTo end
+  ${EndIf}
+
+  ; Okay, there's at least one install, let's see if it's for this channel.
+  SetShellVarContext all
+  ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
+  ${If} $0 == "false"
+    SetShellVarContext current
+    ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
+    ${If} $0 == "false"
+      ; Existing installs are not for this channel. Don't show any prompt.
+      GoTo end
+    ${EndIf}
+  ${EndIf}
+
+  ; Find out what version the default profile was last used on.
+  ${If} ${FileExists} "$R0\compatibility.ini"
+    ClearErrors
+    ReadINIStr $0 "$R0\compatibility.ini" "Compatibility" "LastVersion"
+    ${If} ${Errors}
+      GoTo end
+    ${EndIf}
+    ${WordFind} $0 "." "+1{" $0
+
+    ; We don't know what version we're about to install because we haven't
+    ; downloaded it yet. Find out what the latest version released on this
+    ; channel is and assume we'll be installing that one.
+    Call GetLatestReleasedVersion
+    ${If} ${Errors}
+      ; Use this stub installer's version as a fallback when we can't get the
+      ; real current version; this may be behind, but it's better than nothing.
+      StrCpy $1 ${AppVersion}
+    ${EndIf}
+
+    ${WordFind} $1 "." "+1{" $1
+    IntOp $1 $1 - 2
+
+    ${If} $1 > $0
+      ; Default profile was last used more than two versions ago, so we need
+      ; to show the paveover version of the profile cleanup prompt.
+      StrCpy $ProfileCleanupPromptType 2
+    ${EndIf}
+  ${EndIf}
+
+  end:
+  SetRegView lastused
+  SetShellVarContext all
+FunctionEnd
+
+Function GetLatestReleasedVersion
+  ClearErrors
+  nsJSON::Set /tree requestConfig /value \
+    `{"Url": "https://product-details.mozilla.org/1.0/firefox_versions.json", "Async": false}`
+  IfErrors end
+  nsJSON::Set /http requestConfig
+  IfErrors end
+  ${Select} ${Channel}
+  ${Case} "unofficial"
+    StrCpy $1 "FIREFOX_NIGHTLY"
+  ${Case} "nightly"
+    StrCpy $1 "FIREFOX_NIGHTLY"
+  ${Case} "aurora"
+    StrCpy $1 "FIREFOX_AURORA"
+  ${Case} "beta"
+    StrCpy $1 "LATEST_FIREFOX_RELEASED_DEVEL_VERSION"
+  ${Case} "release"
+    StrCpy $1 "LATEST_FIREFOX_VERSION"
+  ${EndSelect}
+  nsJSON::Get "Output" $1 /end
+  IfErrors end
+  Pop $1
+
+  end:
+FunctionEnd
+
 Section
 SectionEnd
--- a/browser/locales/en-US/installer/nsisstrings.properties
+++ b/browser/locales/en-US/installer/nsisstrings.properties
@@ -15,16 +15,24 @@
 # for double ampersand) and prevents the letter following the ampersand from
 # being used as an accesskey.
 
 # You can use \n to create a newline in the string but only when the string
 # from en-US contains a \n.
 
 INSTALLER_WIN_CAPTION=$BrandShortName Installer
 
+# The \n in the next two strings can be moved or deleted as needed to make
+# the string fit in the 3 lines of space available.
+STUB_CLEANUP_PAVEOVER_HEADER=$BrandShortName is already installed.\nLet's update it.
+STUB_CLEANUP_REINSTALL_HEADER=$BrandShortName has been installed before.\nLet's get you a new copy.
+STUB_CLEANUP_PAVEOVER_BUTTON=&Update
+STUB_CLEANUP_REINSTALL_BUTTON=Re-&install
+STUB_CLEANUP_CHECKBOX_LABEL=&Restore default settings and remove old add-ons for optimal performance
+
 STUB_INSTALLING_LABEL=Now installing
 STUB_BLURB1=Fast, responsive online experiences
 STUB_BLURB2=Compatibility with more of your favorite sites
 STUB_BLURB3=Built-in privacy tools for safer browsing
 STUB_BLURB_FOOTER2=Built for people, not for profit
 
 WARN_MIN_SUPPORTED_OSVER_MSG=Sorry, $BrandShortName can't be installed. This version of $BrandShortName requires ${MinSupportedVer} or newer. Please click the OK button for additional information.
 WARN_MIN_SUPPORTED_CPU_MSG=Sorry, $BrandShortName can't be installed. This version of $BrandShortName requires a processor with ${MinSupportedCPU} support. Please click the OK button for additional information.
new file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..36e5263d92f712a3047aa379f32c4d9063da9e8a
GIT binary patch
literal 18944
zc%1Eg3s@A_`S<JvSaoqRf*LOg8e?-Y3JkYpxe5!Ih%T<|Vh{-`WZ57f%!nF6pANLy
zOi1#zX}@05q;1mll7I8qCT(Kcq^PNAzFe%@gckpm<~n3)!at?BDsjHwduDb4HEo~g
z|Noxv*7Z5e%zNJRp7*@xy`I^%?9PWd6UT98G*#ue7dY*c&;8?1KibUeU!2K3H|_Ps
zFBsOmzIcPLp)IkcwfXMW+U<#3Ynz&yg~Tm&iLGK&Vnb76(K>hH_U3JMD`I1(FV$(3
z-*L{9aLYHN!tL1a*GFB$?!OV0gX_KR-!ac(*KeBR*|k1OyMCKJuW#7uqj8t>=5lh}
z8bcI!v5Tt=!v;Cy;u(gS9G8TLIT~#7E%*}`S#TV?H*#FGJ|=wS_Ul4o-T4MKC{7!q
zUA0Rb=6wX$?-qjx0^S9f=_<l&;kZ=5M~XOZ;Xfhk$On8L_&ePzgt}b<t}m?68A_Vq
zJN!umS1Ve#)e5y7w{yBile40wUNKev3JpyY4=u>ZU7;L!>|N2?*1DC`*Ls&e=c=pF
zYprW+-m0ygWWex>ufV%(<i9@ut3DO-u<{x9pu1PNMy{|(iJUw=P-+br`QH5MBYbc5
z5ny;^(c+amtX_}uQ)vFijk2v?Hm|LBIO7B}-y5{_6c_?A7*VsC;g`ETUgd|NDvxxJ
z*}9fs3`Wb<7XQ6oxAL^As?sY~%$O)G<NO>z#{lwoc-?_wtMUNro;TD7?)+8gyf=cs
z*z4|#1~E=~mpZGp0adI&e(39fNp<5~k8|9GVaYV>2@I5-<K4Zst{<RVepMRcgji|F
zAkOTICRGm3=!+&*lr9*BN~t4`6W95(FgiLi=}Nx=&&wjub7f0lrd(<ctToG}R##xH
zRZYmx=i)F;px6?;)^GIZs|lHa;E@5XjPCXR<)HJ-r7#^ETlpK>bD-EPAFbJ3CBI*N
z#G}!JTt6n+`wgmSRYi;P1W@G>8ayT}^)J_<)e(YmI;0P*ZrsQyYTF@~S!oC5S+?iR
z9x0S4*rd=jVY(EG6{4ijbTOu9l_6jVp4G73z_ntxE!e}~fRyZv;|Jt9pg2#C_Al88
zA|>^XLt_4_K!sI}mgfNAB|!Xoud>}p>H%h~1O;||cdz(I|B}j{+tqrz*(*lTT+woc
zHF$?VtE%2{M6k;<fxAu$Ef-cup$_3@DO4>iB{r`oHs=$Y31Cx*11(_&sakZsePM~%
zC(m5%_}#v_{6JwjrhX_E`&$-A!ZgT7f+oMQ5(3rqvD^*i^YW_7s|gz*+k1MWrCy^e
zvQe6bJVhF^z$1?YIzqPRO*CDz-&o^P6MR6zLe>E)g>Dl{q)?5pN(!wIa->kFkO^kR
zDF3aM!DRo9mGyyW(>Wj>Qq0mI*@+W=GcF9B1PYq!Ws~V_xQ8V5h%AnKG#2Ghx7s-e
zy65R}hFtpAehe=S+t@7k)i70IwuYs7y!@CuAdZKJ%5OmA7(WX#ze6+>7dYPBJBogO
z3<_!L>oGe?_ouF3$iM(zK7o-K)h5pIxRu8^Z9Bzj{!LhzQ95h{_ie@6t>fI@lc1-o
zEh(DQa=&H&`Ag!e{kx#G*=~NI$7S@&7whfwh3h=>r}c8Q?U!o}^@j#6^)rr@)CXqv
zr0%J=UnhRBS$0}$s=$Rw;x5UDtB>qIuZoEvb4i@vQ)=9A=fvp!yG)z_bGC-j4(YQ3
z(78}9v(!s_3odOEXO+6jm3NsHfOXS^9|H>C+kIS!l~-GXW_h(azx%itsy^b63rm8q
zNcX!{p&4ca+(i3svluOxm;(i7$<D!I>;`d$eYaHyTBSVBEiWRA-FusqXXpgLwC@JB
z-Kw4nE|g1%*#wW=V)k<`<uS6SqhM=H{L5x(p9SR9c8hw-tDdT=VJu3+H$jSSwZ*FM
zWq+W0yvJHi`&B-%Ukd5#4e)Rteyn@I_TXIHdi~}ae{_}K5^#np<zs!$kxdvBC>w=e
zm)FA}<6G+;6(fO)VYwn?>;5%qJ9<`G>GAoZ7GuCUY<s>}3f1pR5td7#CBhOZv{G0k
zg*FNEq|g>y_e11$EtSR6A)|D}DD@eG9kO#c_=(>d{76!oggQwv3!6$M#UQN5A8`wo
zBD*kiQR{RUjC>aTnN5G@x_X@bzPJJ|4%W2|OM+Gd;!^sT5mzDUXp~$w>Yolpt27-m
z^_xP$1pl;Pmp?YR-#>k5H}gxw=!O0<b`BFqSSy$cRD>KOwg;jajxwR0Fcd4_>+!nq
zfg=ztK1yaVzbkBRSv~6v5g}9T5^myqT`sTZ3Unx3big4W4Ni-A>#(!&bE+8E<FvqJ
zT}m~DmRI7!)&X6ZE@hg;C^_F+eFTad39p!IrPOKVgqwW_Ku+C>{V!q@33GkVg(0OH
zBnqK6hzPxT!UI4Fy56LSXYzP4bHE8rmq(O=ix5I+W4CJSdYXE%9~LO}H0iv-*8LSg
zC^X3}R)O~_)i(m+SfFgY`}ppe<ZWIZhY87L<ECR|HJv-aqnIDy<(+27p}n65YhgUM
z09Gq?S~wBmzuq1rtnsQlEfLmp88)>2Opup4;yGLQ%Mb|V1aoNaTqDdW*CfvLPG0Y{
z@Sw9m?EVW$kcM`#m>)N>J^XPjCoogWG>G@w%i?U^KbwNk<FWF`1JH)?fTiAU6H>jJ
zMP2Uv<ce{mJOvinDD8ql#(LEjEMSe^GXrxtjtR2^JIujZVaCo4`Jr5a7OR{RbYW^S
zizc|~b0$C<K)dL5X;g!;Q!@k;b`N1ND$rsMngVTBuclBnh~BivVeOTM<B?UtHR-eP
zWANJi@!~P3)hze>mjuc}cZy5!WE5vf7ma(Z_4c`9Ouc<W%_RiQLSsN2#Q@AuHVV&>
zNXGxWKId>J?Z-f8vF&-QS2}OmyHGkmy)#}qKcn*+>3nSGbm{yO?9us6aHGL%LERwC
zCe(UI*+^?N{toXnNxjjl9H;ia1oBtNHbk2DAwZ7maa+%y*j|ouNE(j^qvbd50Mit1
z0x^#NVe5I3V+smCRVp8r6pO7ZK=^Pb9rR}h^DEovPo<<J2!)c8CFC-eQv8XP^^Oy*
z7JnyQcP=3^zciP+dd%&<n<ku$Tox)teqb627Sspg+k*}k)y(ZcM_4NjF2l3GH8`Ix
z^}*S6X$o5DA_Sx8vW2iKi|KDcF_;J253Hu#bqS*roDQ?`>dMnM$@8#{gQr%7rE&>r
z!=ie7k^qlqaVdL7VWJ%tx&Pe2#sve?yM~}q{*%Wp#tVhM{iM-GkKgFSO+F<72}_AK
ztkfc%R|Tgk&f|OS&V{zG^}4*}%HC<r-GB=<A(eD(G2a^yEjj{C=k0OE;RdcLI)>#E
z{8(}KfDkiD>#B5po9Ckf(x2bw3~?5Yo02LuV4KP9@W;Ivy-Y5oMO|NSw+QpuqJHpK
zEUMZfht8edxImRYG+@Jx@_F($9$VK1az$-cs3N%n8)(VV*jHQEZRm*1Iy^Bea^Rf^
zYa^QKyYSHC9CcA*M9IGLwuvnT{zRPP;|iuYOE^9fqQP(QS<y3P+~fMVjuE(*E>=D$
z@JhfIuP3ouHK{3>(Awi1#_fnpc_Ru{!*y&1of#{kMEY;nh(@0G{td3esj#egzTbdU
zZe$7*^EG^c^*VxFjIHZ;VXnvXFXMX1$Oa4@qk3So74m<L$>(!~^P-UvGWkX?C-hxL
zC`oc)n)W$<jym0u-Ve-doo$hIwppi)-a^|}Wd<h{dy2^luF7tf&Tgg3R3dl-RI*Hg
zaQ%j`cqb;^Vb&5{SW1jrSIH{Gc-@e+NNMYehn|c%<IG;60pWJ1RdJ4~Dgeff8?|yy
zi1(XeF3OP$S}kN98U&OX5XZFGLTXAFA@Vw*jVwe`O5`_tl^!<Ai(wiILr<{5RA4Dj
z8Yl1{ps~?zY;1)TG6)$`?<HK@+bNK1DIxMZD9R~0hTzjF;|oL!=3oIzpb{&a;8d_w
zE~X^$HhusmV(2L~+piVR1$LQ(EBOI8EzSoGl4CY*BpOzGjYWtFTo>rH25l2m7@Jjk
z`ArpLBm)^ZNA)4aRU4dzD#p8%XSI^?vZRRRIDR8sWo0-Y%IvXrOi4UhPP9bNhY-d%
z4f*e6L#Q69p1k!aLW6lqXgG#EWT8Wh+c%e-qx?H~;C3o3de$2O?Hs9*4@JsXa5)cT
z;uvPM>4?M7xm>PT*nLtckPW8ZfC2h~!0%Es)P!v2=&gYmIhsFdI<m@P*!MQNXUQ|=
z7%aaBVL|iIhU*xU{7J;Wzsb=KL&qn;OOT!MvQ54Y?6~05H)3W`Tct)L#Qib&x~7B+
zyT>}OM}4A6RMQN~nAU1%7t+TD@y{Cbl)Av|VO#Mo$imdCmvU-C5?Da7PyU$ns){yw
zgi3=jI>>q=n7Uv$hs|IfIJkDo+z+w2<7n;=^|^<sSVHjORcX*5p9^%12b`lqqgRV7
zCNIntH~ZNnEUxrR7ga6M^1}LV(nAdrLg7}*EWDMl*aFib*A#t-^OEVRs9Z5-IiPMW
z_`qy~3uqXO)+6ZdDHt8Lz;O)SOWndkuSa?LKUCG*gKg`umb;V}{=$xQ<WR6|f9@x!
zXT-VtI}N`QXYKDa{zjZ3^?n9ePq7IGbF`-{gb~S~L45rX-+(Y5{16?NTWMi*ta3>A
zB`o=f(PcR~LSt*(`YBgD#f-UR<<$#WMp*BLoO&^U5^Im*r<v%SVV+yv5q7#5kF-%k
zSPcX>LiQ_>AsL?$8%a{fJWfa;YOh7Q7P78+7*C10?Y>o%7oRtD?vTzK1&?&zWb0lD
z&{P&a9B0QejD>KasIg$x5RQ%$3zLUDH_liXI*Yx%V$cvdJ~yU)@iYszFPy-|<~;1u
z#_nR%%_C0_H$_Y9kI>yjGr<fn*<b|tc6dOld@TRmAKVL$-*5~wm3wJoLb(5uW+x<&
z<x>gH(8jt;T~kPQG5%8__!r(I6?;)(oT3<?;Pb@Ah%EEX0Vnyq29m;Xgvv7)KX=}*
zUcBnOR()QgX6qitZb5Bl!$Cy*Mp&*N_9kJ6M-G(&;tN!q%VIpaEa_gTQJE*aA<BD<
z4cK&BBxtu;yvpkUd%}vN{DvVVQpzOE@`RN}`H?nOa&Wc>ETk;(g_|UYQ7DoeP?o=8
zCnqMlH!{(rDDG9Zv5CS+9&Mr_%D<;#4$=7!COAlH;bB^VpzS>5@+ylj>MBT5zR?pF
zr7{h0a$I2{DrI5BrA!w*qzmPYi#IQ6E1?B0ouJTnfek2iP0*OeI9v)&*yei<Dw8<*
z?-v*uq$05WLQi<Vm3<ekO5S%R`JXV%rP#IZ_27x^q~Cm%BcW9NpvNO;KnoU29SHNq
z$V)9klt-<wZdB9NQyxTnRM{ilz2aG~dTO(Nu0w}99#Tje^ea*wImza%wg%xX(<6m_
ziM$i}jW}!<<Agjpji&edt>EM>w;Z~nx_ry+3dHEuMwvx61WGJ&U%+02(&{KX9tdah
ziEa-+hFB7(%(cO<F*j-Jx}BXFS*F%%sg#<RSv|f@g|M=KEl^`tzVL~vx)k>(nl6D&
zADuB+Jn*BI$mwCJ9GmIl;!MZOrxJXZrB35MUm=+kE5rOougBLy^p&+NJ4w{5kD+HN
z^62+J!M$%k!+PptV&LS*dM0D*`ZB5NRhZb#4;0E5JwA02;+slg366g+x*bQv)!0C%
z+v7pR9fZvan#5O19q(Nu%+X+tEO({577A4-RdHAq75_D=IH)G<q9tAGR>kkCbkNEo
z++TwzoGMV+6|=%A5=`PL=XoO4ML{sm`$Q7EndfWGKxvYeji*2w2}^ZZd(7#)IyIhl
zxDgNk7E33ufsXiKfp2*ci~qWg<ohxwKa11)Py*ANj78A@YsqUNc?eX;dtAy{7XIQ8
z3*~-9p?LP1=K#=VB6DynaY3ZHyt=VO4~SJ*95RM^GS`<+u7%U{N$Bnq#5BDUI&9^b
zO69Y(!t3=7uHq;I33QxM)L|BFU5f25RPvmrlE>CnMDKwX(dvBkvd^C%=ok!igaRE(
zpkoxqGScVlrI(KZalFrYjHV5Ayh2I-3}AcZoOre=R49yUwb<K4^~cEa<+O#QO&sOB
ziux9T9|D-Nr(V7O7}U|*8JrM98e+XkKPx&#C#?Ns4{ib#O`Ng5MtB))pl?`PrLvHg
zST_tCqW28dCurPHn3FWxy1vbBOt!ACXl_$GFh!xutF)dka+8j{tdRn;#1zs_mEMJd
z*7lh5CLPNUW53qw1O%tNh)g{~Vi{GQ=3OrJRD>2tIfq%G&^qu`q7kR_mO?8i%4M&~
z)&1^rKi6j{Vka)6elAdukE@R>)b_F<TXqfx3Nrf&mUGw;Wm!<)*mTmEjmR2?ArlO?
zpyY#iko=ui6U=(OJ!oJeo2L;|hCb3?TglFTeqeF+X<k)Z%$fnw>7m!RRLA_yM`Y$f
z!k<YVy`OTR6y!j{ZOBc;G+8_$@3aQzM<fvIhEoeEsc}>^TD66~6V##C>Cm&Z=WI4r
z9OcIX;V1Zkk$^ZVr%}r4V!BC&9oD2?CL6eMq1M!oow51zdkT%nG+2QVv`&71M4nE#
zfj@398`-%~t~f1=<M*4B3+^+|T}9_)f7Rv)RGcQ&9Jzch@f#Su0kxj*NnOi+IYfeZ
z2Y=n?`hE9l&;?`C2WI8ltg8=y&-E|pl?IJ7j=H>V-wO=KqFnk9mag<U&yXQuqiH^R
z>t)rPN1lJq6koGa6-Rveh0LF5uGxdFD=pNh9Uy9DXN>UBx{DxET5f*=)MnG!>r9!c
zX^boU8uT^&PS3d^45=f;3DY9`z_M^Wj)orx+2dgNQDKjYzD;^Xsmj%7UZ%J?cEljY
zgHLZ{(bL$X<HwE|^^P$ABO|P1BF(21U)^a|)0K>iq{)$Tb#wJ$v*y^AYwtgqZxaf@
z{#j3;dPKQF(M^C?$hVDd+<0(A*Z148o4&n3_4=N}UypL`Lmf9pVdn^5FCF_Ne3zN@
z85(j8Il}Czf%32LG<>hi*UM-;`ytgc%*hPHVW;~V0cI&D;hbPN4~6G>{6l7zk&s}?
z=Z^=RgTWa7xO^y3HV6VY(g6S)kE)2F2WR>Xl!z%d&27v5sHXmaUFnO-rkKqfO1{)F
z%mqr~10_kqe2|$#r~D?t=GOg9FU-JDk|dWTp|Xwmt&-)mNCUa(lK3edOkih`z<BA!
z3R8h6busoD+4^06lgnj$zGR+DZO0~_Q4_ww<n|unJPI72QlJT8gbCsP4|E}X>jSni
zeDB7McIU8wbWTVlUKhw|n<)5<)45>#a%4^HFE$xO_}+uu{{5V^&uX?69g5{<Sh`%k
zr7YHgJ>Y>u@jW%+f&9qPr%mJ2$E~92XK2$0oQluN)NwSt3kT=n`GpZyNgd;R%U!-*
zY{Vbl*BKtdjR$qCk{m@jmqL4R0!{+ecDi)IAjEf{6f^>^a^=_Wvps|SyHDC4A;w+m
z4y)q+NS}U`KOWxhQEc^0m;U+?b=N3zGM8T^^P|M&K3(4Q_K;Soy<SGg!+OV8ndb}T
zyQaKu6!t-<%KCfC`pZE{8}W$dRH{fEPqM|0zen!rruUh98Y8&wy~uZ+Wt}tLpX9q_
zQE_<)Eo{p<f6*P-2+L_qK=4HT2w3H@mU91WPoNCWq)Sux8Z}`aIgZdCaRV5?;w1z*
zx=db62|WJUjCF0OFdzPcraz`n|1z_Mc&Ti}I590mNX||!JB=_Tn7faQVP^-NM_FKb
zPrrf6YJL)`qn|hoqkMxd#*&*8!-txBk}Fq_Fcp84dANg<+*|pCd%I;g;@+<H8#RUS
zMmV+(f3A;Pesv#Lj{MX;IYIqL`RTwEQQEqi$$s4)rQF263lu`K8K{mM>Y;?aw{UW9
zYRV6``%X^*nf=D#7T@4yFjH`?Z}>8pIhf@OT?UH^F7sK6roy6w*ZAg5fz9#9R0fcQ
zD@D^N<+XLyp(qWm^(edW94!9F33Z&FP^-eVZg!SB9_ScHLjB-7T0&h7pY2gTuxJT2
z<Vg<vW~#<RG2$G1jrE06kUZ$ZAGj6Qkk%C~F7ztmSr`GUhCJan-*KKHR>_B=#OXd7
zGjyI(#gJJnkYjE=A|%s0uO;+nHoYb3rlV}%Uc#f71ilu!qi;+3w$a^7bXQrVS5s2R
zD9&c@oNV0!q@cE2hghB(GKsg^ueWv6J2n87>u6JzxBqknckx5jbh4Od6pl!FCUG&H
zbRYC8uCw}^WnqI?Nwh@jwxAQs7p|p)N4FbQHqk!+p7xclco1SSD7b(Y8{;D>1#MWV
zpb$f`?A^C=G6tS$pio!bn(l$ARmp^=Gg`=3))Z*A&^f5P)&~bk^W_oWT?Ln^;~W-9
zb&%$UE+K|#h0gbuyBOYd<ueq!PtX&4n-1D8C1ZrXK15D>#NJ_rO^*rj_D(aVed*s3
zd5V#I3MRSJOvj25L!`G8X61)Kkm}7amhhplg%hvwRWfs<S(I;lLJWt$ZinncK4)h=
z;AKac7kc4_=p9X3C)$1a1uVy+g}4E@Uh!x>*MnoXPWOwSXt8}H7^U1x-D7lQZ1w8h
zTf^N|mo4d|GoQO8snbmP8I~2Kuh_`!?Y7AkG|{VyxtL?~)r#PM;A5sb>rPa_ysCUD
zimfM8UPNp3J<7E56d+O3n}(px_gqmJV3Xc7dK3@C7v2g+dz1%p5BsOc%8qP+v<PMO
zvx7`)No{9(m4+x%+f%$+5;XEEky2}_E>kxB3uNRo<!Y<v?-uooA`4n@Sy6B6qAw?S
z<+Fi~I1l;Dj&bFhOUyk}2Jk5R2Bx@9U+Ke<l}6{oj|G=ZT024Ff^ohB5l9>$;~u3_
z`xfV6TK&URb63a9Wg#RJE9vlZz;Cz{fzMWS*hQuZ!z@>}{F#J7$GWO89ae^cUNfBm
zGP~<m#Ti&1f4o5E0;QJ8XXXF7@Np|UcQSaCS)Z|-X3ysxXw>^2nA~COI!3PT;le2}
zdTr}_bc#c-_Wfweb28R9pM8yzPDVQDWaPJJbVqI_N506Ug^4yO^boxp@hH<7NZg5`
zt*9u1D_|`L5Nd;)J<7*M)^{iRJm`CqhZB3pqr9zw3-!$kFXtMM@{-nbGX~s<P#5nv
z1d%KKP=n59#U;uXWx>1fFaxm1zvKEMJD|N0vw9~<J-i{B^x3wk?yH<I*}6vA8kaE!
zY~2H-njYoi1??MiwyswR;0-t-FDpB!b19DuXaaMx*CRis2$E^Eb$^$lM~hWiOvi@o
z6h$V|(wnM6M>$yDX5T9lo8cidRG!g#EFR?}_VU%EJjx)#X?UsM)2+Je3-kgi4nuN`
z0Ekv!Kd2~ls2;qTxqao|=zf}C)$!cn8Xw*|UP<d`{!}d?F-7!wnrw=kZ0X+&W#34E
z9EC(b`{MA#QA#hn55Ip4=0tj6f9$0l+}%@uh`h90B$rX1;OAUOx37F}H}MTg{<z!Y
zwLL%2pHvg*u+%%wirafyjJD_JNEdbqE2ImR!p;7S8h=u~<L&nK^^OWl>zX@*v5vld
zJEYzd(otiN^_cDXXvg7>;b09Adv?;;o>eEL(6n~5d?GlvXVv3~uI*9MN8aG{o>jl0
zhqPPYKJ;g^>DAzg8u>q@i?@kU{8%v}uVM^Nmo7}(XS?V4bknN{gtq77Zhd?AFcxGy
z_lKIz@}Vj{Ym{S2yFBnGE97MQW*h}EVRlck*~J#iPN5l&9PJ4hBHv*)@xA$x>N<z|
zIU)CZq@W%o>Xylgy2|ucMoMhyS!*s=6A~wAP7BP`?gGh-+P^;kHJ{N^j{C`Kj{6o`
z7g`hAU1%<}OtfQYTY%Go)`@mM+Sk#ZMEg0~VYCxyXV4V1CBWy<tZ2Vpt<CWct{<bB
zZ|AsqXv@*E(bn9q^%r4W652wvOC=ol5!zd5y=Vu}9xa*RmwwJt&((5`TpL%1`z5Sp
zvA#sKDztmiV!7!IhJ>G^FJX3|E!SyO0g{93GBkl}<wQn_W3VJ{7k4AKbxNbpCScu&
z-c<C{=N4`yM?W1}$tct@3Ps>cK-<9W=2~=`w{vc89k-U<uSZ`yt`!*3h-(wp5bjSz
zb3<@$+_j)r<nHEf;~Idol1t`RVCEH=c?Fxj0DX1nq2KwqK(iHXJ7{s2Jx}JNgmdB<
zt{Km*+!hAQo5Zs=b{*#5!0rpVW;TO1&utM}S>Uf0cf?}@WJ9AH8172&b{p15|8Bz`
zHDjLbxD(lGiOyEQw&89Y=$GPJ%br>p=N0I0h+wS4C;@YbY>k@SroyH^ao}5EyojJy
zs8b2cLttmsB)3*bP6Suls%Ty><MSRS>AN9~20U?0g_|POr}8$rhl5VgUU87_F5n1L
z<}SbtyV<%4ZySS^f}bYbH|jVQpf?$BB_!9N_pSux$uo0$zb4sQ#=%NpG^1}9c+w~n
z>>faN>-gGzo35*2jabRXGypE>eKd=xYZW~=Fbmrbd?7-sPM<;RZ(>|&_CQ?FXEz{i
z`g53P(n!)tqOz0Ou4Zv<%yu-%YcwbK(z6d30{fYm#&4Z2Bf@HD6vOLkV7uBJ*%#_<
z(C;t*gnttli#Cq-q!Z14WxoJ8B@5Ad%XDhLEA*k2-^_6%JnSix<8Hv%B)FiTJ2-A1
z`o4sAKib1+-$HxLNz@FSkuz~-E{bbvyWPER?TT%Uja*S(W1UdPmDCAbx!8<5cU@~k
zZDYe8x)V5WZ9{koaoTVC*zfz6Zf|0+eC@Y@D;nrWdP~y?gaVqo3*j;R`@Zn+5m(e{
z&$L!rv(~?K&z?QowvhDc_lnsQv{H!H8NlKvU_8M_0Q<KI82iN_`R6|xBd~PFn@jVz
z;olFU+|+1oY116lUE0qC+3yCICM7cv00)c_7~?^h<oi8{4_)~h58==0)tVeOOv3Xc
zu58P{>el~X74N>yt!@(PTAS*G!q(cY_f)htdb!_5VC!1yn!MasCU6>?+v-Ydo3=I9
zao?PPHa9iZZ56y6_i6;TzOHs#aRbn}qY+rSSl7C{2zynqv$mnJb_@0H<Vpphh2~jb
zw?nLJBP#nC%w5;CZ31>LgE0!Lo9deZ@vdIGdaZK<Q}M%GVQXEjP)9R*IfoItPPcB-
zahG|5mp?~&TN^Yq%rbd3b4?|U&0A_43mWOCqc`eL>+7`V>-6X1);ds8qZ-?U)~(xH
zz=bhdzi;qzQQCdWZosFr`^LJa0*;#zz6bnwrZTZnXjrvdsN2x&ZP-><=&NnzUXKdj
zZfK75bCxLBr^X+xxpjN3(9jI4bkX|O)V2xE*4Ac(lGFO?T@5YZ?&&D9=;G#9cSF<N
zjdkm`_^~(KavI~@)qu^~R_AJM-daaCqUa+x_;7*K9SBp>5Ww%aw$@tO150>)SjZjD
z^=qAL_!K67?p{-STT5$0lTh#FMh&amXe9-WExy{d;&wQjtzItAw4t@OsjU&j%RssI
zZgBbwuBf4{rB>ML(?OV{(deqHyJzCL#pr5o6DFQ3jAd<i>kDL(Y6&l~&AE%Ka68wB
zdHUDq|CCP>vda{XM_!x8rE?ivCTB<F+=4hu(Yv1EWEO;f;r_4B|Jxq}+vs`P|8_71
zPI*w$Rp8Ssl+#Yc?#n+1jFcjHke}D!uM@dERXJ4g)Zn_7E5<#2UNV1jNt?&Oa;@++
zPp4zXzst$T841rlDT^%Db7#uY>sdxRY#^K@=yeh_o9eeMtcG+U8!<%ii&)mWmC<R5
zR5bMY=V~;Pjb#!&k6zbI(4iVGT*py@l+Min{%XC(YQ>du+O@25tGjI86|7#++~!@}
zbr`n>ICrxVRByB(4<^p<X8AIA6<8woV>CCbk0=`_h!->(v1T9cQ<xthzH%8~>-4b=
zI$z;+HT^^WN|{_;7*F}X2>QaLRsHkwOl9(%O6PK^UM|xjrZ*n8!Yeg_<Ce1jY+?he
zMTn2aN!2TTrdbaB|97wigTD`#;^fNYuP1++V&IqY+xT|=Yy1h`kUAr+H0{x}pQUxA
zccq&%=49|0MHyescr@b&8P8;RGatzOMdn+XA7@_5oM&HWudz4T_t?K?|Aqa|taq}8
zvML-ubNtaUBYS!F_p=XVpUfV}9?s6mc`)buIj`pgb8gHn&wVWS)7-^*Re8tA$P*D~
zV5{*-w<HxLtxpn@P9~j8+LQcb^3%!FQWmCUrMObODUB&Y%10^ZQf}f`@m2gz{%QWV
z{Ga#>ye)N3YI^D&sb5ZgEcMycgQ@SOs;P6*9BIz9=Cm)SJ(BixT7OzF?X$Fm^kwPG
z({E3&N`D~z@$>`fzf3=!9!$TKeqBa##?Fi{XM88)rx`~wqBF0}%*$Mp>CJ4&Y{}f6
z8OZ!v=Hr>KWWJkuJ~P%n*M5uLZr^0Dvv0Sz*>~ITw?Aor+WwsVS9VL*O<4t5zO3(O
zy_}Wc_=2O;@eRi_j#nM09kJPWWZ#*+C%Y?K$sWr#<?uNLId|vu<UEnnm!ss2<(PA4
z<R;|an#<>w<$7~B=eFeT%Y88SJGoEhK9l?F+~4OK@)qSe@=Ej8=G~LGKkqAfKhAq5
z@0WRR<b9k+&ddk7ElA2rawRn+eKF~Yq-T?UmGpknM@jL?Ur5eMz9V@<a#iwO$&Jb1
zOCCu6G<il!VoFZR>Xh1)`%<1tc`2nY<!nkQC5B(bXYmF6?Yx`c#JBN#_%HE2{I~g^
z^9T9&`Oo;M)Y#P7sn@37lIlwJrfyC3r?#hdrpl>bNqs!^DaicA)V|a=Qva4Zo*I>A
zO{+|+PHRu=O#5$X2h)zEy`J_qB)>4dEWI_oGyS3T$I@R;|84r&^bgWMO+TMLGh<=K
z-i$9otA3R6tBk`Lf6O?aQ47iM$ZXH-fQ~(t`E=&5GvCWhwrAN_*-Pzfp=l38&wgk>
zXrGgnomG^zCTo3GWmZF0ch&=0k7fNJ>*rYqvyNuHp7mDNzh`~HQRvv<sB(PMaoBOp
z@w($J$ADwlu_SwIwm<tj*+0(yMfPvAU(5a=J0WLj&dQukIa_mX&0U?lF89vd=G^w&
b``9i#k^9qJs%Pn2s!Nmp_nPZ}_2+*A-SuN2
--- a/toolkit/mozapps/installer/windows/nsis/common.nsh
+++ b/toolkit/mozapps/installer/windows/nsis/common.nsh
@@ -7605,16 +7605,56 @@
   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 dialog units from the top of a dialog to the bottom of a
+ * control
+ *
+ * _DIALOG the handle of the dialog
+ * _CONTROL the handle of the control
+ * _RES_DU return value - dialog units from the top of the dialog to the bottom
+ *         of the control
+ */
+!macro GetDlgItemBottomDUCall _DIALOG _CONTROL _RES_DU
+  Push "${_DIALOG}"
+  Push "${_CONTROL}"
+  ${CallArtificialFunction} GetDlgItemBottomDU_
+  Pop ${_RES_DU}
+!macroend
+
+!define GetDlgItemBottomDU "!insertmacro GetDlgItemBottomDUCall"
+!define un.GetDlgItemBottomDU "!insertmacro GetDlgItemBottomDUCall"
+
+!macro GetDlgItemBottomDU_
+  Exch $0 ; handle of the control
+  Exch $1 ; handle of the dialog
+  Push $2
+  Push $3
+
+  ; #32770 is the dialog class
+  FindWindow $2 "#32770" "" $HWNDPARENT
+  System::Call '*(i, i, i, i) i .r3'
+  System::Call 'user32::GetWindowRect(i r0, i r3)'
+  System::Call 'user32::MapWindowPoints(i 0, i r2, i r3, i 2)'
+  System::Call 'user32::MapDialogRect(i r1, i r3)'
+  System::Call '*$3(i, i, i, i .r0)'
+  System::Free $3
+
+  Pop $3
+  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.
  * If the text has embedded newlines then the width and height will be
  * determined without trying to optimize the control's width and height. If the
  * text doesn't contain newlines the control's height and width will be
  * dynamically determined using a minimum of 3 lines (incrementing the
  * number of lines if necessary) for the height and the maximum width specified.
  *
  * _TEXT       the text
--- a/toolkit/mozapps/installer/windows/nsis/makensis.mk
+++ b/toolkit/mozapps/installer/windows/nsis/makensis.mk
@@ -26,16 +26,17 @@ CUSTOM_NSIS_PLUGINS = \
 	AccessControl.dll \
 	AppAssocReg.dll \
 	ApplicationID.dll \
 	CertCheck.dll \
 	CityHash.dll \
 	InetBgDL.dll \
 	InvokeShellVerb.dll \
 	liteFirewallW.dll \
+	nsJSON.dll \
 	ServicesHelper.dll \
 	ShellLink.dll \
 	UAC.dll \
 	$(NULL)
 
 CUSTOM_UI = \
 	nsisui.exe \
 	$(NULL)