1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this
3# file, You can obtain one at http://mozilla.org/MPL/2.0/.
4
5# Required Plugins:
6# AppAssocReg
7# CertCheck
8# InetBgDL
9# ShellLink
10# UAC
11
12; Set verbosity to 3 (e.g. no script) to lessen the noise in the build logs
13!verbose 3
14
15SetDatablockOptimize on
16SetCompress off
17CRCCheck on
18
19RequestExecutionLevel user
20
21Unicode true
22ManifestSupportedOS all
23ManifestDPIAware true
24
25!addplugindir ./
26
27Var CheckboxSetAsDefault
28Var CheckboxShortcuts
29Var CheckboxSendPing
30Var CheckboxInstallMaintSvc
31Var CheckboxCleanupProfile
32
33Var FontFamilyName
34
35Var CanWriteToInstallDir
36Var HasRequiredSpaceAvailable
37Var IsDownloadFinished
38Var DownloadSizeBytes
39Var DownloadReset
40Var ExistingTopDir
41Var SpaceAvailableBytes
42Var InitialInstallDir
43Var HandleDownload
44Var CanSetAsDefault
45Var InstallCounterStep
46Var InstallTotalSteps
47Var ProgressCompleted
48Var UsingHighContrastMode
49
50Var ExitCode
51Var FirefoxLaunchCode
52
53Var StartDownloadPhaseTickCount
54; Since the Intro and Options pages can be displayed multiple times the total
55; seconds spent on each of these pages is reported.
56Var IntroPhaseSeconds
57Var OptionsPhaseSeconds
58; The tick count for the last download.
59Var StartLastDownloadTickCount
60; The number of seconds from the start of the download phase until the first
61; bytes are received. This is only recorded for first request so it is possible
62; to determine connection issues for the first request.
63Var DownloadFirstTransferSeconds
64; The last four tick counts are for the end of a phase in the installation page.
65Var EndDownloadPhaseTickCount
66Var EndPreInstallPhaseTickCount
67Var EndInstallPhaseTickCount
68Var EndFinishPhaseTickCount
69
70Var InitialInstallRequirementsCode
71Var ExistingProfile
72Var ExistingVersion
73Var ExistingBuildID
74Var DownloadedBytes
75Var DownloadRetryCount
76Var OpenedDownloadPage
77Var DownloadServerIP
78Var PostSigningData
79Var PreviousInstallDir
80Var ProfileCleanupPromptType
81Var AppLaunchWaitTickCount
82Var TimerHandle
83
84!define ARCH_X86 1
85!define ARCH_AMD64 2
86!define ARCH_AARCH64 3
87Var ArchToInstall
88
89; Uncomment the following to prevent pinging the metrics server when testing
90; the stub installer
91;!define STUB_DEBUG
92
93!define StubURLVersion "v8"
94
95; Successful install exit code
96!define ERR_SUCCESS 0
97
98/**
99 * The following errors prefixed with ERR_DOWNLOAD apply to the download phase.
100 */
101; The download was cancelled by the user
102!define ERR_DOWNLOAD_CANCEL 10
103
104; Too many attempts to download. The maximum attempts is defined in
105; DownloadMaxRetries.
106!define ERR_DOWNLOAD_TOO_MANY_RETRIES 11
107
108/**
109 * The following errors prefixed with ERR_PREINSTALL apply to the pre-install
110 * check phase.
111 */
112; Unable to acquire a file handle to the downloaded file
113!define ERR_PREINSTALL_INVALID_HANDLE 20
114
115; The downloaded file's certificate is not trusted by the certificate store.
116!define ERR_PREINSTALL_CERT_UNTRUSTED 21
117
118; The downloaded file's certificate attribute values were incorrect.
119!define ERR_PREINSTALL_CERT_ATTRIBUTES 22
120
121; The downloaded file's certificate is not trusted by the certificate store and
122; certificate attribute values were incorrect.
123!define ERR_PREINSTALL_CERT_UNTRUSTED_AND_ATTRIBUTES 23
124
125; Timed out while waiting for the certificate checks to run.
126!define ERR_PREINSTALL_CERT_TIMEOUT 24
127
128/**
129 * The following errors prefixed with ERR_INSTALL apply to the install phase.
130 */
131; The installation timed out. The installation timeout is defined by the number
132; of progress steps defined in InstallTotalSteps and the install timer
133; interval defined in InstallIntervalMS
134!define ERR_INSTALL_TIMEOUT 30
135
136; Maximum times to retry the download before displaying an error
137!define DownloadMaxRetries 9
138
139; Interval before retrying to download. 3 seconds is used along with 10
140; attempted downloads (the first attempt along with 9 retries) to give a
141; minimum of 30 seconds or retrying before giving up.
142!define DownloadRetryIntervalMS 3000
143
144; Interval for the download timer
145!define DownloadIntervalMS 200
146
147; Timeout for the certificate check
148!define PreinstallCertCheckMaxWaitSec 30
149
150; Interval for the install timer
151!define InstallIntervalMS 100
152
153; Number of steps for the install progress.
154; This might not be enough when installing on a slow network drive so it will
155; fallback to downloading the full installer if it reaches this number.
156
157; Approximately 150 seconds with a 100 millisecond timer.
158!define InstallCleanTotalSteps 1500
159
160; Approximately 165 seconds with a 100 millisecond timer.
161!define InstallPaveOverTotalSteps 1650
162
163; Blurb duty cycle
164!define BlurbDisplayMS 19500
165!define BlurbBlankMS 500
166
167; Interval between checks for the application window and progress bar updates.
168!define AppLaunchWaitIntervalMS 100
169
170; Total time to wait for the application to start before just exiting.
171!define AppLaunchWaitTimeoutMS 10000
172
173; Maximum value of the download/install/launch progress bar, and the end values
174; for each individual stage.
175!define PROGRESS_BAR_TOTAL_STEPS 500
176!define PROGRESS_BAR_DOWNLOAD_END_STEP 300
177!define PROGRESS_BAR_INSTALL_END_STEP 475
178!define PROGRESS_BAR_APP_LAUNCH_END_STEP 500
179
180; Amount of physical memory required for the 64-bit build to be selected (2 GB).
181; Machines with this or less RAM get the 32-bit build, even with a 64-bit OS.
182!define RAM_NEEDED_FOR_64BIT 0x80000000
183
184; Attempt to elevate Standard Users in addition to users that
185; are a member of the Administrators group.
186!define NONADMIN_ELEVATE
187
188!define CONFIG_INI "config.ini"
189!define PARTNER_INI "$EXEDIR\partner.ini"
190
191!ifndef FILE_SHARE_READ
192  !define FILE_SHARE_READ 1
193!endif
194!ifndef GENERIC_READ
195  !define GENERIC_READ 0x80000000
196!endif
197!ifndef OPEN_EXISTING
198  !define OPEN_EXISTING 3
199!endif
200!ifndef INVALID_HANDLE_VALUE
201  !define INVALID_HANDLE_VALUE -1
202!endif
203
204!define DefaultInstDir32bit "$PROGRAMFILES32\${BrandFullName}"
205!define DefaultInstDir64bit "$PROGRAMFILES64\${BrandFullName}"
206
207!include "LogicLib.nsh"
208!include "FileFunc.nsh"
209!include "TextFunc.nsh"
210!include "WinVer.nsh"
211!include "WordFunc.nsh"
212
213!insertmacro GetParameters
214!insertmacro GetOptions
215!insertmacro LineFind
216!insertmacro StrFilter
217
218!include "locales.nsi"
219!include "branding.nsi"
220
221!include "defines.nsi"
222
223; Must be included after defines.nsi
224!include "locale-fonts.nsh"
225
226; The OFFICIAL define is a workaround to support different urls for Release and
227; Beta since they share the same branding when building with other branches that
228; set the update channel to beta.
229!ifdef OFFICIAL
230!ifdef BETA_UPDATE_CHANNEL
231!undef URLStubDownloadX86
232!undef URLStubDownloadAMD64
233!undef URLStubDownloadAArch64
234!define URLStubDownloadX86 "https://download.mozilla.org/?os=win&lang=${AB_CD}&product=firefox-beta-latest"
235!define URLStubDownloadAMD64 "https://download.mozilla.org/?os=win64&lang=${AB_CD}&product=firefox-beta-latest"
236!define URLStubDownloadAArch64 "https://download.mozilla.org/?os=win64-aarch64&lang=${AB_CD}&product=firefox-beta-latest"
237!undef URLManualDownload
238!define URLManualDownload "https://www.mozilla.org/${AB_CD}/firefox/installer-help/?channel=beta&installer_lang=${AB_CD}"
239!undef Channel
240!define Channel "beta"
241!endif
242!endif
243
244!include "common.nsh"
245
246!insertmacro CopyPostSigningData
247!insertmacro ElevateUAC
248!insertmacro GetLongPath
249!insertmacro GetPathFromString
250!insertmacro GetParent
251!insertmacro GetSingleInstallPath
252!insertmacro InitHashAppModelId
253!insertmacro IsUserAdmin
254!insertmacro RemovePrecompleteEntries
255!insertmacro SetBrandNameVars
256!insertmacro ITBL3Create
257!insertmacro UnloadUAC
258
259VIAddVersionKey "FileDescription" "${BrandShortName} Installer"
260VIAddVersionKey "OriginalFilename" "setup-stub.exe"
261
262Name "$BrandFullName"
263OutFile "setup-stub.exe"
264Icon "firefox64.ico"
265XPStyle on
266BrandingText " "
267ChangeUI IDD_INST "nsisui.exe"
268
269!ifdef ${AB_CD}_rtl
270  LoadLanguageFile "locale-rtl.nlf"
271!else
272  LoadLanguageFile "locale.nlf"
273!endif
274
275!include "nsisstrings.nlf"
276
277Caption "$(INSTALLER_WIN_CAPTION)"
278
279Page custom createProfileCleanup
280Page custom createInstall ; Download / Installation page
281
282Function .onInit
283  ; Remove the current exe directory from the search order.
284  ; This only effects LoadLibrary calls and not implicitly loaded DLLs.
285  System::Call 'kernel32::SetDllDirectoryW(w "")'
286
287  StrCpy $LANGUAGE 0
288  ; This macro is used to set the brand name variables but the ini file method
289  ; isn't supported for the stub installer.
290  ${SetBrandNameVars} "$PLUGINSDIR\ignored.ini"
291
292  ; Don't install on systems that don't support SSE2. The parameter value of
293  ; 10 is for PF_XMMI64_INSTRUCTIONS_AVAILABLE which will check whether the
294  ; SSE2 instruction set is available.
295  System::Call "kernel32::IsProcessorFeaturePresent(i 10)i .R7"
296
297  ; Windows NT 6.0 (Vista/Server 2008) and lower are not supported.
298  ${Unless} ${AtLeastWin7}
299    ${If} "$R7" == "0"
300      strCpy $R7 "$(WARN_MIN_SUPPORTED_OSVER_CPU_MSG)"
301    ${Else}
302      strCpy $R7 "$(WARN_MIN_SUPPORTED_OSVER_MSG)"
303    ${EndIf}
304    MessageBox MB_OKCANCEL|MB_ICONSTOP "$R7" IDCANCEL +2
305    ExecShell "open" "${URLSystemRequirements}"
306    Quit
307  ${EndUnless}
308
309  ; SSE2 CPU support
310  ${If} "$R7" == "0"
311    MessageBox MB_OKCANCEL|MB_ICONSTOP "$(WARN_MIN_SUPPORTED_CPU_MSG)" IDCANCEL +2
312    ExecShell "open" "${URLSystemRequirements}"
313    Quit
314  ${EndIf}
315
316  Call GetArchToInstall
317  ${If} $ArchToInstall == ${ARCH_AARCH64}
318  ${OrIf} $ArchToInstall == ${ARCH_AMD64}
319    StrCpy $INSTDIR "${DefaultInstDir64bit}"
320  ${Else}
321    StrCpy $INSTDIR "${DefaultInstDir32bit}"
322  ${EndIf}
323
324  ; Require elevation if the user can elevate
325  ${ElevateUAC}
326
327  ; If we have any existing installation, use its location as the default
328  ; path for this install, even if it's not the same architecture.
329  SetRegView 32
330  SetShellVarContext all ; Set SHCTX to HKLM
331  ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
332
333  ${If} "$R9" == "false"
334    ${If} ${IsNativeAMD64}
335    ${OrIf} ${IsNativeARM64}
336      SetRegView 64
337      ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
338    ${EndIf}
339  ${EndIf}
340
341  ${If} "$R9" == "false"
342    SetShellVarContext current ; Set SHCTX to HKCU
343    ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $R9
344  ${EndIf}
345
346  StrCpy $PreviousInstallDir ""
347  ${If} "$R9" != "false"
348    StrCpy $PreviousInstallDir "$R9"
349    StrCpy $INSTDIR "$PreviousInstallDir"
350  ${EndIf}
351
352  ; Used to determine if the default installation directory was used.
353  StrCpy $InitialInstallDir "$INSTDIR"
354
355  ClearErrors
356  WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
357                   "Write Test"
358
359  ; Only display set as default when there is write access to HKLM and on Win7
360  ; and below.
361  ${If} ${Errors}
362  ${OrIf} ${AtLeastWin8}
363    StrCpy $CanSetAsDefault "false"
364  ${Else}
365    DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
366    StrCpy $CanSetAsDefault "true"
367  ${EndIf}
368  StrCpy $CheckboxSetAsDefault "0"
369
370  ; Initialize the majority of variables except those that need to be reset
371  ; when a page is displayed.
372  StrCpy $ExitCode "${ERR_DOWNLOAD_CANCEL}"
373  StrCpy $IntroPhaseSeconds "0"
374  StrCpy $OptionsPhaseSeconds "0"
375  StrCpy $EndPreInstallPhaseTickCount "0"
376  StrCpy $EndInstallPhaseTickCount "0"
377  StrCpy $StartDownloadPhaseTickCount "0"
378  StrCpy $EndDownloadPhaseTickCount "0"
379  StrCpy $InitialInstallRequirementsCode ""
380  StrCpy $IsDownloadFinished ""
381  StrCpy $FirefoxLaunchCode "0"
382  StrCpy $CheckboxShortcuts "1"
383  StrCpy $CheckboxSendPing "1"
384  StrCpy $CheckboxCleanupProfile "0"
385  StrCpy $ProgressCompleted "0"
386!ifdef MOZ_MAINTENANCE_SERVICE
387  ; We can only install the maintenance service if the user is an admin.
388  Call IsUserAdmin
389  Pop $0
390  ${If} "$0" == "true"
391    StrCpy $CheckboxInstallMaintSvc "1"
392  ${Else}
393    StrCpy $CheckboxInstallMaintSvc "0"
394  ${EndIf}
395!else
396  StrCpy $CheckboxInstallMaintSvc "0"
397!endif
398
399  StrCpy $FontFamilyName ""
400!ifdef FONT_FILE1
401  ${If} ${FileExists} "$FONTS\${FONT_FILE1}"
402    StrCpy $FontFamilyName "${FONT_NAME1}"
403  ${EndIf}
404!endif
405
406!ifdef FONT_FILE2
407  ${If} $FontFamilyName == ""
408  ${AndIf} ${FileExists} "$FONTS\${FONT_FILE2}"
409    StrCpy $FontFamilyName "${FONT_NAME2}"
410  ${EndIf}
411!endif
412
413  ${If} $FontFamilyName == ""
414    StrCpy $FontFamilyName "$(^Font)"
415  ${EndIf}
416
417  InitPluginsDir
418  File /oname=$PLUGINSDIR\bgstub.jpg "bgstub.jpg"
419
420  ; Detect whether the machine is running with a high contrast theme.
421  ; We'll hide our background images in that case, both because they don't
422  ; always render properly and also to improve the contrast.
423  System::Call '*(i 12, i 0, p 0) p . r0'
424  ; 0x42 == SPI_GETHIGHCONTRAST
425  System::Call 'user32::SystemParametersInfoW(i 0x42, i 0, p r0, i 0)'
426  System::Call '*$0(i, i . r1, p)'
427  System::Free $0
428  IntOp $UsingHighContrastMode $1 & 1
429
430  SetShellVarContext all ; Set SHCTX to All Users
431  ; If the user doesn't have write access to the installation directory set
432  ; the installation directory to a subdirectory of the user's local
433  ; application directory (e.g. non-roaming).
434  Call CanWrite
435  ${If} "$CanWriteToInstallDir" == "false"
436    ${GetLocalAppDataFolder} $0
437    StrCpy $INSTDIR "$0\${BrandFullName}\"
438    Call CanWrite
439  ${EndIf}
440
441  Call CheckSpace
442
443  ${If} ${FileExists} "$INSTDIR"
444    ; Always display the long path if the path exists.
445    ${GetLongPath} "$INSTDIR" $INSTDIR
446  ${EndIf}
447
448  ; Check whether the install requirements are satisfied using the default
449  ; values for metrics.
450  ${If} "$InitialInstallRequirementsCode" == ""
451    ${If} "$CanWriteToInstallDir" != "true"
452    ${AndIf} "$HasRequiredSpaceAvailable" != "true"
453      StrCpy $InitialInstallRequirementsCode "1"
454    ${ElseIf} "$CanWriteToInstallDir" != "true"
455      StrCpy $InitialInstallRequirementsCode "2"
456    ${ElseIf} "$HasRequiredSpaceAvailable" != "true"
457      StrCpy $InitialInstallRequirementsCode "3"
458    ${Else}
459      StrCpy $InitialInstallRequirementsCode "0"
460    ${EndIf}
461  ${EndIf}
462
463  Call CanWrite
464  ${If} "$CanWriteToInstallDir" == "false"
465    MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_WRITE_ACCESS_QUIT)$\n$\n$INSTDIR"
466    Quit
467  ${EndIf}
468
469  Call CheckSpace
470  ${If} "$HasRequiredSpaceAvailable" == "false"
471    MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_DISK_SPACE_QUIT)"
472    Quit
473  ${EndIf}
474
475  ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
476
477  File /oname=$PLUGINSDIR\stub_common.css "stub_common.css"
478  File /oname=$PLUGINSDIR\stub_common.js "stub_common.js"
479FunctionEnd
480
481; .onGUIInit isn't needed except for RTL locales
482!ifdef ${AB_CD}_rtl
483Function .onGUIInit
484  ${MakeWindowRTL} $HWNDPARENT
485FunctionEnd
486!endif
487
488Function .onGUIEnd
489  Delete "$PLUGINSDIR\_temp"
490  Delete "$PLUGINSDIR\download.exe"
491  Delete "$PLUGINSDIR\${CONFIG_INI}"
492
493  ${UnloadUAC}
494FunctionEnd
495
496Function .onUserAbort
497  WebBrowser::CancelTimer $TimerHandle
498
499  ${If} "$IsDownloadFinished" != ""
500    ; Go ahead and cancel the download so it doesn't keep running while this
501    ; prompt is up. We'll resume it if the user decides to continue.
502    InetBgDL::Get /RESET /END
503
504    ${ShowTaskDialog} $(STUB_CANCEL_PROMPT_HEADING) \
505                      $(STUB_CANCEL_PROMPT_MESSAGE) \
506                      $(STUB_CANCEL_PROMPT_BUTTON_CONTINUE) \
507                      $(STUB_CANCEL_PROMPT_BUTTON_EXIT)
508    Pop $0
509    ${If} $0 == 1002
510      ; The cancel button was clicked
511      Call LaunchHelpPage
512      Call SendPing
513    ${Else}
514      ; Either the continue button was clicked or the dialog was dismissed
515      Call StartDownload
516    ${EndIf}
517  ${Else}
518    Call SendPing
519  ${EndIf}
520
521  ; Aborting the abort will allow SendPing to hide the installer window and
522  ; close the installer after it sends the metrics ping, or allow us to just go
523  ; back to installing if that's what the user selected.
524  Abort
525FunctionEnd
526
527!macro _RegisterAllCustomFunctions
528  GetFunctionAddress $0 getUIString
529  WebBrowser::RegisterCustomFunction $0 "getUIString"
530
531  GetFunctionAddress $0 getTextDirection
532  WebBrowser::RegisterCustomFunction $0 "getTextDirection"
533
534  GetFunctionAddress $0 getFontName
535  WebBrowser::RegisterCustomFunction $0 "getFontName"
536
537  GetFunctionAddress $0 getIsHighContrast
538  WebBrowser::RegisterCustomFunction $0 "getIsHighContrast"
539
540  GetFunctionAddress $0 gotoInstallPage
541  WebBrowser::RegisterCustomFunction $0 "gotoInstallPage"
542
543  GetFunctionAddress $0 getProgressBarPercent
544  WebBrowser::RegisterCustomFunction $0 "getProgressBarPercent"
545!macroend
546!define RegisterAllCustomFunctions "!insertmacro _RegisterAllCustomFunctions"
547
548!macro _StartTimer _INTERVAL_MS _FUNCTION_NAME
549  Push $0
550  GetFunctionAddress $0 ${_FUNCTION_NAME}
551  WebBrowser::CreateTimer $0 ${_INTERVAL_MS}
552  Pop $TimerHandle
553  Pop $0
554!macroend
555!define StartTimer "!insertmacro _StartTimer"
556
557Function gotoInstallPage
558  Pop $0
559  StrCpy $CheckboxCleanupProfile $0
560
561  StrCpy $R9 1
562  Call RelativeGotoPage
563  Push $0
564FunctionEnd
565
566Function getProgressBarPercent
567  ; Custom functions always get one parameter, which we don't use here.
568  ; But we will use $0 as a scratch accumulator register.
569  Pop $0
570  ; This math is getting the progess bar completion fraction and converting it
571  ; to a percentage, but we implement that with the operations in the reverse
572  ; of the intuitive order so that our integer math doesn't truncate to zero.
573  IntOp $0 $ProgressCompleted * 100
574  IntOp $0 $0 / ${PROGRESS_BAR_TOTAL_STEPS}
575  Push $0
576FunctionEnd
577
578Function getTextDirection
579  Pop $0
580  !ifdef ${AB_CD}_rtl
581    Push "rtl"
582  !else
583    Push "ltr"
584  !endif
585FunctionEnd
586
587Function getFontName
588  Pop $0
589  Push $FontFamilyName
590FunctionEnd
591
592Function getIsHighContrast
593  Pop $0
594  Push $UsingHighContrastMode
595FunctionEnd
596
597Function getUIString
598  Pop $0
599  ${Select} $0
600    ${Case} "cleanup_header"
601      ${If} $ProfileCleanupPromptType == 1
602        Push "$(STUB_CLEANUP_REINSTALL_HEADER2)"
603      ${Else}
604        Push "$(STUB_CLEANUP_PAVEOVER_HEADER2)"
605      ${EndIf}
606    ${Case} "cleanup_button"
607      ${If} $ProfileCleanupPromptType == 1
608        Push "$(STUB_CLEANUP_REINSTALL_BUTTON2)"
609      ${Else}
610        Push "$(STUB_CLEANUP_PAVEOVER_BUTTON2)"
611      ${EndIf}
612    ${Case} "cleanup_checkbox"
613      Push "$(STUB_CLEANUP_CHECKBOX_LABEL2)"
614    ${Case} "installing_header"
615      Push "$(STUB_INSTALLING_HEADLINE2)"
616    ${Case} "installing_label"
617      Push "$(STUB_INSTALLING_LABEL2)"
618    ${Case} "installing_content"
619      Push "$(STUB_INSTALLING_BODY2)"
620    ${Case} "installing_blurb_0"
621      Push "$(STUB_BLURB_FIRST1)"
622    ${Case} "installing_blurb_1"
623      Push "$(STUB_BLURB_SECOND1)"
624    ${Case} "installing_blurb_2"
625      Push "$(STUB_BLURB_THIRD1)"
626    ${Case} "global_footer"
627      Push "$(STUB_BLURB_FOOTER2)"
628    ${Default}
629      Push ""
630  ${EndSelect}
631FunctionEnd
632
633Function createProfileCleanup
634  Call ShouldPromptForProfileCleanup
635
636  ${If} $ProfileCleanupPromptType == 0
637    StrCpy $CheckboxCleanupProfile 0
638    Abort ; Skip this page
639  ${EndIf}
640
641  ${RegisterAllCustomFunctions}
642
643  File /oname=$PLUGINSDIR\profile_cleanup.html "profile_cleanup.html"
644  File /oname=$PLUGINSDIR\profile_cleanup_page.css "profile_cleanup_page.css"
645  File /oname=$PLUGINSDIR\profile_cleanup.js "profile_cleanup.js"
646  WebBrowser::ShowPage "$PLUGINSDIR\profile_cleanup.html"
647FunctionEnd
648
649Function createInstall
650  GetDlgItem $0 $HWNDPARENT 1 ; Install button
651  EnableWindow $0 0
652  ShowWindow $0 ${SW_HIDE}
653
654  GetDlgItem $0 $HWNDPARENT 3 ; Back button
655  EnableWindow $0 0
656  ShowWindow $0 ${SW_HIDE}
657
658  GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
659  ; Hide the Cancel button, but don't disable it (or else it won't be possible
660  ; to close the window)
661  ShowWindow $0 ${SW_HIDE}
662
663  ; Get keyboard focus on the parent
664  System::Call "user32::SetFocus(p$HWNDPARENT)"
665
666  ; Set $DownloadReset to true so the first download tick count is measured.
667  StrCpy $DownloadReset "true"
668  StrCpy $IsDownloadFinished "false"
669  StrCpy $DownloadRetryCount "0"
670  StrCpy $DownloadedBytes "0"
671  StrCpy $StartLastDownloadTickCount ""
672  StrCpy $DownloadFirstTransferSeconds ""
673  StrCpy $OpenedDownloadPage "0"
674
675  ClearErrors
676  ReadINIStr $ExistingVersion "$INSTDIR\application.ini" "App" "Version"
677  ${If} ${Errors}
678    StrCpy $ExistingVersion "0"
679  ${EndIf}
680
681  ClearErrors
682  ReadINIStr $ExistingBuildID "$INSTDIR\application.ini" "App" "BuildID"
683  ${If} ${Errors}
684    StrCpy $ExistingBuildID "0"
685  ${EndIf}
686
687  ${GetLocalAppDataFolder} $0
688  ${If} ${FileExists} "$0\Mozilla\Firefox"
689    StrCpy $ExistingProfile "1"
690  ${Else}
691    StrCpy $ExistingProfile "0"
692  ${EndIf}
693
694  StrCpy $DownloadServerIP ""
695
696  System::Call "kernel32::GetTickCount()l .s"
697  Pop $StartDownloadPhaseTickCount
698
699  ${If} ${FileExists} "$INSTDIR\uninstall\uninstall.log"
700    StrCpy $InstallTotalSteps ${InstallPaveOverTotalSteps}
701  ${Else}
702    StrCpy $InstallTotalSteps ${InstallCleanTotalSteps}
703  ${EndIf}
704
705  ${ITBL3Create}
706  ${ITBL3SetProgressState} "${TBPF_INDETERMINATE}"
707
708  ; Make sure the file we're about to try to download to doesn't already exist,
709  ; so we don't end up trying to "resume" on top of the wrong file.
710  Delete "$PLUGINSDIR\download.exe"
711
712  ${StartTimer} ${DownloadIntervalMS} StartDownload
713
714  ${RegisterAllCustomFunctions}
715
716  File /oname=$PLUGINSDIR\installing.html "installing.html"
717  File /oname=$PLUGINSDIR\installing_page.css "installing_page.css"
718  File /oname=$PLUGINSDIR\installing.js "installing.js"
719  WebBrowser::ShowPage "$PLUGINSDIR\installing.html"
720FunctionEnd
721
722Function StartDownload
723  WebBrowser::CancelTimer $TimerHandle
724
725  Call GetDownloadURL
726  Pop $0
727  InetBgDL::Get "$0" "$PLUGINSDIR\download.exe" \
728                /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
729
730  ${StartTimer} ${DownloadIntervalMS} OnDownload
731
732  ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
733    RmDir /r "$INSTDIR\${TO_BE_DELETED}"
734  ${EndIf}
735FunctionEnd
736
737Function SetProgressBars
738  ${ITBL3SetProgressValue} "$ProgressCompleted" "${PROGRESS_BAR_TOTAL_STEPS}"
739FunctionEnd
740
741Function OnDownload
742  InetBgDL::GetStats
743  # $0 = HTTP status code, 0=Completed
744  # $1 = Completed files
745  # $2 = Remaining files
746  # $3 = Number of downloaded bytes for the current file
747  # $4 = Size of current file (Empty string if the size is unknown)
748  # /RESET must be used if status $0 > 299 (e.g. failure), even if resuming
749  # When status is $0 =< 299 it is handled by InetBgDL
750  StrCpy $DownloadServerIP "$5"
751  ${If} $0 > 299
752    WebBrowser::CancelTimer $TimerHandle
753    IntOp $DownloadRetryCount $DownloadRetryCount + 1
754    ${If} $DownloadRetryCount >= ${DownloadMaxRetries}
755      StrCpy $ExitCode "${ERR_DOWNLOAD_TOO_MANY_RETRIES}"
756      ; Use a timer so the UI has a chance to update
757      ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
758      Return
759    ${EndIf}
760
761    ; 1000 is a special code meaning InetBgDL lost the connection before it got
762    ; all the bytes it was expecting. We'll try to resume the transfer in that
763    ; case (assuming we aren't out of retries), so don't treat it as a reset
764    ; or clear the progress bar.
765    ${If} $0 != 1000
766      ${If} "$DownloadReset" != "true"
767        StrCpy $DownloadedBytes "0"
768        ${ITBL3SetProgressState} "${TBPF_INDETERMINATE}"
769      ${EndIf}
770      StrCpy $DownloadSizeBytes ""
771      StrCpy $DownloadReset "true"
772      Delete "$PLUGINSDIR\download.exe"
773    ${EndIf}
774
775    InetBgDL::Get /RESET /END
776    ${StartTimer} ${DownloadRetryIntervalMS} StartDownload
777    Return
778  ${EndIf}
779
780  ${If} "$DownloadReset" == "true"
781    System::Call "kernel32::GetTickCount()l .s"
782    Pop $StartLastDownloadTickCount
783    StrCpy $DownloadReset "false"
784    ; The seconds elapsed from the start of the download phase until the first
785    ; bytes are received are only recorded for the first request so it is
786    ; possible to determine connection issues for the first request.
787    ${If} "$DownloadFirstTransferSeconds" == ""
788      ; Get the seconds elapsed from the start of the download phase until the
789      ; first bytes are received.
790      ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$StartLastDownloadTickCount" $DownloadFirstTransferSeconds
791    ${EndIf}
792  ${EndIf}
793
794  ${If} "$DownloadSizeBytes" == ""
795  ${AndIf} "$4" != ""
796    StrCpy $DownloadSizeBytes "$4"
797    StrCpy $ProgressCompleted 0
798  ${EndIf}
799
800  ; Don't update the status until after the download starts
801  ${If} $2 != 0
802  ${AndIf} "$4" == ""
803    Return
804  ${EndIf}
805
806  ${If} $IsDownloadFinished != "true"
807    ${If} $2 == 0
808      WebBrowser::CancelTimer $TimerHandle
809      StrCpy $IsDownloadFinished "true"
810      System::Call "kernel32::GetTickCount()l .s"
811      Pop $EndDownloadPhaseTickCount
812
813      ${If} "$DownloadSizeBytes" == ""
814        ; It's possible for the download to finish before we were able to
815        ; get the size while it was downloading, and InetBgDL doesn't report
816        ; it afterwards. Use the size of the finished file.
817        ClearErrors
818        FileOpen $5 "$PLUGINSDIR\download.exe" r
819        ${IfNot} ${Errors}
820          FileSeek $5 0 END $DownloadSizeBytes
821          FileClose $5
822        ${EndIf}
823      ${EndIf}
824      StrCpy $DownloadedBytes "$DownloadSizeBytes"
825
826      ; Update the progress bars first in the UI change so they take affect
827      ; before other UI changes.
828      StrCpy $ProgressCompleted "${PROGRESS_BAR_DOWNLOAD_END_STEP}"
829      Call SetProgressBars
830
831      ; Disable the Cancel button during the install
832      GetDlgItem $5 $HWNDPARENT 2
833      EnableWindow $5 0
834
835      ; Open a handle to prevent modification of the full installer
836      StrCpy $R9 "${INVALID_HANDLE_VALUE}"
837      System::Call 'kernel32::CreateFileW(w "$PLUGINSDIR\download.exe", \
838                                          i ${GENERIC_READ}, \
839                                          i ${FILE_SHARE_READ}, i 0, \
840                                          i ${OPEN_EXISTING}, i 0, i 0) i .R9'
841      StrCpy $HandleDownload "$R9"
842
843      ${If} $HandleDownload == ${INVALID_HANDLE_VALUE}
844        StrCpy $ExitCode "${ERR_PREINSTALL_INVALID_HANDLE}"
845        System::Call "kernel32::GetTickCount()l .s"
846        Pop $EndPreInstallPhaseTickCount
847        ; Use a timer so the UI has a chance to update
848        ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
849      ${Else}
850        CertCheck::CheckPETrustAndInfoAsync "$PLUGINSDIR\download.exe" \
851          "${CertNameDownload}" "${CertIssuerDownload}"
852        ${StartTimer} ${DownloadIntervalMS} OnCertCheck
853      ${EndIf}
854    ${Else}
855      StrCpy $DownloadedBytes "$3"
856      System::Int64Op $DownloadedBytes * ${PROGRESS_BAR_DOWNLOAD_END_STEP}
857      Pop $ProgressCompleted
858      System::Int64Op $ProgressCompleted / $DownloadSizeBytes
859      Pop $ProgressCompleted
860      Call SetProgressBars
861    ${EndIf}
862  ${EndIf}
863FunctionEnd
864
865Function OnCertCheck
866  System::Call "kernel32::GetTickCount()l .s"
867  Pop $EndPreInstallPhaseTickCount
868
869  CertCheck::GetStatus
870  Pop $0
871  ${If} $0 == 0
872    ${GetSecondsElapsed} "$EndDownloadPhaseTickCount" "$EndPreInstallPhaseTickCount" $0
873    ${If} $0 >= ${PreinstallCertCheckMaxWaitSec}
874      WebBrowser::CancelTimer $TimerHandle
875      StrCpy $ExitCode "${ERR_PREINSTALL_CERT_TIMEOUT}"
876      ; Use a timer so the UI has a chance to update
877      ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
878    ${EndIf}
879    Return
880  ${EndIf}
881  Pop $0
882  Pop $1
883
884  ${If} $0 == 0
885  ${AndIf} $1 == 0
886    StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED_AND_ATTRIBUTES}"
887  ${ElseIf} $0 == 0
888    StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED}"
889  ${ElseIf} $1 == 0
890    StrCpy $ExitCode "${ERR_PREINSTALL_CERT_ATTRIBUTES}"
891  ${EndIf}
892
893  WebBrowser::CancelTimer $TimerHandle
894
895  ${If} $0 == 0
896  ${OrIf} $1 == 0
897    ; Use a timer so the UI has a chance to update
898    ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
899    Return
900  ${EndIf}
901
902  Call LaunchFullInstaller
903FunctionEnd
904
905Function LaunchFullInstaller
906  ; Instead of extracting the files we use the downloaded installer to
907  ; install in case it needs to perform operations that the stub doesn't
908  ; know about.
909  WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "InstallDirectoryPath" "$INSTDIR"
910  ; Don't create the QuickLaunch or Taskbar shortcut from the launched installer
911  WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "QuickLaunchShortcut" "false"
912
913  ; Always create a start menu shortcut, so the user always has some way
914  ; to access the application.
915  WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "StartMenuShortcuts" "true"
916
917  ; Either avoid or force adding a taskbar pin and desktop shortcut
918  ; based on the checkbox value.
919  ${If} $CheckboxShortcuts == 0
920    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "false"
921    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "false"
922  ${Else}
923    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "true"
924    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "true"
925  ${EndIf}
926
927!ifdef MOZ_MAINTENANCE_SERVICE
928  ${If} $CheckboxInstallMaintSvc == 1
929    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "true"
930  ${Else}
931    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
932  ${EndIf}
933!else
934  WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
935!endif
936
937  ; Delete the taskbar shortcut history to ensure we do the right thing based on
938  ; the config file above.
939  ${GetShortcutsLogPath} $0
940  Delete "$0"
941
942  ${RemovePrecompleteEntries} "false"
943
944  ; Delete the install.log and let the full installer create it. When the
945  ; installer closes it we can detect that it has completed.
946  Delete "$INSTDIR\install.log"
947
948  ; Delete firefox.exe.moz-upgrade and firefox.exe.moz-delete if it exists
949  ; since it being present will require an OS restart for the full
950  ; installer.
951  Delete "$INSTDIR\${FileMainEXE}.moz-upgrade"
952  Delete "$INSTDIR\${FileMainEXE}.moz-delete"
953
954  System::Call "kernel32::GetTickCount()l .s"
955  Pop $EndPreInstallPhaseTickCount
956
957  Exec "$\"$PLUGINSDIR\download.exe$\" /LaunchedFromStub /INI=$PLUGINSDIR\${CONFIG_INI}"
958  ${StartTimer} ${InstallIntervalMS} CheckInstall
959FunctionEnd
960
961Function SendPing
962  HideWindow
963
964  ${If} $CheckboxSendPing == 1
965    ; Get the tick count for the completion of all phases.
966    System::Call "kernel32::GetTickCount()l .s"
967    Pop $EndFinishPhaseTickCount
968
969    ; When the value of $IsDownloadFinished is false the download was started
970    ; but didn't finish. In this case the tick count stored in
971    ; $EndFinishPhaseTickCount is used to determine how long the download was
972    ; in progress.
973    ${If} "$IsDownloadFinished" == "false"
974      StrCpy $EndDownloadPhaseTickCount "$EndFinishPhaseTickCount"
975      ; Cancel the download in progress
976      InetBgDL::Get /RESET /END
977    ${EndIf}
978
979
980    ; When $DownloadFirstTransferSeconds equals an empty string the download
981    ; never successfully started so set the value to 0. It will be possible to
982    ; determine that the download didn't successfully start from the seconds for
983    ; the last download.
984    ${If} "$DownloadFirstTransferSeconds" == ""
985      StrCpy $DownloadFirstTransferSeconds "0"
986    ${EndIf}
987
988    ; When $StartLastDownloadTickCount equals an empty string the download never
989    ; successfully started so set the value to $EndDownloadPhaseTickCount to
990    ; compute the correct value.
991    ${If} $StartLastDownloadTickCount == ""
992      ; This could happen if the download never successfully starts
993      StrCpy $StartLastDownloadTickCount "$EndDownloadPhaseTickCount"
994    ${EndIf}
995
996    ; When $EndPreInstallPhaseTickCount equals 0 the installation phase was
997    ; never completed so set its value to $EndFinishPhaseTickCount to compute
998    ; the correct value.
999    ${If} "$EndPreInstallPhaseTickCount" == "0"
1000      StrCpy $EndPreInstallPhaseTickCount "$EndFinishPhaseTickCount"
1001    ${EndIf}
1002
1003    ; When $EndInstallPhaseTickCount equals 0 the installation phase was never
1004    ; completed so set its value to $EndFinishPhaseTickCount to compute the
1005    ; correct value.
1006    ${If} "$EndInstallPhaseTickCount" == "0"
1007      StrCpy $EndInstallPhaseTickCount "$EndFinishPhaseTickCount"
1008    ${EndIf}
1009
1010    ; Get the seconds elapsed from the start of the download phase to the end of
1011    ; the download phase.
1012    ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$EndDownloadPhaseTickCount" $0
1013
1014    ; Get the seconds elapsed from the start of the last download to the end of
1015    ; the last download.
1016    ${GetSecondsElapsed} "$StartLastDownloadTickCount" "$EndDownloadPhaseTickCount" $1
1017
1018    ; Get the seconds elapsed from the end of the download phase to the
1019    ; completion of the pre-installation check phase.
1020    ${GetSecondsElapsed} "$EndDownloadPhaseTickCount" "$EndPreInstallPhaseTickCount" $2
1021
1022    ; Get the seconds elapsed from the end of the pre-installation check phase
1023    ; to the completion of the installation phase.
1024    ${GetSecondsElapsed} "$EndPreInstallPhaseTickCount" "$EndInstallPhaseTickCount" $3
1025
1026    ; Get the seconds elapsed from the end of the installation phase to the
1027    ; completion of all phases.
1028    ${GetSecondsElapsed} "$EndInstallPhaseTickCount" "$EndFinishPhaseTickCount" $4
1029
1030    ${If} $ArchToInstall == ${ARCH_AMD64}
1031    ${OrIf} $ArchToInstall == ${ARCH_AARCH64}
1032      StrCpy $R0 "1"
1033    ${Else}
1034      StrCpy $R0 "0"
1035    ${EndIf}
1036
1037    ${If} ${IsNativeAMD64}
1038    ${OrIf} ${IsNativeARM64}
1039      StrCpy $R1 "1"
1040    ${Else}
1041      StrCpy $R1 "0"
1042    ${EndIf}
1043
1044    ; Though these values are sometimes incorrect due to bug 444664 it happens
1045    ; so rarely it isn't worth working around it by reading the registry values.
1046    ${WinVerGetMajor} $5
1047    ${WinVerGetMinor} $6
1048    ${WinVerGetBuild} $7
1049    ${WinVerGetServicePackLevel} $8
1050    ${If} ${IsServerOS}
1051      StrCpy $9 "1"
1052    ${Else}
1053      StrCpy $9 "0"
1054    ${EndIf}
1055
1056    ${If} "$ExitCode" == "${ERR_SUCCESS}"
1057      ReadINIStr $R5 "$INSTDIR\application.ini" "App" "Version"
1058      ReadINIStr $R6 "$INSTDIR\application.ini" "App" "BuildID"
1059    ${Else}
1060      StrCpy $R5 "0"
1061      StrCpy $R6 "0"
1062    ${EndIf}
1063
1064    ; Whether installed into the default installation directory
1065    ${GetLongPath} "$INSTDIR" $R7
1066    ${GetLongPath} "$InitialInstallDir" $R8
1067    ${If} "$R7" == "$R8"
1068      StrCpy $R7 "1"
1069    ${Else}
1070      StrCpy $R7 "0"
1071    ${EndIf}
1072
1073    ClearErrors
1074    WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
1075                     "Write Test"
1076    ${If} ${Errors}
1077      StrCpy $R8 "0"
1078    ${Else}
1079      DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
1080      StrCpy $R8 "1"
1081    ${EndIf}
1082
1083    ${If} "$DownloadServerIP" == ""
1084      StrCpy $DownloadServerIP "Unknown"
1085    ${EndIf}
1086
1087    StrCpy $R2 ""
1088    SetShellVarContext current ; Set SHCTX to the current user
1089    ReadRegStr $R2 HKCU "Software\Classes\http\shell\open\command" ""
1090    ${If} $R2 != ""
1091      ${GetPathFromString} "$R2" $R2
1092      ${GetParent} "$R2" $R3
1093      ${GetLongPath} "$R3" $R3
1094      ${If} $R3 == $INSTDIR
1095        StrCpy $R2 "1" ; This Firefox install is set as default.
1096      ${Else}
1097        StrCpy $R2 "$R2" "" -11 # length of firefox.exe
1098        ${If} "$R2" == "${FileMainEXE}"
1099          StrCpy $R2 "2" ; Another Firefox install is set as default.
1100        ${Else}
1101          StrCpy $R2 "0"
1102        ${EndIf}
1103      ${EndIf}
1104    ${Else}
1105      StrCpy $R2 "0" ; Firefox is not set as default.
1106    ${EndIf}
1107
1108    ${If} "$R2" == "0"
1109      StrCpy $R3 ""
1110      ReadRegStr $R2 HKLM "Software\Classes\http\shell\open\command" ""
1111      ${If} $R2 != ""
1112        ${GetPathFromString} "$R2" $R2
1113        ${GetParent} "$R2" $R3
1114        ${GetLongPath} "$R3" $R3
1115        ${If} $R3 == $INSTDIR
1116          StrCpy $R2 "1" ; This Firefox install is set as default.
1117        ${Else}
1118          StrCpy $R2 "$R2" "" -11 # length of firefox.exe
1119          ${If} "$R2" == "${FileMainEXE}"
1120            StrCpy $R2 "2" ; Another Firefox install is set as default.
1121          ${Else}
1122            StrCpy $R2 "0"
1123          ${EndIf}
1124        ${EndIf}
1125      ${Else}
1126        StrCpy $R2 "0" ; Firefox is not set as default.
1127      ${EndIf}
1128    ${EndIf}
1129
1130    ${If} $CanSetAsDefault == "true"
1131      ${If} $CheckboxSetAsDefault == "1"
1132        StrCpy $R3 "2"
1133      ${Else}
1134        StrCpy $R3 "3"
1135      ${EndIf}
1136    ${Else}
1137      ${If} ${AtLeastWin8}
1138        StrCpy $R3 "1"
1139      ${Else}
1140        StrCpy $R3 "0"
1141      ${EndIf}
1142    ${EndIf}
1143
1144!ifdef STUB_DEBUG
1145    MessageBox MB_OK "${BaseURLStubPing} \
1146                      $\nStub URL Version = ${StubURLVersion}${StubURLVersionAppend} \
1147                      $\nBuild Channel = ${Channel} \
1148                      $\nUpdate Channel = ${UpdateChannel} \
1149                      $\nLocale = ${AB_CD} \
1150                      $\nFirefox x64 = $R0 \
1151                      $\nRunning x64 Windows = $R1 \
1152                      $\nMajor = $5 \
1153                      $\nMinor = $6 \
1154                      $\nBuild = $7 \
1155                      $\nServicePack = $8 \
1156                      $\nIsServer = $9 \
1157                      $\nExit Code = $ExitCode \
1158                      $\nFirefox Launch Code = $FirefoxLaunchCode \
1159                      $\nDownload Retry Count = $DownloadRetryCount \
1160                      $\nDownloaded Bytes = $DownloadedBytes \
1161                      $\nDownload Size Bytes = $DownloadSizeBytes \
1162                      $\nIntroduction Phase Seconds = $IntroPhaseSeconds \
1163                      $\nOptions Phase Seconds = $OptionsPhaseSeconds \
1164                      $\nDownload Phase Seconds = $0 \
1165                      $\nLast Download Seconds = $1 \
1166                      $\nDownload First Transfer Seconds = $DownloadFirstTransferSeconds \
1167                      $\nPreinstall Phase Seconds = $2 \
1168                      $\nInstall Phase Seconds = $3 \
1169                      $\nFinish Phase Seconds = $4 \
1170                      $\nInitial Install Requirements Code = $InitialInstallRequirementsCode \
1171                      $\nOpened Download Page = $OpenedDownloadPage \
1172                      $\nExisting Profile = $ExistingProfile \
1173                      $\nExisting Version = $ExistingVersion \
1174                      $\nExisting Build ID = $ExistingBuildID \
1175                      $\nNew Version = $R5 \
1176                      $\nNew Build ID = $R6 \
1177                      $\nDefault Install Dir = $R7 \
1178                      $\nHas Admin = $R8 \
1179                      $\nDefault Status = $R2 \
1180                      $\nSet As Sefault Status = $R3 \
1181                      $\nDownload Server IP = $DownloadServerIP \
1182                      $\nPost-Signing Data = $PostSigningData \
1183                      $\nProfile cleanup prompt shown = $ProfileCleanupPromptType \
1184                      $\nDid profile cleanup = $CheckboxCleanupProfile"
1185    ; The following will exit the installer
1186    SetAutoClose true
1187    StrCpy $R9 "2"
1188    Call RelativeGotoPage
1189!else
1190    ${StartTimer} ${DownloadIntervalMS} OnPing
1191    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" \
1192                  "$PLUGINSDIR\_temp" /END
1193!endif
1194  ${Else}
1195    ${If} "$IsDownloadFinished" == "false"
1196      ; Cancel the download in progress
1197      InetBgDL::Get /RESET /END
1198    ${EndIf}
1199    ; The following will exit the installer
1200    SetAutoClose true
1201    StrCpy $R9 "2"
1202    Call RelativeGotoPage
1203  ${EndIf}
1204FunctionEnd
1205
1206Function OnPing
1207  InetBgDL::GetStats
1208  # $0 = HTTP status code, 0=Completed
1209  # $1 = Completed files
1210  # $2 = Remaining files
1211  # $3 = Number of downloaded bytes for the current file
1212  # $4 = Size of current file (Empty string if the size is unknown)
1213  # /RESET must be used if status $0 > 299 (e.g. failure)
1214  # When status is $0 =< 299 it is handled by InetBgDL
1215  ${If} $2 == 0
1216  ${OrIf} $0 > 299
1217    WebBrowser::CancelTimer $TimerHandle
1218    ${If} $0 > 299
1219      InetBgDL::Get /RESET /END
1220    ${EndIf}
1221    ; The following will exit the installer
1222    SetAutoClose true
1223    StrCpy $R9 "2"
1224    Call RelativeGotoPage
1225  ${EndIf}
1226FunctionEnd
1227
1228Function CheckInstall
1229  IntOp $InstallCounterStep $InstallCounterStep + 1
1230  ${If} $InstallCounterStep >= $InstallTotalSteps
1231    WebBrowser::CancelTimer $TimerHandle
1232    ; Close the handle that prevents modification of the full installer
1233    System::Call 'kernel32::CloseHandle(i $HandleDownload)'
1234    StrCpy $ExitCode "${ERR_INSTALL_TIMEOUT}"
1235    ; Use a timer so the UI has a chance to update
1236    ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
1237    Return
1238  ${EndIf}
1239
1240  ${If} $ProgressCompleted < ${PROGRESS_BAR_INSTALL_END_STEP}
1241    IntOp $0 ${PROGRESS_BAR_INSTALL_END_STEP} - ${PROGRESS_BAR_DOWNLOAD_END_STEP}
1242    IntOp $0 $InstallCounterStep * $0
1243    IntOp $0 $0 / $InstallTotalSteps
1244    IntOp $ProgressCompleted ${PROGRESS_BAR_DOWNLOAD_END_STEP} + $0
1245    Call SetProgressBars
1246  ${EndIf}
1247
1248  ${If} ${FileExists} "$INSTDIR\install.log"
1249    Delete "$INSTDIR\install.tmp"
1250    CopyFiles /SILENT "$INSTDIR\install.log" "$INSTDIR\install.tmp"
1251
1252    ; The unfocus and refocus that happens approximately here is caused by the
1253    ; installer calling RefreshShellIcons to refresh the shortcut icons.
1254
1255    ; When the full installer completes the installation the install.log will no
1256    ; longer be in use.
1257    ClearErrors
1258    Delete "$INSTDIR\install.log"
1259    ${Unless} ${Errors}
1260      WebBrowser::CancelTimer $TimerHandle
1261      ; Close the handle that prevents modification of the full installer
1262      System::Call 'kernel32::CloseHandle(i $HandleDownload)'
1263      Rename "$INSTDIR\install.tmp" "$INSTDIR\install.log"
1264      Delete "$PLUGINSDIR\download.exe"
1265      Delete "$PLUGINSDIR\${CONFIG_INI}"
1266      System::Call "kernel32::GetTickCount()l .s"
1267      Pop $EndInstallPhaseTickCount
1268      Call FinishInstall
1269    ${EndUnless}
1270  ${EndIf}
1271FunctionEnd
1272
1273Function FinishInstall
1274  StrCpy $ProgressCompleted "${PROGRESS_BAR_INSTALL_END_STEP}"
1275  Call SetProgressBars
1276
1277  ${If} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade"
1278    Delete "$INSTDIR\${FileMainEXE}"
1279    Rename "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}"
1280  ${EndIf}
1281
1282  StrCpy $ExitCode "${ERR_SUCCESS}"
1283
1284  ${CopyPostSigningData}
1285  Pop $PostSigningData
1286
1287  Call LaunchApp
1288FunctionEnd
1289
1290Function RelativeGotoPage
1291  IntCmp $R9 0 0 Move Move
1292  StrCmp $R9 "X" 0 Move
1293  StrCpy $R9 "120"
1294
1295  Move:
1296  SendMessage $HWNDPARENT "0x408" "$R9" ""
1297FunctionEnd
1298
1299Function CheckSpace
1300  ${If} "$ExistingTopDir" != ""
1301    StrLen $0 "$ExistingTopDir"
1302    StrLen $1 "$INSTDIR"
1303    ${If} $0 <= $1
1304      StrCpy $2 "$INSTDIR" $3
1305      ${If} "$2" == "$ExistingTopDir"
1306        Return
1307      ${EndIf}
1308    ${EndIf}
1309  ${EndIf}
1310
1311  StrCpy $ExistingTopDir "$INSTDIR"
1312  ${DoUntil} ${FileExists} "$ExistingTopDir"
1313    ${GetParent} "$ExistingTopDir" $ExistingTopDir
1314    ${If} "$ExistingTopDir" == ""
1315      StrCpy $SpaceAvailableBytes "0"
1316      StrCpy $HasRequiredSpaceAvailable "false"
1317      Return
1318    ${EndIf}
1319  ${Loop}
1320
1321  ${GetLongPath} "$ExistingTopDir" $ExistingTopDir
1322
1323  ; GetDiskFreeSpaceExW requires a backslash.
1324  StrCpy $0 "$ExistingTopDir" "" -1 ; the last character
1325  ${If} "$0" != "\"
1326    StrCpy $0 "\"
1327  ${Else}
1328    StrCpy $0 ""
1329  ${EndIf}
1330
1331  System::Call 'kernel32::GetDiskFreeSpaceExW(w, *l, *l, *l) i("$ExistingTopDir$0", .r1, .r2, .r3) .'
1332  StrCpy $SpaceAvailableBytes "$1"
1333
1334  System::Int64Op $SpaceAvailableBytes / 1048576
1335  Pop $1
1336  System::Int64Op $1 > ${APPROXIMATE_REQUIRED_SPACE_MB}
1337  Pop $1
1338  ${If} $1 == 1
1339    StrCpy $HasRequiredSpaceAvailable "true"
1340  ${Else}
1341    StrCpy $HasRequiredSpaceAvailable "false"
1342  ${EndIf}
1343FunctionEnd
1344
1345Function CanWrite
1346  StrCpy $CanWriteToInstallDir "false"
1347
1348  StrCpy $0 "$INSTDIR"
1349  ; Use the existing directory when it exists
1350  ${Unless} ${FileExists} "$INSTDIR"
1351    ; Get the topmost directory that exists for new installs
1352    ${DoUntil} ${FileExists} "$0"
1353      ${GetParent} "$0" $0
1354      ${If} "$0" == ""
1355        Return
1356      ${EndIf}
1357    ${Loop}
1358  ${EndUnless}
1359
1360  GetTempFileName $2 "$0"
1361  Delete $2
1362  CreateDirectory "$2"
1363
1364  ${If} ${FileExists} "$2"
1365    ${If} ${FileExists} "$INSTDIR"
1366      GetTempFileName $3 "$INSTDIR"
1367    ${Else}
1368      GetTempFileName $3 "$2"
1369    ${EndIf}
1370    ${If} ${FileExists} "$3"
1371      Delete "$3"
1372      StrCpy $CanWriteToInstallDir "true"
1373    ${EndIf}
1374    RmDir "$2"
1375  ${EndIf}
1376FunctionEnd
1377
1378Function LaunchApp
1379  StrCpy $FirefoxLaunchCode "2"
1380
1381  ; Set the current working directory to the installation directory
1382  SetOutPath "$INSTDIR"
1383  ClearErrors
1384  ${GetParameters} $0
1385  ${GetOptions} "$0" "/UAC:" $1
1386  ${If} ${Errors}
1387    ${If} $CheckboxCleanupProfile == 1
1388      ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration -first-startup"
1389    ${Else}
1390      ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -first-startup"
1391    ${EndIf}
1392  ${Else}
1393    StrCpy $R1 $CheckboxCleanupProfile
1394    GetFunctionAddress $0 LaunchAppFromElevatedProcess
1395    UAC::ExecCodeSegment $0
1396  ${EndIf}
1397
1398  StrCpy $AppLaunchWaitTickCount 0
1399  ${StartTimer} ${AppLaunchWaitIntervalMS} WaitForAppLaunch
1400FunctionEnd
1401
1402Function LaunchAppFromElevatedProcess
1403  ; Set the current working directory to the installation directory
1404  SetOutPath "$INSTDIR"
1405  ${If} $R1 == 1
1406    ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration -first-startup"
1407  ${Else}
1408    ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -first-startup"
1409  ${EndIf}
1410FunctionEnd
1411
1412Function WaitForAppLaunch
1413  FindWindow $0 "${MainWindowClass}"
1414  FindWindow $1 "${DialogWindowClass}"
1415  ${If} $0 <> 0
1416  ${OrIf} $1 <> 0
1417    WebBrowser::CancelTimer $TimerHandle
1418    StrCpy $ProgressCompleted "${PROGRESS_BAR_APP_LAUNCH_END_STEP}"
1419    Call SetProgressBars
1420    Call SendPing
1421    Return
1422  ${EndIf}
1423
1424  IntOp $AppLaunchWaitTickCount $AppLaunchWaitTickCount + 1
1425  IntOp $0 $AppLaunchWaitTickCount * ${AppLaunchWaitIntervalMS}
1426  ${If} $0 >= ${AppLaunchWaitTimeoutMS}
1427    ; We've waited an unreasonably long time, so just exit.
1428    WebBrowser::CancelTimer $TimerHandle
1429    Call SendPing
1430    Return
1431  ${EndIf}
1432
1433  ${If} $ProgressCompleted < ${PROGRESS_BAR_APP_LAUNCH_END_STEP}
1434    IntOp $ProgressCompleted $ProgressCompleted + 1
1435    Call SetProgressBars
1436  ${EndIf}
1437FunctionEnd
1438
1439Function DisplayDownloadError
1440  WebBrowser::CancelTimer $TimerHandle
1441  ; To better display the error state on the taskbar set the progress completed
1442  ; value to the total value.
1443  ${ITBL3SetProgressValue} "100" "100"
1444  ${ITBL3SetProgressState} "${TBPF_ERROR}"
1445
1446  MessageBox MB_OKCANCEL|MB_ICONSTOP "$(ERROR_DOWNLOAD_CONT)" IDCANCEL +2 IDOK +1
1447  Call LaunchHelpPage
1448  Call SendPing
1449FunctionEnd
1450
1451Function LaunchHelpPage
1452  StrCpy $OpenedDownloadPage "1" ; Already initialized to 0
1453  ClearErrors
1454  ${GetParameters} $0
1455  ${GetOptions} "$0" "/UAC:" $1
1456  ${If} ${Errors}
1457    Call OpenManualDownloadURL
1458  ${Else}
1459    GetFunctionAddress $0 OpenManualDownloadURL
1460    UAC::ExecCodeSegment $0
1461  ${EndIf}
1462FunctionEnd
1463
1464Function OpenManualDownloadURL
1465  ClearErrors
1466  ReadINIStr $0 "${PARTNER_INI}" "DownloadURL" "FallbackPage"
1467  ${IfNot} ${Errors}
1468    ExecShell "open" "$0"
1469  ${Else}
1470    ExecShell "open" "${URLManualDownload}${URLManualDownloadAppend}"
1471  ${EndIf}
1472FunctionEnd
1473
1474Function ShouldPromptForProfileCleanup
1475  ; This will be our return value.
1476  StrCpy $ProfileCleanupPromptType 0
1477
1478  ; Only consider installations of the same architecture we're installing.
1479  ${If} $ArchToInstall == ${ARCH_AMD64}
1480  ${OrIf} $ArchToInstall == ${ARCH_AARCH64}
1481    SetRegView 64
1482  ${Else}
1483    SetRegView 32
1484  ${EndIf}
1485
1486  ; Make sure $APPDATA is the user's AppData and not ProgramData.
1487  ; We'll set this back to all at the end of the function.
1488  SetShellVarContext current
1489
1490  ${FindInstallSpecificProfile}
1491  Pop $R0
1492
1493  ${If} $R0 == ""
1494    ; We don't have an install-specific profile, so look for an old-style
1495    ; default profile instead by checking each numbered Profile section.
1496    StrCpy $0 0
1497    ${Do}
1498      ClearErrors
1499      ; Check if the section exists by reading a value that must be present.
1500      ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Path"
1501      ${If} ${Errors}
1502        ; We've run out of profile sections.
1503        ${Break}
1504      ${EndIf}
1505
1506      ClearErrors
1507      ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Default"
1508      ${IfNot} ${Errors}
1509      ${AndIf} $1 == "1"
1510        ; We've found the default profile
1511        ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Path"
1512        ReadINIStr $2 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "IsRelative"
1513        ${If} $2 == "1"
1514          StrCpy $R0 "$APPDATA\Mozilla\Firefox\$1"
1515        ${Else}
1516          StrCpy $R0 "$1"
1517        ${EndIf}
1518        ${Break}
1519      ${EndIf}
1520
1521      IntOp $0 $0 + 1
1522    ${Loop}
1523  ${EndIf}
1524
1525  GetFullPathName $R0 $R0
1526
1527  ${If} $R0 == ""
1528    ; No profile to clean up, so don't show the cleanup prompt.
1529    GoTo end
1530  ${EndIf}
1531
1532  ; We have at least one profile present. If we don't have any installations,
1533  ; then we need to show the re-install prompt. We'll say there's an
1534  ; installation present if HKCR\FirefoxURL* exists and points to a real path.
1535  StrCpy $0 0
1536  StrCpy $R9 ""
1537  ${Do}
1538    ClearErrors
1539    EnumRegKey $1 HKCR "" $0
1540    ${If} ${Errors}
1541    ${OrIf} $1 == ""
1542      ${Break}
1543    ${EndIf}
1544    ${WordFind} "$1" "-" "+1{" $2
1545    ${If} $2 == "FirefoxURL"
1546      ClearErrors
1547      ReadRegStr $2 HKCR "$1\DefaultIcon" ""
1548      ${IfNot} ${Errors}
1549        ${GetPathFromString} $2 $1
1550        ${If} ${FileExists} $1
1551          StrCpy $R9 $1
1552          ${Break}
1553        ${EndIf}
1554      ${EndIf}
1555    ${EndIf}
1556    IntOp $0 $0 + 1
1557  ${Loop}
1558  ${If} $R9 == ""
1559    StrCpy $ProfileCleanupPromptType 1
1560    GoTo end
1561  ${EndIf}
1562
1563  ; Okay, there's at least one install, let's see if it's for this channel.
1564  SetShellVarContext all
1565  ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
1566  ${If} $0 == "false"
1567    SetShellVarContext current
1568    ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
1569    ${If} $0 == "false"
1570      ; Existing installs are not for this channel. Don't show any prompt.
1571      GoTo end
1572    ${EndIf}
1573  ${EndIf}
1574
1575  ; Find out what version the default profile was last used on.
1576  ${If} ${FileExists} "$R0\compatibility.ini"
1577    ClearErrors
1578    ReadINIStr $0 "$R0\compatibility.ini" "Compatibility" "LastVersion"
1579    ${If} ${Errors}
1580      GoTo end
1581    ${EndIf}
1582    ${WordFind} $0 "." "+1{" $0
1583
1584    ; We don't know what version we're about to install because we haven't
1585    ; downloaded it yet. Find out what the latest version released on this
1586    ; channel is and assume we'll be installing that one.
1587    Call GetLatestReleasedVersion
1588    ${If} ${Errors}
1589      ; Use this stub installer's version as a fallback when we can't get the
1590      ; real current version; this may be behind, but it's better than nothing.
1591      StrCpy $1 ${AppVersion}
1592    ${EndIf}
1593
1594    ${WordFind} $1 "." "+1{" $1
1595    IntOp $1 $1 - 2
1596
1597    ${If} $1 > $0
1598      ; Default profile was last used more than two versions ago, so we need
1599      ; to show the paveover version of the profile cleanup prompt.
1600      StrCpy $ProfileCleanupPromptType 2
1601    ${EndIf}
1602  ${EndIf}
1603
1604  end:
1605  SetRegView lastused
1606  SetShellVarContext all
1607FunctionEnd
1608
1609Function GetLatestReleasedVersion
1610  ClearErrors
1611  Push $0 ; InetBgDl::GetStats uses $0 for the HTTP error code
1612  ; $1 is our return value, so don't save it
1613  Push $2 ; InetBgDl::GetStats uses $2 to tell us when the transfer is done
1614  Push $3 ; $3 - $5 are also set by InetBgDl::GetStats, but we don't use them
1615  Push $4
1616  Push $5
1617  Push $6 ; This is our response timeout counter
1618
1619  InetBgDL::Get /RESET /END
1620  InetBgDL::Get "https://product-details.mozilla.org/1.0/firefox_versions.json" \
1621                "$PLUGINSDIR\firefox_versions.json" \
1622                /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
1623
1624  ; Wait for the response, but only give it half a second since this is on the
1625  ; installer startup path (we haven't even shown a window yet).
1626  StrCpy $6 0
1627  ${Do}
1628    Sleep 100
1629    InetBgDL::GetStats
1630    IntOp $6 $6 + 1
1631
1632    ${If} $2 == 0
1633      ${Break}
1634    ${ElseIf} $6 >= 5
1635      InetBgDL::Get /RESET /END
1636      SetErrors
1637      GoTo end
1638    ${EndIf}
1639  ${Loop}
1640
1641  StrCpy $1 0
1642  nsJSON::Set /file "$PLUGINSDIR\firefox_versions.json"
1643  IfErrors end
1644  ${Select} ${Channel}
1645  ${Case} "unofficial"
1646    StrCpy $1 "FIREFOX_NIGHTLY"
1647  ${Case} "nightly"
1648    StrCpy $1 "FIREFOX_NIGHTLY"
1649  ${Case} "aurora"
1650    StrCpy $1 "FIREFOX_DEVEDITION"
1651  ${Case} "beta"
1652    StrCpy $1 "LATEST_FIREFOX_RELEASED_DEVEL_VERSION"
1653  ${Case} "release"
1654    StrCpy $1 "LATEST_FIREFOX_VERSION"
1655  ${EndSelect}
1656  nsJSON::Get $1 /end
1657
1658  end:
1659  ${If} ${Errors}
1660  ${OrIf} $1 == 0
1661    SetErrors
1662    StrCpy $1 0
1663  ${Else}
1664    Pop $1
1665  ${EndIf}
1666
1667  Pop $6
1668  Pop $5
1669  Pop $4
1670  Pop $3
1671  Pop $2
1672  Pop $0
1673FunctionEnd
1674
1675; Determine which architecture build we should download and install.
1676; AArch64 is always selected if it's the native architecture of the machine.
1677; Otherwise, we check a few things to determine if AMD64 is appropriate:
1678; 1) Running a 64-bit OS (we've already checked the OS version).
1679; 2) An amount of RAM strictly greater than RAM_NEEDED_FOR_64BIT
1680; 3) No third-party products installed that cause issues with the 64-bit build.
1681;    Currently this includes Lenovo OneKey Theater and Lenovo Energy Management.
1682; We also make sure that the partner.ini file contains a download URL for the
1683; selected architecture, when a partner.ini file eixsts.
1684; If any of those checks fail, the 32-bit x86 build is selected.
1685Function GetArchToInstall
1686  StrCpy $ArchToInstall ${ARCH_X86}
1687
1688  ${If} ${IsNativeARM64}
1689    StrCpy $ArchToInstall ${ARCH_AARCH64}
1690    GoTo downloadUrlCheck
1691  ${EndIf}
1692
1693  ${IfNot} ${IsNativeAMD64}
1694    Return
1695  ${EndIf}
1696
1697  System::Call "*(i 64, i, l 0, l, l, l, l, l, l)p.r1"
1698  System::Call "Kernel32::GlobalMemoryStatusEx(p r1)"
1699  System::Call "*$1(i, i, l.r2, l, l, l, l, l, l)"
1700  System::Free $1
1701  ${If} $2 L<= ${RAM_NEEDED_FOR_64BIT}
1702    Return
1703  ${EndIf}
1704
1705  ; Lenovo OneKey Theater can theoretically be in a directory other than this
1706  ; one, because some installer versions let you change it, but it's unlikely.
1707  ${If} ${FileExists} "$PROGRAMFILES32\Lenovo\Onekey Theater\windowsapihookdll64.dll"
1708    Return
1709  ${EndIf}
1710
1711  ${If} ${FileExists} "$PROGRAMFILES32\Lenovo\Energy Management\Energy Management.exe"
1712    Return
1713  ${EndIf}
1714
1715  StrCpy $ArchToInstall ${ARCH_AMD64}
1716
1717  downloadUrlCheck:
1718  ; If we've selected an architecture that doesn't have a download URL in the
1719  ; partner.ini, but there is a URL there for 32-bit x86, then fall back to
1720  ; 32-bit x86 on the theory that we should never use a non-partner build if
1721  ; we are configured as a partner installer, even if the only build that's
1722  ; provided is suboptimal for the machine. If there isn't even an x86 URL,
1723  ; then we won't force x86 and GetDownloadURL will stick with the built-in URL.
1724  ClearErrors
1725  ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "X86"
1726  ${IfNot} ${Errors}
1727    ${If} $ArchToInstall == ${ARCH_AMD64}
1728      ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AMD64"
1729      ${If} ${Errors}
1730        StrCpy $ArchToInstall ${ARCH_X86}
1731      ${EndIf}
1732    ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
1733      ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AArch64"
1734      ${If} ${Errors}
1735        StrCpy $ArchToInstall ${ARCH_X86}
1736      ${EndIf}
1737    ${EndIf}
1738  ${EndIf}
1739FunctionEnd
1740
1741Function GetDownloadURL
1742  Push $0
1743  Push $1
1744
1745  ; Start with the appropriate URL from our built-in branding info.
1746  ${If} $ArchToInstall == ${ARCH_AMD64}
1747    StrCpy $0 "${URLStubDownloadAMD64}${URLStubDownloadAppend}"
1748  ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
1749    StrCpy $0 "${URLStubDownloadAArch64}${URLStubDownloadAppend}"
1750  ${Else}
1751    StrCpy $0 "${URLStubDownloadX86}${URLStubDownloadAppend}"
1752  ${EndIf}
1753
1754  ; If we have a partner.ini file then use the URL from there instead.
1755  ClearErrors
1756  ${If} $ArchToInstall == ${ARCH_AMD64}
1757    ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AMD64"
1758  ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
1759    ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AArch64"
1760  ${Else}
1761    ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "X86"
1762  ${EndIf}
1763  ${IfNot} ${Errors}
1764    StrCpy $0 "$1"
1765  ${EndIf}
1766
1767  Pop $1
1768  Exch $0
1769FunctionEnd
1770
1771Section
1772SectionEnd
1773