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 All Users application
433  ; directory and if the user can't write to that location set the installation
434  ; directory to a subdirectory of the users local application directory
435  ; (e.g. non-roaming).
436  Call CanWrite
437  ${If} "$CanWriteToInstallDir" == "false"
438    StrCpy $INSTDIR "$APPDATA\${BrandFullName}\"
439    Call CanWrite
440    ${If} "$CanWriteToInstallDir" == "false"
441      ; This should never happen but just in case.
442      StrCpy $CanWriteToInstallDir "false"
443    ${Else}
444      StrCpy $INSTDIR "$LOCALAPPDATA\${BrandFullName}\"
445      Call CanWrite
446    ${EndIf}
447  ${EndIf}
448
449  Call CheckSpace
450
451  ${If} ${FileExists} "$INSTDIR"
452    ; Always display the long path if the path exists.
453    ${GetLongPath} "$INSTDIR" $INSTDIR
454  ${EndIf}
455
456  ; Check whether the install requirements are satisfied using the default
457  ; values for metrics.
458  ${If} "$InitialInstallRequirementsCode" == ""
459    ${If} "$CanWriteToInstallDir" != "true"
460    ${AndIf} "$HasRequiredSpaceAvailable" != "true"
461      StrCpy $InitialInstallRequirementsCode "1"
462    ${ElseIf} "$CanWriteToInstallDir" != "true"
463      StrCpy $InitialInstallRequirementsCode "2"
464    ${ElseIf} "$HasRequiredSpaceAvailable" != "true"
465      StrCpy $InitialInstallRequirementsCode "3"
466    ${Else}
467      StrCpy $InitialInstallRequirementsCode "0"
468    ${EndIf}
469  ${EndIf}
470
471  Call CanWrite
472  ${If} "$CanWriteToInstallDir" == "false"
473    MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_WRITE_ACCESS_QUIT)$\n$\n$INSTDIR"
474    Quit
475  ${EndIf}
476
477  Call CheckSpace
478  ${If} "$HasRequiredSpaceAvailable" == "false"
479    MessageBox MB_OK|MB_ICONEXCLAMATION "$(WARN_DISK_SPACE_QUIT)"
480    Quit
481  ${EndIf}
482
483  ${InitHashAppModelId} "$INSTDIR" "Software\Mozilla\${AppName}\TaskBarIDs"
484
485  File /oname=$PLUGINSDIR\stub_common.css "stub_common.css"
486  File /oname=$PLUGINSDIR\stub_common.js "stub_common.js"
487FunctionEnd
488
489; .onGUIInit isn't needed except for RTL locales
490!ifdef ${AB_CD}_rtl
491Function .onGUIInit
492  ${MakeWindowRTL} $HWNDPARENT
493FunctionEnd
494!endif
495
496Function .onGUIEnd
497  Delete "$PLUGINSDIR\_temp"
498  Delete "$PLUGINSDIR\download.exe"
499  Delete "$PLUGINSDIR\${CONFIG_INI}"
500
501  ${UnloadUAC}
502FunctionEnd
503
504Function .onUserAbort
505  WebBrowser::CancelTimer $TimerHandle
506
507  ${If} "$IsDownloadFinished" != ""
508    ; Go ahead and cancel the download so it doesn't keep running while this
509    ; prompt is up. We'll resume it if the user decides to continue.
510    InetBgDL::Get /RESET /END
511
512    ${ShowTaskDialog} $(STUB_CANCEL_PROMPT_HEADING) \
513                      $(STUB_CANCEL_PROMPT_MESSAGE) \
514                      $(STUB_CANCEL_PROMPT_BUTTON_CONTINUE) \
515                      $(STUB_CANCEL_PROMPT_BUTTON_EXIT)
516    Pop $0
517    ${If} $0 == 1002
518      ; The cancel button was clicked
519      Call LaunchHelpPage
520      Call SendPing
521    ${Else}
522      ; Either the continue button was clicked or the dialog was dismissed
523      Call StartDownload
524    ${EndIf}
525  ${Else}
526    Call SendPing
527  ${EndIf}
528
529  ; Aborting the abort will allow SendPing to hide the installer window and
530  ; close the installer after it sends the metrics ping, or allow us to just go
531  ; back to installing if that's what the user selected.
532  Abort
533FunctionEnd
534
535!macro _RegisterAllCustomFunctions
536  GetFunctionAddress $0 getUIString
537  WebBrowser::RegisterCustomFunction $0 "getUIString"
538
539  GetFunctionAddress $0 getTextDirection
540  WebBrowser::RegisterCustomFunction $0 "getTextDirection"
541
542  GetFunctionAddress $0 getFontName
543  WebBrowser::RegisterCustomFunction $0 "getFontName"
544
545  GetFunctionAddress $0 getIsHighContrast
546  WebBrowser::RegisterCustomFunction $0 "getIsHighContrast"
547
548  GetFunctionAddress $0 gotoInstallPage
549  WebBrowser::RegisterCustomFunction $0 "gotoInstallPage"
550
551  GetFunctionAddress $0 getProgressBarPercent
552  WebBrowser::RegisterCustomFunction $0 "getProgressBarPercent"
553!macroend
554!define RegisterAllCustomFunctions "!insertmacro _RegisterAllCustomFunctions"
555
556!macro _StartTimer _INTERVAL_MS _FUNCTION_NAME
557  Push $0
558  GetFunctionAddress $0 ${_FUNCTION_NAME}
559  WebBrowser::CreateTimer $0 ${_INTERVAL_MS}
560  Pop $TimerHandle
561  Pop $0
562!macroend
563!define StartTimer "!insertmacro _StartTimer"
564
565Function gotoInstallPage
566  Pop $0
567  StrCpy $CheckboxCleanupProfile $0
568
569  StrCpy $R9 1
570  Call RelativeGotoPage
571  Push $0
572FunctionEnd
573
574Function getProgressBarPercent
575  ; Custom functions always get one parameter, which we don't use here.
576  ; But we will use $0 as a scratch accumulator register.
577  Pop $0
578  ; This math is getting the progess bar completion fraction and converting it
579  ; to a percentage, but we implement that with the operations in the reverse
580  ; of the intuitive order so that our integer math doesn't truncate to zero.
581  IntOp $0 $ProgressCompleted * 100
582  IntOp $0 $0 / ${PROGRESS_BAR_TOTAL_STEPS}
583  Push $0
584FunctionEnd
585
586Function getTextDirection
587  Pop $0
588  !ifdef ${AB_CD}_rtl
589    Push "rtl"
590  !else
591    Push "ltr"
592  !endif
593FunctionEnd
594
595Function getFontName
596  Pop $0
597  Push $FontFamilyName
598FunctionEnd
599
600Function getIsHighContrast
601  Pop $0
602  Push $UsingHighContrastMode
603FunctionEnd
604
605Function getUIString
606  Pop $0
607  ${Select} $0
608    ${Case} "cleanup_header"
609      ${If} $ProfileCleanupPromptType == 1
610        Push "$(STUB_CLEANUP_REINSTALL_HEADER2)"
611      ${Else}
612        Push "$(STUB_CLEANUP_PAVEOVER_HEADER2)"
613      ${EndIf}
614    ${Case} "cleanup_button"
615      ${If} $ProfileCleanupPromptType == 1
616        Push "$(STUB_CLEANUP_REINSTALL_BUTTON2)"
617      ${Else}
618        Push "$(STUB_CLEANUP_PAVEOVER_BUTTON2)"
619      ${EndIf}
620    ${Case} "cleanup_checkbox"
621      Push "$(STUB_CLEANUP_CHECKBOX_LABEL2)"
622    ${Case} "installing_header"
623      Push "$(STUB_INSTALLING_HEADLINE2)"
624    ${Case} "installing_label"
625      Push "$(STUB_INSTALLING_LABEL2)"
626    ${Case} "installing_content"
627      Push "$(STUB_INSTALLING_BODY2)"
628    ${Case} "installing_blurb_0"
629      Push "$(STUB_BLURB_FIRST1)"
630    ${Case} "installing_blurb_1"
631      Push "$(STUB_BLURB_SECOND1)"
632    ${Case} "installing_blurb_2"
633      Push "$(STUB_BLURB_THIRD1)"
634    ${Case} "global_footer"
635      Push "$(STUB_BLURB_FOOTER2)"
636    ${Default}
637      Push ""
638  ${EndSelect}
639FunctionEnd
640
641Function createProfileCleanup
642  Call ShouldPromptForProfileCleanup
643
644  ${If} $ProfileCleanupPromptType == 0
645    StrCpy $CheckboxCleanupProfile 0
646    Abort ; Skip this page
647  ${EndIf}
648
649  ${RegisterAllCustomFunctions}
650
651  File /oname=$PLUGINSDIR\profile_cleanup.html "profile_cleanup.html"
652  File /oname=$PLUGINSDIR\profile_cleanup_page.css "profile_cleanup_page.css"
653  File /oname=$PLUGINSDIR\profile_cleanup.js "profile_cleanup.js"
654  WebBrowser::ShowPage "$PLUGINSDIR\profile_cleanup.html"
655FunctionEnd
656
657Function createInstall
658  GetDlgItem $0 $HWNDPARENT 1 ; Install button
659  EnableWindow $0 0
660  ShowWindow $0 ${SW_HIDE}
661
662  GetDlgItem $0 $HWNDPARENT 3 ; Back button
663  EnableWindow $0 0
664  ShowWindow $0 ${SW_HIDE}
665
666  GetDlgItem $0 $HWNDPARENT 2 ; Cancel button
667  ; Hide the Cancel button, but don't disable it (or else it won't be possible
668  ; to close the window)
669  ShowWindow $0 ${SW_HIDE}
670
671  ; Get keyboard focus on the parent
672  System::Call "user32::SetFocus(p$HWNDPARENT)"
673
674  ; Set $DownloadReset to true so the first download tick count is measured.
675  StrCpy $DownloadReset "true"
676  StrCpy $IsDownloadFinished "false"
677  StrCpy $DownloadRetryCount "0"
678  StrCpy $DownloadedBytes "0"
679  StrCpy $StartLastDownloadTickCount ""
680  StrCpy $DownloadFirstTransferSeconds ""
681  StrCpy $OpenedDownloadPage "0"
682
683  ClearErrors
684  ReadINIStr $ExistingVersion "$INSTDIR\application.ini" "App" "Version"
685  ${If} ${Errors}
686    StrCpy $ExistingVersion "0"
687  ${EndIf}
688
689  ClearErrors
690  ReadINIStr $ExistingBuildID "$INSTDIR\application.ini" "App" "BuildID"
691  ${If} ${Errors}
692    StrCpy $ExistingBuildID "0"
693  ${EndIf}
694
695  ${If} ${FileExists} "$LOCALAPPDATA\Mozilla\Firefox"
696    StrCpy $ExistingProfile "1"
697  ${Else}
698    StrCpy $ExistingProfile "0"
699  ${EndIf}
700
701  StrCpy $DownloadServerIP ""
702
703  System::Call "kernel32::GetTickCount()l .s"
704  Pop $StartDownloadPhaseTickCount
705
706  ${If} ${FileExists} "$INSTDIR\uninstall\uninstall.log"
707    StrCpy $InstallTotalSteps ${InstallPaveOverTotalSteps}
708  ${Else}
709    StrCpy $InstallTotalSteps ${InstallCleanTotalSteps}
710  ${EndIf}
711
712  ${ITBL3Create}
713  ${ITBL3SetProgressState} "${TBPF_INDETERMINATE}"
714
715  ; Make sure the file we're about to try to download to doesn't already exist,
716  ; so we don't end up trying to "resume" on top of the wrong file.
717  Delete "$PLUGINSDIR\download.exe"
718
719  ${StartTimer} ${DownloadIntervalMS} StartDownload
720
721  ${RegisterAllCustomFunctions}
722
723  File /oname=$PLUGINSDIR\installing.html "installing.html"
724  File /oname=$PLUGINSDIR\installing_page.css "installing_page.css"
725  File /oname=$PLUGINSDIR\installing.js "installing.js"
726  WebBrowser::ShowPage "$PLUGINSDIR\installing.html"
727FunctionEnd
728
729Function StartDownload
730  WebBrowser::CancelTimer $TimerHandle
731
732  Call GetDownloadURL
733  Pop $0
734  InetBgDL::Get "$0" "$PLUGINSDIR\download.exe" \
735                /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
736
737  ${StartTimer} ${DownloadIntervalMS} OnDownload
738
739  ${If} ${FileExists} "$INSTDIR\${TO_BE_DELETED}"
740    RmDir /r "$INSTDIR\${TO_BE_DELETED}"
741  ${EndIf}
742FunctionEnd
743
744Function SetProgressBars
745  ${ITBL3SetProgressValue} "$ProgressCompleted" "${PROGRESS_BAR_TOTAL_STEPS}"
746FunctionEnd
747
748Function OnDownload
749  InetBgDL::GetStats
750  # $0 = HTTP status code, 0=Completed
751  # $1 = Completed files
752  # $2 = Remaining files
753  # $3 = Number of downloaded bytes for the current file
754  # $4 = Size of current file (Empty string if the size is unknown)
755  # /RESET must be used if status $0 > 299 (e.g. failure), even if resuming
756  # When status is $0 =< 299 it is handled by InetBgDL
757  StrCpy $DownloadServerIP "$5"
758  ${If} $0 > 299
759    WebBrowser::CancelTimer $TimerHandle
760    IntOp $DownloadRetryCount $DownloadRetryCount + 1
761    ${If} $DownloadRetryCount >= ${DownloadMaxRetries}
762      StrCpy $ExitCode "${ERR_DOWNLOAD_TOO_MANY_RETRIES}"
763      ; Use a timer so the UI has a chance to update
764      ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
765      Return
766    ${EndIf}
767
768    ; 1000 is a special code meaning InetBgDL lost the connection before it got
769    ; all the bytes it was expecting. We'll try to resume the transfer in that
770    ; case (assuming we aren't out of retries), so don't treat it as a reset
771    ; or clear the progress bar.
772    ${If} $0 != 1000
773      ${If} "$DownloadReset" != "true"
774        StrCpy $DownloadedBytes "0"
775        ${ITBL3SetProgressState} "${TBPF_INDETERMINATE}"
776      ${EndIf}
777      StrCpy $DownloadSizeBytes ""
778      StrCpy $DownloadReset "true"
779      Delete "$PLUGINSDIR\download.exe"
780    ${EndIf}
781
782    InetBgDL::Get /RESET /END
783    ${StartTimer} ${DownloadRetryIntervalMS} StartDownload
784    Return
785  ${EndIf}
786
787  ${If} "$DownloadReset" == "true"
788    System::Call "kernel32::GetTickCount()l .s"
789    Pop $StartLastDownloadTickCount
790    StrCpy $DownloadReset "false"
791    ; The seconds elapsed from the start of the download phase until the first
792    ; bytes are received are only recorded for the first request so it is
793    ; possible to determine connection issues for the first request.
794    ${If} "$DownloadFirstTransferSeconds" == ""
795      ; Get the seconds elapsed from the start of the download phase until the
796      ; first bytes are received.
797      ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$StartLastDownloadTickCount" $DownloadFirstTransferSeconds
798    ${EndIf}
799  ${EndIf}
800
801  ${If} "$DownloadSizeBytes" == ""
802  ${AndIf} "$4" != ""
803    StrCpy $DownloadSizeBytes "$4"
804    StrCpy $ProgressCompleted 0
805  ${EndIf}
806
807  ; Don't update the status until after the download starts
808  ${If} $2 != 0
809  ${AndIf} "$4" == ""
810    Return
811  ${EndIf}
812
813  ${If} $IsDownloadFinished != "true"
814    ${If} $2 == 0
815      WebBrowser::CancelTimer $TimerHandle
816      StrCpy $IsDownloadFinished "true"
817      System::Call "kernel32::GetTickCount()l .s"
818      Pop $EndDownloadPhaseTickCount
819
820      ${If} "$DownloadSizeBytes" == ""
821        ; It's possible for the download to finish before we were able to
822        ; get the size while it was downloading, and InetBgDL doesn't report
823        ; it afterwards. Use the size of the finished file.
824        ClearErrors
825        FileOpen $5 "$PLUGINSDIR\download.exe" r
826        ${IfNot} ${Errors}
827          FileSeek $5 0 END $DownloadSizeBytes
828          FileClose $5
829        ${EndIf}
830      ${EndIf}
831      StrCpy $DownloadedBytes "$DownloadSizeBytes"
832
833      ; Update the progress bars first in the UI change so they take affect
834      ; before other UI changes.
835      StrCpy $ProgressCompleted "${PROGRESS_BAR_DOWNLOAD_END_STEP}"
836      Call SetProgressBars
837
838      ; Disable the Cancel button during the install
839      GetDlgItem $5 $HWNDPARENT 2
840      EnableWindow $5 0
841
842      ; Open a handle to prevent modification of the full installer
843      StrCpy $R9 "${INVALID_HANDLE_VALUE}"
844      System::Call 'kernel32::CreateFileW(w "$PLUGINSDIR\download.exe", \
845                                          i ${GENERIC_READ}, \
846                                          i ${FILE_SHARE_READ}, i 0, \
847                                          i ${OPEN_EXISTING}, i 0, i 0) i .R9'
848      StrCpy $HandleDownload "$R9"
849
850      ${If} $HandleDownload == ${INVALID_HANDLE_VALUE}
851        StrCpy $ExitCode "${ERR_PREINSTALL_INVALID_HANDLE}"
852        System::Call "kernel32::GetTickCount()l .s"
853        Pop $EndPreInstallPhaseTickCount
854        ; Use a timer so the UI has a chance to update
855        ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
856      ${Else}
857        CertCheck::CheckPETrustAndInfoAsync "$PLUGINSDIR\download.exe" \
858          "${CertNameDownload}" "${CertIssuerDownload}"
859        ${StartTimer} ${DownloadIntervalMS} OnCertCheck
860      ${EndIf}
861    ${Else}
862      StrCpy $DownloadedBytes "$3"
863      System::Int64Op $DownloadedBytes * ${PROGRESS_BAR_DOWNLOAD_END_STEP}
864      Pop $ProgressCompleted
865      System::Int64Op $ProgressCompleted / $DownloadSizeBytes
866      Pop $ProgressCompleted
867      Call SetProgressBars
868    ${EndIf}
869  ${EndIf}
870FunctionEnd
871
872Function OnCertCheck
873  System::Call "kernel32::GetTickCount()l .s"
874  Pop $EndPreInstallPhaseTickCount
875
876  CertCheck::GetStatus
877  Pop $0
878  ${If} $0 == 0
879    ${GetSecondsElapsed} "$EndDownloadPhaseTickCount" "$EndPreInstallPhaseTickCount" $0
880    ${If} $0 >= ${PreinstallCertCheckMaxWaitSec}
881      WebBrowser::CancelTimer $TimerHandle
882      StrCpy $ExitCode "${ERR_PREINSTALL_CERT_TIMEOUT}"
883      ; Use a timer so the UI has a chance to update
884      ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
885    ${EndIf}
886    Return
887  ${EndIf}
888  Pop $0
889  Pop $1
890
891  ${If} $0 == 0
892  ${AndIf} $1 == 0
893    StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED_AND_ATTRIBUTES}"
894  ${ElseIf} $0 == 0
895    StrCpy $ExitCode "${ERR_PREINSTALL_CERT_UNTRUSTED}"
896  ${ElseIf} $1 == 0
897    StrCpy $ExitCode "${ERR_PREINSTALL_CERT_ATTRIBUTES}"
898  ${EndIf}
899
900  WebBrowser::CancelTimer $TimerHandle
901
902  ${If} $0 == 0
903  ${OrIf} $1 == 0
904    ; Use a timer so the UI has a chance to update
905    ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
906    Return
907  ${EndIf}
908
909  Call LaunchFullInstaller
910FunctionEnd
911
912Function LaunchFullInstaller
913  ; Instead of extracting the files we use the downloaded installer to
914  ; install in case it needs to perform operations that the stub doesn't
915  ; know about.
916  WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "InstallDirectoryPath" "$INSTDIR"
917  ; Don't create the QuickLaunch or Taskbar shortcut from the launched installer
918  WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "QuickLaunchShortcut" "false"
919
920  ; Always create a start menu shortcut, so the user always has some way
921  ; to access the application.
922  WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "StartMenuShortcuts" "true"
923
924  ; Either avoid or force adding a taskbar pin and desktop shortcut
925  ; based on the checkbox value.
926  ${If} $CheckboxShortcuts == 0
927    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "false"
928    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "false"
929  ${Else}
930    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "TaskbarShortcut" "true"
931    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "DesktopShortcut" "true"
932  ${EndIf}
933
934!ifdef MOZ_MAINTENANCE_SERVICE
935  ${If} $CheckboxInstallMaintSvc == 1
936    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "true"
937  ${Else}
938    WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
939  ${EndIf}
940!else
941  WriteINIStr "$PLUGINSDIR\${CONFIG_INI}" "Install" "MaintenanceService" "false"
942!endif
943
944  ; Delete the taskbar shortcut history to ensure we do the right thing based on
945  ; the config file above.
946  ${GetShortcutsLogPath} $0
947  Delete "$0"
948
949  ${RemovePrecompleteEntries} "false"
950
951  ; Delete the install.log and let the full installer create it. When the
952  ; installer closes it we can detect that it has completed.
953  Delete "$INSTDIR\install.log"
954
955  ; Delete firefox.exe.moz-upgrade and firefox.exe.moz-delete if it exists
956  ; since it being present will require an OS restart for the full
957  ; installer.
958  Delete "$INSTDIR\${FileMainEXE}.moz-upgrade"
959  Delete "$INSTDIR\${FileMainEXE}.moz-delete"
960
961  System::Call "kernel32::GetTickCount()l .s"
962  Pop $EndPreInstallPhaseTickCount
963
964  Exec "$\"$PLUGINSDIR\download.exe$\" /LaunchedFromStub /INI=$PLUGINSDIR\${CONFIG_INI}"
965  ${StartTimer} ${InstallIntervalMS} CheckInstall
966FunctionEnd
967
968Function SendPing
969  HideWindow
970
971  ${If} $CheckboxSendPing == 1
972    ; Get the tick count for the completion of all phases.
973    System::Call "kernel32::GetTickCount()l .s"
974    Pop $EndFinishPhaseTickCount
975
976    ; When the value of $IsDownloadFinished is false the download was started
977    ; but didn't finish. In this case the tick count stored in
978    ; $EndFinishPhaseTickCount is used to determine how long the download was
979    ; in progress.
980    ${If} "$IsDownloadFinished" == "false"
981      StrCpy $EndDownloadPhaseTickCount "$EndFinishPhaseTickCount"
982      ; Cancel the download in progress
983      InetBgDL::Get /RESET /END
984    ${EndIf}
985
986
987    ; When $DownloadFirstTransferSeconds equals an empty string the download
988    ; never successfully started so set the value to 0. It will be possible to
989    ; determine that the download didn't successfully start from the seconds for
990    ; the last download.
991    ${If} "$DownloadFirstTransferSeconds" == ""
992      StrCpy $DownloadFirstTransferSeconds "0"
993    ${EndIf}
994
995    ; When $StartLastDownloadTickCount equals an empty string the download never
996    ; successfully started so set the value to $EndDownloadPhaseTickCount to
997    ; compute the correct value.
998    ${If} $StartLastDownloadTickCount == ""
999      ; This could happen if the download never successfully starts
1000      StrCpy $StartLastDownloadTickCount "$EndDownloadPhaseTickCount"
1001    ${EndIf}
1002
1003    ; When $EndPreInstallPhaseTickCount equals 0 the installation phase was
1004    ; never completed so set its value to $EndFinishPhaseTickCount to compute
1005    ; the correct value.
1006    ${If} "$EndPreInstallPhaseTickCount" == "0"
1007      StrCpy $EndPreInstallPhaseTickCount "$EndFinishPhaseTickCount"
1008    ${EndIf}
1009
1010    ; When $EndInstallPhaseTickCount equals 0 the installation phase was never
1011    ; completed so set its value to $EndFinishPhaseTickCount to compute the
1012    ; correct value.
1013    ${If} "$EndInstallPhaseTickCount" == "0"
1014      StrCpy $EndInstallPhaseTickCount "$EndFinishPhaseTickCount"
1015    ${EndIf}
1016
1017    ; Get the seconds elapsed from the start of the download phase to the end of
1018    ; the download phase.
1019    ${GetSecondsElapsed} "$StartDownloadPhaseTickCount" "$EndDownloadPhaseTickCount" $0
1020
1021    ; Get the seconds elapsed from the start of the last download to the end of
1022    ; the last download.
1023    ${GetSecondsElapsed} "$StartLastDownloadTickCount" "$EndDownloadPhaseTickCount" $1
1024
1025    ; Get the seconds elapsed from the end of the download phase to the
1026    ; completion of the pre-installation check phase.
1027    ${GetSecondsElapsed} "$EndDownloadPhaseTickCount" "$EndPreInstallPhaseTickCount" $2
1028
1029    ; Get the seconds elapsed from the end of the pre-installation check phase
1030    ; to the completion of the installation phase.
1031    ${GetSecondsElapsed} "$EndPreInstallPhaseTickCount" "$EndInstallPhaseTickCount" $3
1032
1033    ; Get the seconds elapsed from the end of the installation phase to the
1034    ; completion of all phases.
1035    ${GetSecondsElapsed} "$EndInstallPhaseTickCount" "$EndFinishPhaseTickCount" $4
1036
1037    ${If} $ArchToInstall == ${ARCH_AMD64}
1038    ${OrIf} $ArchToInstall == ${ARCH_AARCH64}
1039      StrCpy $R0 "1"
1040    ${Else}
1041      StrCpy $R0 "0"
1042    ${EndIf}
1043
1044    ${If} ${IsNativeAMD64}
1045    ${OrIf} ${IsNativeARM64}
1046      StrCpy $R1 "1"
1047    ${Else}
1048      StrCpy $R1 "0"
1049    ${EndIf}
1050
1051    ; Though these values are sometimes incorrect due to bug 444664 it happens
1052    ; so rarely it isn't worth working around it by reading the registry values.
1053    ${WinVerGetMajor} $5
1054    ${WinVerGetMinor} $6
1055    ${WinVerGetBuild} $7
1056    ${WinVerGetServicePackLevel} $8
1057    ${If} ${IsServerOS}
1058      StrCpy $9 "1"
1059    ${Else}
1060      StrCpy $9 "0"
1061    ${EndIf}
1062
1063    ${If} "$ExitCode" == "${ERR_SUCCESS}"
1064      ReadINIStr $R5 "$INSTDIR\application.ini" "App" "Version"
1065      ReadINIStr $R6 "$INSTDIR\application.ini" "App" "BuildID"
1066    ${Else}
1067      StrCpy $R5 "0"
1068      StrCpy $R6 "0"
1069    ${EndIf}
1070
1071    ; Whether installed into the default installation directory
1072    ${GetLongPath} "$INSTDIR" $R7
1073    ${GetLongPath} "$InitialInstallDir" $R8
1074    ${If} "$R7" == "$R8"
1075      StrCpy $R7 "1"
1076    ${Else}
1077      StrCpy $R7 "0"
1078    ${EndIf}
1079
1080    ClearErrors
1081    WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
1082                     "Write Test"
1083    ${If} ${Errors}
1084      StrCpy $R8 "0"
1085    ${Else}
1086      DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
1087      StrCpy $R8 "1"
1088    ${EndIf}
1089
1090    ${If} "$DownloadServerIP" == ""
1091      StrCpy $DownloadServerIP "Unknown"
1092    ${EndIf}
1093
1094    StrCpy $R2 ""
1095    SetShellVarContext current ; Set SHCTX to the current user
1096    ReadRegStr $R2 HKCU "Software\Classes\http\shell\open\command" ""
1097    ${If} $R2 != ""
1098      ${GetPathFromString} "$R2" $R2
1099      ${GetParent} "$R2" $R3
1100      ${GetLongPath} "$R3" $R3
1101      ${If} $R3 == $INSTDIR
1102        StrCpy $R2 "1" ; This Firefox install is set as default.
1103      ${Else}
1104        StrCpy $R2 "$R2" "" -11 # length of firefox.exe
1105        ${If} "$R2" == "${FileMainEXE}"
1106          StrCpy $R2 "2" ; Another Firefox install is set as default.
1107        ${Else}
1108          StrCpy $R2 "0"
1109        ${EndIf}
1110      ${EndIf}
1111    ${Else}
1112      StrCpy $R2 "0" ; Firefox is not set as default.
1113    ${EndIf}
1114
1115    ${If} "$R2" == "0"
1116      StrCpy $R3 ""
1117      ReadRegStr $R2 HKLM "Software\Classes\http\shell\open\command" ""
1118      ${If} $R2 != ""
1119        ${GetPathFromString} "$R2" $R2
1120        ${GetParent} "$R2" $R3
1121        ${GetLongPath} "$R3" $R3
1122        ${If} $R3 == $INSTDIR
1123          StrCpy $R2 "1" ; This Firefox install is set as default.
1124        ${Else}
1125          StrCpy $R2 "$R2" "" -11 # length of firefox.exe
1126          ${If} "$R2" == "${FileMainEXE}"
1127            StrCpy $R2 "2" ; Another Firefox install is set as default.
1128          ${Else}
1129            StrCpy $R2 "0"
1130          ${EndIf}
1131        ${EndIf}
1132      ${Else}
1133        StrCpy $R2 "0" ; Firefox is not set as default.
1134      ${EndIf}
1135    ${EndIf}
1136
1137    ${If} $CanSetAsDefault == "true"
1138      ${If} $CheckboxSetAsDefault == "1"
1139        StrCpy $R3 "2"
1140      ${Else}
1141        StrCpy $R3 "3"
1142      ${EndIf}
1143    ${Else}
1144      ${If} ${AtLeastWin8}
1145        StrCpy $R3 "1"
1146      ${Else}
1147        StrCpy $R3 "0"
1148      ${EndIf}
1149    ${EndIf}
1150
1151!ifdef STUB_DEBUG
1152    MessageBox MB_OK "${BaseURLStubPing} \
1153                      $\nStub URL Version = ${StubURLVersion}${StubURLVersionAppend} \
1154                      $\nBuild Channel = ${Channel} \
1155                      $\nUpdate Channel = ${UpdateChannel} \
1156                      $\nLocale = ${AB_CD} \
1157                      $\nFirefox x64 = $R0 \
1158                      $\nRunning x64 Windows = $R1 \
1159                      $\nMajor = $5 \
1160                      $\nMinor = $6 \
1161                      $\nBuild = $7 \
1162                      $\nServicePack = $8 \
1163                      $\nIsServer = $9 \
1164                      $\nExit Code = $ExitCode \
1165                      $\nFirefox Launch Code = $FirefoxLaunchCode \
1166                      $\nDownload Retry Count = $DownloadRetryCount \
1167                      $\nDownloaded Bytes = $DownloadedBytes \
1168                      $\nDownload Size Bytes = $DownloadSizeBytes \
1169                      $\nIntroduction Phase Seconds = $IntroPhaseSeconds \
1170                      $\nOptions Phase Seconds = $OptionsPhaseSeconds \
1171                      $\nDownload Phase Seconds = $0 \
1172                      $\nLast Download Seconds = $1 \
1173                      $\nDownload First Transfer Seconds = $DownloadFirstTransferSeconds \
1174                      $\nPreinstall Phase Seconds = $2 \
1175                      $\nInstall Phase Seconds = $3 \
1176                      $\nFinish Phase Seconds = $4 \
1177                      $\nInitial Install Requirements Code = $InitialInstallRequirementsCode \
1178                      $\nOpened Download Page = $OpenedDownloadPage \
1179                      $\nExisting Profile = $ExistingProfile \
1180                      $\nExisting Version = $ExistingVersion \
1181                      $\nExisting Build ID = $ExistingBuildID \
1182                      $\nNew Version = $R5 \
1183                      $\nNew Build ID = $R6 \
1184                      $\nDefault Install Dir = $R7 \
1185                      $\nHas Admin = $R8 \
1186                      $\nDefault Status = $R2 \
1187                      $\nSet As Sefault Status = $R3 \
1188                      $\nDownload Server IP = $DownloadServerIP \
1189                      $\nPost-Signing Data = $PostSigningData \
1190                      $\nProfile cleanup prompt shown = $ProfileCleanupPromptType \
1191                      $\nDid profile cleanup = $CheckboxCleanupProfile"
1192    ; The following will exit the installer
1193    SetAutoClose true
1194    StrCpy $R9 "2"
1195    Call RelativeGotoPage
1196!else
1197    ${StartTimer} ${DownloadIntervalMS} OnPing
1198    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" \
1199                  "$PLUGINSDIR\_temp" /END
1200!endif
1201  ${Else}
1202    ${If} "$IsDownloadFinished" == "false"
1203      ; Cancel the download in progress
1204      InetBgDL::Get /RESET /END
1205    ${EndIf}
1206    ; The following will exit the installer
1207    SetAutoClose true
1208    StrCpy $R9 "2"
1209    Call RelativeGotoPage
1210  ${EndIf}
1211FunctionEnd
1212
1213Function OnPing
1214  InetBgDL::GetStats
1215  # $0 = HTTP status code, 0=Completed
1216  # $1 = Completed files
1217  # $2 = Remaining files
1218  # $3 = Number of downloaded bytes for the current file
1219  # $4 = Size of current file (Empty string if the size is unknown)
1220  # /RESET must be used if status $0 > 299 (e.g. failure)
1221  # When status is $0 =< 299 it is handled by InetBgDL
1222  ${If} $2 == 0
1223  ${OrIf} $0 > 299
1224    WebBrowser::CancelTimer $TimerHandle
1225    ${If} $0 > 299
1226      InetBgDL::Get /RESET /END
1227    ${EndIf}
1228    ; The following will exit the installer
1229    SetAutoClose true
1230    StrCpy $R9 "2"
1231    Call RelativeGotoPage
1232  ${EndIf}
1233FunctionEnd
1234
1235Function CheckInstall
1236  IntOp $InstallCounterStep $InstallCounterStep + 1
1237  ${If} $InstallCounterStep >= $InstallTotalSteps
1238    WebBrowser::CancelTimer $TimerHandle
1239    ; Close the handle that prevents modification of the full installer
1240    System::Call 'kernel32::CloseHandle(i $HandleDownload)'
1241    StrCpy $ExitCode "${ERR_INSTALL_TIMEOUT}"
1242    ; Use a timer so the UI has a chance to update
1243    ${StartTimer} ${InstallIntervalMS} DisplayDownloadError
1244    Return
1245  ${EndIf}
1246
1247  ${If} $ProgressCompleted < ${PROGRESS_BAR_INSTALL_END_STEP}
1248    IntOp $0 ${PROGRESS_BAR_INSTALL_END_STEP} - ${PROGRESS_BAR_DOWNLOAD_END_STEP}
1249    IntOp $0 $InstallCounterStep * $0
1250    IntOp $0 $0 / $InstallTotalSteps
1251    IntOp $ProgressCompleted ${PROGRESS_BAR_DOWNLOAD_END_STEP} + $0
1252    Call SetProgressBars
1253  ${EndIf}
1254
1255  ${If} ${FileExists} "$INSTDIR\install.log"
1256    Delete "$INSTDIR\install.tmp"
1257    CopyFiles /SILENT "$INSTDIR\install.log" "$INSTDIR\install.tmp"
1258
1259    ; The unfocus and refocus that happens approximately here is caused by the
1260    ; installer calling RefreshShellIcons to refresh the shortcut icons.
1261
1262    ; When the full installer completes the installation the install.log will no
1263    ; longer be in use.
1264    ClearErrors
1265    Delete "$INSTDIR\install.log"
1266    ${Unless} ${Errors}
1267      WebBrowser::CancelTimer $TimerHandle
1268      ; Close the handle that prevents modification of the full installer
1269      System::Call 'kernel32::CloseHandle(i $HandleDownload)'
1270      Rename "$INSTDIR\install.tmp" "$INSTDIR\install.log"
1271      Delete "$PLUGINSDIR\download.exe"
1272      Delete "$PLUGINSDIR\${CONFIG_INI}"
1273      System::Call "kernel32::GetTickCount()l .s"
1274      Pop $EndInstallPhaseTickCount
1275      Call FinishInstall
1276    ${EndUnless}
1277  ${EndIf}
1278FunctionEnd
1279
1280Function FinishInstall
1281  StrCpy $ProgressCompleted "${PROGRESS_BAR_INSTALL_END_STEP}"
1282  Call SetProgressBars
1283
1284  ${If} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade"
1285    Delete "$INSTDIR\${FileMainEXE}"
1286    Rename "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}"
1287  ${EndIf}
1288
1289  StrCpy $ExitCode "${ERR_SUCCESS}"
1290
1291  ${CopyPostSigningData}
1292  Pop $PostSigningData
1293
1294  Call LaunchApp
1295FunctionEnd
1296
1297Function RelativeGotoPage
1298  IntCmp $R9 0 0 Move Move
1299  StrCmp $R9 "X" 0 Move
1300  StrCpy $R9 "120"
1301
1302  Move:
1303  SendMessage $HWNDPARENT "0x408" "$R9" ""
1304FunctionEnd
1305
1306Function CheckSpace
1307  ${If} "$ExistingTopDir" != ""
1308    StrLen $0 "$ExistingTopDir"
1309    StrLen $1 "$INSTDIR"
1310    ${If} $0 <= $1
1311      StrCpy $2 "$INSTDIR" $3
1312      ${If} "$2" == "$ExistingTopDir"
1313        Return
1314      ${EndIf}
1315    ${EndIf}
1316  ${EndIf}
1317
1318  StrCpy $ExistingTopDir "$INSTDIR"
1319  ${DoUntil} ${FileExists} "$ExistingTopDir"
1320    ${GetParent} "$ExistingTopDir" $ExistingTopDir
1321    ${If} "$ExistingTopDir" == ""
1322      StrCpy $SpaceAvailableBytes "0"
1323      StrCpy $HasRequiredSpaceAvailable "false"
1324      Return
1325    ${EndIf}
1326  ${Loop}
1327
1328  ${GetLongPath} "$ExistingTopDir" $ExistingTopDir
1329
1330  ; GetDiskFreeSpaceExW requires a backslash.
1331  StrCpy $0 "$ExistingTopDir" "" -1 ; the last character
1332  ${If} "$0" != "\"
1333    StrCpy $0 "\"
1334  ${Else}
1335    StrCpy $0 ""
1336  ${EndIf}
1337
1338  System::Call 'kernel32::GetDiskFreeSpaceExW(w, *l, *l, *l) i("$ExistingTopDir$0", .r1, .r2, .r3) .'
1339  StrCpy $SpaceAvailableBytes "$1"
1340
1341  System::Int64Op $SpaceAvailableBytes / 1048576
1342  Pop $1
1343  System::Int64Op $1 > ${APPROXIMATE_REQUIRED_SPACE_MB}
1344  Pop $1
1345  ${If} $1 == 1
1346    StrCpy $HasRequiredSpaceAvailable "true"
1347  ${Else}
1348    StrCpy $HasRequiredSpaceAvailable "false"
1349  ${EndIf}
1350FunctionEnd
1351
1352Function CanWrite
1353  StrCpy $CanWriteToInstallDir "false"
1354
1355  StrCpy $0 "$INSTDIR"
1356  ; Use the existing directory when it exists
1357  ${Unless} ${FileExists} "$INSTDIR"
1358    ; Get the topmost directory that exists for new installs
1359    ${DoUntil} ${FileExists} "$0"
1360      ${GetParent} "$0" $0
1361      ${If} "$0" == ""
1362        Return
1363      ${EndIf}
1364    ${Loop}
1365  ${EndUnless}
1366
1367  GetTempFileName $2 "$0"
1368  Delete $2
1369  CreateDirectory "$2"
1370
1371  ${If} ${FileExists} "$2"
1372    ${If} ${FileExists} "$INSTDIR"
1373      GetTempFileName $3 "$INSTDIR"
1374    ${Else}
1375      GetTempFileName $3 "$2"
1376    ${EndIf}
1377    ${If} ${FileExists} "$3"
1378      Delete "$3"
1379      StrCpy $CanWriteToInstallDir "true"
1380    ${EndIf}
1381    RmDir "$2"
1382  ${EndIf}
1383FunctionEnd
1384
1385Function LaunchApp
1386  StrCpy $FirefoxLaunchCode "2"
1387
1388  ; Set the current working directory to the installation directory
1389  SetOutPath "$INSTDIR"
1390  ClearErrors
1391  ${GetParameters} $0
1392  ${GetOptions} "$0" "/UAC:" $1
1393  ${If} ${Errors}
1394    ${If} $CheckboxCleanupProfile == 1
1395      ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration -first-startup"
1396    ${Else}
1397      ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -first-startup"
1398    ${EndIf}
1399  ${Else}
1400    StrCpy $R1 $CheckboxCleanupProfile
1401    GetFunctionAddress $0 LaunchAppFromElevatedProcess
1402    UAC::ExecCodeSegment $0
1403  ${EndIf}
1404
1405  StrCpy $AppLaunchWaitTickCount 0
1406  ${StartTimer} ${AppLaunchWaitIntervalMS} WaitForAppLaunch
1407FunctionEnd
1408
1409Function LaunchAppFromElevatedProcess
1410  ; Set the current working directory to the installation directory
1411  SetOutPath "$INSTDIR"
1412  ${If} $R1 == 1
1413    ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -reset-profile -migration -first-startup"
1414  ${Else}
1415    ${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\" -first-startup"
1416  ${EndIf}
1417FunctionEnd
1418
1419Function WaitForAppLaunch
1420  FindWindow $0 "${MainWindowClass}"
1421  FindWindow $1 "${DialogWindowClass}"
1422  ${If} $0 <> 0
1423  ${OrIf} $1 <> 0
1424    WebBrowser::CancelTimer $TimerHandle
1425    StrCpy $ProgressCompleted "${PROGRESS_BAR_APP_LAUNCH_END_STEP}"
1426    Call SetProgressBars
1427    Call SendPing
1428    Return
1429  ${EndIf}
1430
1431  IntOp $AppLaunchWaitTickCount $AppLaunchWaitTickCount + 1
1432  IntOp $0 $AppLaunchWaitTickCount * ${AppLaunchWaitIntervalMS}
1433  ${If} $0 >= ${AppLaunchWaitTimeoutMS}
1434    ; We've waited an unreasonably long time, so just exit.
1435    WebBrowser::CancelTimer $TimerHandle
1436    Call SendPing
1437    Return
1438  ${EndIf}
1439
1440  ${If} $ProgressCompleted < ${PROGRESS_BAR_APP_LAUNCH_END_STEP}
1441    IntOp $ProgressCompleted $ProgressCompleted + 1
1442    Call SetProgressBars
1443  ${EndIf}
1444FunctionEnd
1445
1446Function DisplayDownloadError
1447  WebBrowser::CancelTimer $TimerHandle
1448  ; To better display the error state on the taskbar set the progress completed
1449  ; value to the total value.
1450  ${ITBL3SetProgressValue} "100" "100"
1451  ${ITBL3SetProgressState} "${TBPF_ERROR}"
1452
1453  MessageBox MB_OKCANCEL|MB_ICONSTOP "$(ERROR_DOWNLOAD_CONT)" IDCANCEL +2 IDOK +1
1454  Call LaunchHelpPage
1455  Call SendPing
1456FunctionEnd
1457
1458Function LaunchHelpPage
1459  StrCpy $OpenedDownloadPage "1" ; Already initialized to 0
1460  ClearErrors
1461  ${GetParameters} $0
1462  ${GetOptions} "$0" "/UAC:" $1
1463  ${If} ${Errors}
1464    Call OpenManualDownloadURL
1465  ${Else}
1466    GetFunctionAddress $0 OpenManualDownloadURL
1467    UAC::ExecCodeSegment $0
1468  ${EndIf}
1469FunctionEnd
1470
1471Function OpenManualDownloadURL
1472  ClearErrors
1473  ReadINIStr $0 "${PARTNER_INI}" "DownloadURL" "FallbackPage"
1474  ${IfNot} ${Errors}
1475    ExecShell "open" "$0"
1476  ${Else}
1477    ExecShell "open" "${URLManualDownload}${URLManualDownloadAppend}"
1478  ${EndIf}
1479FunctionEnd
1480
1481Function ShouldPromptForProfileCleanup
1482  ; This will be our return value.
1483  StrCpy $ProfileCleanupPromptType 0
1484
1485  ; Only consider installations of the same architecture we're installing.
1486  ${If} $ArchToInstall == ${ARCH_AMD64}
1487  ${OrIf} $ArchToInstall == ${ARCH_AARCH64}
1488    SetRegView 64
1489  ${Else}
1490    SetRegView 32
1491  ${EndIf}
1492
1493  ; Make sure $APPDATA is the user's AppData and not ProgramData.
1494  ; We'll set this back to all at the end of the function.
1495  SetShellVarContext current
1496
1497  ${FindInstallSpecificProfile}
1498  Pop $R0
1499
1500  ${If} $R0 == ""
1501    ; We don't have an install-specific profile, so look for an old-style
1502    ; default profile instead by checking each numbered Profile section.
1503    StrCpy $0 0
1504    ${Do}
1505      ClearErrors
1506      ; Check if the section exists by reading a value that must be present.
1507      ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Path"
1508      ${If} ${Errors}
1509        ; We've run out of profile sections.
1510        ${Break}
1511      ${EndIf}
1512
1513      ClearErrors
1514      ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Default"
1515      ${IfNot} ${Errors}
1516      ${AndIf} $1 == "1"
1517        ; We've found the default profile
1518        ReadINIStr $1 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "Path"
1519        ReadINIStr $2 "$APPDATA\Mozilla\Firefox\profiles.ini" "Profile$0" "IsRelative"
1520        ${If} $2 == "1"
1521          StrCpy $R0 "$APPDATA\Mozilla\Firefox\$1"
1522        ${Else}
1523          StrCpy $R0 "$1"
1524        ${EndIf}
1525        ${Break}
1526      ${EndIf}
1527
1528      IntOp $0 $0 + 1
1529    ${Loop}
1530  ${EndIf}
1531
1532  GetFullPathName $R0 $R0
1533
1534  ${If} $R0 == ""
1535    ; No profile to clean up, so don't show the cleanup prompt.
1536    GoTo end
1537  ${EndIf}
1538
1539  ; We have at least one profile present. If we don't have any installations,
1540  ; then we need to show the re-install prompt. We'll say there's an
1541  ; installation present if HKCR\FirefoxURL* exists and points to a real path.
1542  StrCpy $0 0
1543  StrCpy $R9 ""
1544  ${Do}
1545    ClearErrors
1546    EnumRegKey $1 HKCR "" $0
1547    ${If} ${Errors}
1548    ${OrIf} $1 == ""
1549      ${Break}
1550    ${EndIf}
1551    ${WordFind} "$1" "-" "+1{" $2
1552    ${If} $2 == "FirefoxURL"
1553      ClearErrors
1554      ReadRegStr $2 HKCR "$1\DefaultIcon" ""
1555      ${IfNot} ${Errors}
1556        ${GetPathFromString} $2 $1
1557        ${If} ${FileExists} $1
1558          StrCpy $R9 $1
1559          ${Break}
1560        ${EndIf}
1561      ${EndIf}
1562    ${EndIf}
1563    IntOp $0 $0 + 1
1564  ${Loop}
1565  ${If} $R9 == ""
1566    StrCpy $ProfileCleanupPromptType 1
1567    GoTo end
1568  ${EndIf}
1569
1570  ; Okay, there's at least one install, let's see if it's for this channel.
1571  SetShellVarContext all
1572  ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
1573  ${If} $0 == "false"
1574    SetShellVarContext current
1575    ${GetSingleInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
1576    ${If} $0 == "false"
1577      ; Existing installs are not for this channel. Don't show any prompt.
1578      GoTo end
1579    ${EndIf}
1580  ${EndIf}
1581
1582  ; Find out what version the default profile was last used on.
1583  ${If} ${FileExists} "$R0\compatibility.ini"
1584    ClearErrors
1585    ReadINIStr $0 "$R0\compatibility.ini" "Compatibility" "LastVersion"
1586    ${If} ${Errors}
1587      GoTo end
1588    ${EndIf}
1589    ${WordFind} $0 "." "+1{" $0
1590
1591    ; We don't know what version we're about to install because we haven't
1592    ; downloaded it yet. Find out what the latest version released on this
1593    ; channel is and assume we'll be installing that one.
1594    Call GetLatestReleasedVersion
1595    ${If} ${Errors}
1596      ; Use this stub installer's version as a fallback when we can't get the
1597      ; real current version; this may be behind, but it's better than nothing.
1598      StrCpy $1 ${AppVersion}
1599    ${EndIf}
1600
1601    ${WordFind} $1 "." "+1{" $1
1602    IntOp $1 $1 - 2
1603
1604    ${If} $1 > $0
1605      ; Default profile was last used more than two versions ago, so we need
1606      ; to show the paveover version of the profile cleanup prompt.
1607      StrCpy $ProfileCleanupPromptType 2
1608    ${EndIf}
1609  ${EndIf}
1610
1611  end:
1612  SetRegView lastused
1613  SetShellVarContext all
1614FunctionEnd
1615
1616Function GetLatestReleasedVersion
1617  ClearErrors
1618  Push $0 ; InetBgDl::GetStats uses $0 for the HTTP error code
1619  ; $1 is our return value, so don't save it
1620  Push $2 ; InetBgDl::GetStats uses $2 to tell us when the transfer is done
1621  Push $3 ; $3 - $5 are also set by InetBgDl::GetStats, but we don't use them
1622  Push $4
1623  Push $5
1624  Push $6 ; This is our response timeout counter
1625
1626  InetBgDL::Get /RESET /END
1627  InetBgDL::Get "https://product-details.mozilla.org/1.0/firefox_versions.json" \
1628                "$PLUGINSDIR\firefox_versions.json" \
1629                /CONNECTTIMEOUT 120 /RECEIVETIMEOUT 120 /END
1630
1631  ; Wait for the response, but only give it half a second since this is on the
1632  ; installer startup path (we haven't even shown a window yet).
1633  StrCpy $6 0
1634  ${Do}
1635    Sleep 100
1636    InetBgDL::GetStats
1637    IntOp $6 $6 + 1
1638
1639    ${If} $2 == 0
1640      ${Break}
1641    ${ElseIf} $6 >= 5
1642      InetBgDL::Get /RESET /END
1643      SetErrors
1644      GoTo end
1645    ${EndIf}
1646  ${Loop}
1647
1648  StrCpy $1 0
1649  nsJSON::Set /file "$PLUGINSDIR\firefox_versions.json"
1650  IfErrors end
1651  ${Select} ${Channel}
1652  ${Case} "unofficial"
1653    StrCpy $1 "FIREFOX_NIGHTLY"
1654  ${Case} "nightly"
1655    StrCpy $1 "FIREFOX_NIGHTLY"
1656  ${Case} "aurora"
1657    StrCpy $1 "FIREFOX_DEVEDITION"
1658  ${Case} "beta"
1659    StrCpy $1 "LATEST_FIREFOX_RELEASED_DEVEL_VERSION"
1660  ${Case} "release"
1661    StrCpy $1 "LATEST_FIREFOX_VERSION"
1662  ${EndSelect}
1663  nsJSON::Get $1 /end
1664
1665  end:
1666  ${If} ${Errors}
1667  ${OrIf} $1 == 0
1668    SetErrors
1669    StrCpy $1 0
1670  ${Else}
1671    Pop $1
1672  ${EndIf}
1673
1674  Pop $6
1675  Pop $5
1676  Pop $4
1677  Pop $3
1678  Pop $2
1679  Pop $0
1680FunctionEnd
1681
1682; Determine which architecture build we should download and install.
1683; AArch64 is always selected if it's the native architecture of the machine.
1684; Otherwise, we check a few things to determine if AMD64 is appropriate:
1685; 1) Running a 64-bit OS (we've already checked the OS version).
1686; 2) An amount of RAM strictly greater than RAM_NEEDED_FOR_64BIT
1687; 3) No third-party products installed that cause issues with the 64-bit build.
1688;    Currently this includes Lenovo OneKey Theater and Lenovo Energy Management.
1689; We also make sure that the partner.ini file contains a download URL for the
1690; selected architecture, when a partner.ini file eixsts.
1691; If any of those checks fail, the 32-bit x86 build is selected.
1692Function GetArchToInstall
1693  StrCpy $ArchToInstall ${ARCH_X86}
1694
1695  ${If} ${IsNativeARM64}
1696    StrCpy $ArchToInstall ${ARCH_AARCH64}
1697    GoTo downloadUrlCheck
1698  ${EndIf}
1699
1700  ${IfNot} ${IsNativeAMD64}
1701    Return
1702  ${EndIf}
1703
1704  System::Call "*(i 64, i, l 0, l, l, l, l, l, l)p.r1"
1705  System::Call "Kernel32::GlobalMemoryStatusEx(p r1)"
1706  System::Call "*$1(i, i, l.r2, l, l, l, l, l, l)"
1707  System::Free $1
1708  ${If} $2 L<= ${RAM_NEEDED_FOR_64BIT}
1709    Return
1710  ${EndIf}
1711
1712  ; Lenovo OneKey Theater can theoretically be in a directory other than this
1713  ; one, because some installer versions let you change it, but it's unlikely.
1714  ${If} ${FileExists} "$PROGRAMFILES32\Lenovo\Onekey Theater\windowsapihookdll64.dll"
1715    Return
1716  ${EndIf}
1717
1718  ${If} ${FileExists} "$PROGRAMFILES32\Lenovo\Energy Management\Energy Management.exe"
1719    Return
1720  ${EndIf}
1721
1722  StrCpy $ArchToInstall ${ARCH_AMD64}
1723
1724  downloadUrlCheck:
1725  ; If we've selected an architecture that doesn't have a download URL in the
1726  ; partner.ini, but there is a URL there for 32-bit x86, then fall back to
1727  ; 32-bit x86 on the theory that we should never use a non-partner build if
1728  ; we are configured as a partner installer, even if the only build that's
1729  ; provided is suboptimal for the machine. If there isn't even an x86 URL,
1730  ; then we won't force x86 and GetDownloadURL will stick with the built-in URL.
1731  ClearErrors
1732  ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "X86"
1733  ${IfNot} ${Errors}
1734    ${If} $ArchToInstall == ${ARCH_AMD64}
1735      ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AMD64"
1736      ${If} ${Errors}
1737        StrCpy $ArchToInstall ${ARCH_X86}
1738      ${EndIf}
1739    ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
1740      ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AArch64"
1741      ${If} ${Errors}
1742        StrCpy $ArchToInstall ${ARCH_X86}
1743      ${EndIf}
1744    ${EndIf}
1745  ${EndIf}
1746FunctionEnd
1747
1748Function GetDownloadURL
1749  Push $0
1750  Push $1
1751
1752  ; Start with the appropriate URL from our built-in branding info.
1753  ${If} $ArchToInstall == ${ARCH_AMD64}
1754    StrCpy $0 "${URLStubDownloadAMD64}${URLStubDownloadAppend}"
1755  ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
1756    StrCpy $0 "${URLStubDownloadAArch64}${URLStubDownloadAppend}"
1757  ${Else}
1758    StrCpy $0 "${URLStubDownloadX86}${URLStubDownloadAppend}"
1759  ${EndIf}
1760
1761  ; If we have a partner.ini file then use the URL from there instead.
1762  ClearErrors
1763  ${If} $ArchToInstall == ${ARCH_AMD64}
1764    ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AMD64"
1765  ${ElseIf} $ArchToInstall == ${ARCH_AARCH64}
1766    ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "AArch64"
1767  ${Else}
1768    ReadINIStr $1 "${PARTNER_INI}" "DownloadURL" "X86"
1769  ${EndIf}
1770  ${IfNot} ${Errors}
1771    StrCpy $0 "$1"
1772  ${EndIf}
1773
1774  Pop $1
1775  Exch $0
1776FunctionEnd
1777
1778Section
1779SectionEnd
1780