Bug 1453613 Part 2 - Add a full installer telemetry ping. r=agashlin

--HG--
extra : source : d7ee73e237c70cb7d13567e9a0ec2c32a1162211
This commit is contained in:
Matt Howell 2018-10-15 11:21:25 -07:00
Родитель 7f58899c3a
Коммит 54c17abaeb
4 изменённых файлов: 362 добавлений и 2 удалений

Просмотреть файл

@ -139,3 +139,9 @@ VIAddVersionKey "ProductVersion" "${AppVersion}"
!define PROGRESS_BAR_TOP_DU 112u
!define APPNAME_BMP_EDGE_DU 19u
!define APPNAME_BMP_TOP_DU 12u
# Constants for parts of the telemetry submission URL
!define TELEMETRY_BASE_URL https://incoming.telemetry.mozilla.org/submit
!define TELEMETRY_NAMESPACE firefox-installer
!define TELEMETRY_INSTALL_PING_VERSION 1
!define TELEMETRY_INSTALL_PING_DOCTYPE install

Просмотреть файл

@ -6,6 +6,7 @@
# AppAssocReg http://nsis.sourceforge.net/Application_Association_Registration_plug-in
# ApplicationID http://nsis.sourceforge.net/ApplicationID_plug-in
# CityHash http://dxr.mozilla.org/mozilla-central/source/other-licenses/nsis/Contrib/CityHash
# nsJSON http://nsis.sourceforge.net/NsJSON_plug-in
# ShellLink http://nsis.sourceforge.net/ShellLink_plug-in
# UAC http://nsis.sourceforge.net/UAC_plug-in
# ServicesHelper Mozilla specific plugin that is located in /other-licenses/nsis
@ -39,6 +40,18 @@ Var ExtensionRecommender
Var PageName
Var PreventRebootRequired
; Telemetry ping fields
Var SetAsDefault
Var HadOldInstall
Var DefaultInstDir
Var IntroPhaseStart
Var OptionsPhaseStart
Var InstallPhaseStart
Var FinishPhaseStart
Var FinishPhaseEnd
Var InstallResult
Var LaunchedNewApp
; By defining NO_STARTMENU_DIR an installer that doesn't provide an option for
; an application's Start Menu PROGRAMS directory and doesn't define the
; StartMenuDir variable can use the common InstallOnInitCommon macro.
@ -84,6 +97,7 @@ VIAddVersionKey "OriginalFilename" "setup.exe"
!insertmacro CleanUpdateDirectories
!insertmacro CopyFilesFromDir
!insertmacro CreateRegKey
!insertmacro GetFirstInstallPath
!insertmacro GetLongPath
!insertmacro GetPathFromString
!insertmacro GetParent
@ -188,6 +202,7 @@ Page custom preSummary leaveSummary
!define MUI_FINISHPAGE_RUN_FUNCTION LaunchApp
!define MUI_FINISHPAGE_RUN_TEXT $(LAUNCH_TEXT)
!define MUI_PAGE_CUSTOMFUNCTION_PRE preFinish
!define MUI_PAGE_CUSTOMFUNCTION_LEAVE postFinish
!insertmacro MUI_PAGE_FINISH
; Use the default dialog for IDD_VERIFY for a simple Banner
@ -198,6 +213,9 @@ ChangeUI IDD_VERIFY "${NSISDIR}\Contrib\UIs\default.exe"
; Cleanup operations to perform at the start of the installation.
Section "-InstallStartCleanup"
System::Call "kernel32::GetTickCount()l .s"
Pop $InstallPhaseStart
SetDetailsPrint both
DetailPrint $(STATUS_CLEANUP)
SetDetailsPrint none
@ -663,7 +681,7 @@ Section "-InstallEndCleanup"
ClearErrors
${MUI_INSTALLOPTIONS_READ} $0 "summary.ini" "Field 4" "State"
${If} "$0" == "1"
; NB: this code is duplicated in stub.nsi. Please keep in sync.
StrCpy $SetAsDefault true
; For data migration in the app, we want to know what the default browser
; value was before we changed it. To do so, we read it here and store it
; in our own registry key.
@ -698,6 +716,7 @@ Section "-InstallEndCleanup"
UAC::ExecCodeSegment $0
${EndIf}
${ElseIfNot} ${Errors}
StrCpy $SetAsDefault false
${LogHeader} "Writing default-browser opt-out"
ClearErrors
WriteRegStr HKCU "Software\Mozilla\Firefox" "DefaultBrowserOptOut" "True"
@ -756,6 +775,14 @@ Section "-InstallEndCleanup"
${EndUnless}
${EndIf}
${EndIf}
StrCpy $InstallResult "success"
; When we're using the GUI, .onGUIEnd sends the ping, but of course that isn't
; invoked when we're running silently.
${If} ${Silent}
Call SendPing
${EndIf}
SectionEnd
################################################################################
@ -904,6 +931,8 @@ Function LaunchApp
GetFunctionAddress $0 LaunchAppFromElevatedProcess
UAC::ExecCodeSegment $0
${EndIf}
StrCpy $LaunchedNewApp true
FunctionEnd
Function LaunchAppFromElevatedProcess
@ -913,6 +942,170 @@ Function LaunchAppFromElevatedProcess
${ExecAndWaitForInputIdle} "$\"$INSTDIR\${FileMainEXE}$\""
FunctionEnd
Function SendPing
${GetParameters} $0
${GetOptions} $0 "/LaunchedFromStub" $0
${IfNot} ${Errors}
Return
${EndIf}
; Create a GUID to use as the unique document ID.
System::Call "rpcrt4::UuidCreate(g . r0)i"
; StringFromGUID2 (which is what System::Call uses internally to stringify
; GUIDs) includes braces in its output, and we don't want those.
StrCpy $0 $0 -1 1
; Configure the HTTP request for the ping
nsJSON::Set /tree ping /value "{}"
nsJSON::Set /tree ping "Url" /value \
'"${TELEMETRY_BASE_URL}/${TELEMETRY_NAMESPACE}/${TELEMETRY_INSTALL_PING_DOCTYPE}/${TELEMETRY_INSTALL_PING_VERSION}/$0"'
nsJSON::Set /tree ping "Verb" /value '"POST"'
nsJSON::Set /tree ping "DataType" /value '"JSON"'
nsJSON::Set /tree ping "AccessType" /value '"PreConfig"'
; Fill in the ping payload
nsJSON::Set /tree ping "Data" /value "{}"
nsJSON::Set /tree ping "Data" "installer_type" /value '"full"'
nsJSON::Set /tree ping "Data" "installer_version" /value '"${AppVersion}"'
nsJSON::Set /tree ping "Data" "build_channel" /value '"${Channel}"'
nsJSON::Set /tree ping "Data" "update_channel" /value '"${UpdateChannel}"'
nsJSON::Set /tree ping "Data" "locale" /value '"${AB_CD}"'
ReadINIStr $0 "$INSTDIR\application.ini" "App" "Version"
nsJSON::Set /tree ping "Data" "version" /value '"$0"'
ReadINIStr $0 "$INSTDIR\application.ini" "App" "BuildID"
nsJSON::Set /tree ping "Data" "build_id" /value '"$0"'
${GetParameters} $0
${GetOptions} $0 "/LaunchedFromMSI" $0
${IfNot} ${Errors}
nsJSON::Set /tree ping "Data" "from_msi" /value true
${EndIf}
!ifdef HAVE_64BIT_BUILD
nsJSON::Set /tree ping "Data" "64bit_build" /value true
!else
nsJSON::Set /tree ping "Data" "64bit_build" /value false
!endif
${If} ${RunningX64}
nsJSON::Set /tree ping "Data" "64bit_os" /value true
${Else}
nsJSON::Set /tree ping "Data" "64bit_os" /value false
${EndIf}
; Though these values are sometimes incorrect due to bug 444664 it happens
; so rarely it isn't worth working around it by reading the registry values.
${WinVerGetMajor} $0
${WinVerGetMinor} $1
${WinVerGetBuild} $2
nsJSON::Set /tree ping "Data" "os_version" /value '"$0.$1.$2"'
${If} ${IsServerOS}
nsJSON::Set /tree ping "Data" "server_os" /value true
${Else}
nsJSON::Set /tree ping "Data" "server_os" /value false
${EndIf}
ClearErrors
WriteRegStr HKLM "Software\Mozilla" "${BrandShortName}InstallerTest" \
"Write Test"
${If} ${Errors}
nsJSON::Set /tree ping "Data" "admin_user" /value false
${Else}
DeleteRegValue HKLM "Software\Mozilla" "${BrandShortName}InstallerTest"
nsJSON::Set /tree ping "Data" "admin_user" /value true
${EndIf}
${If} $DefaultInstDir == $INSTDIR
nsJSON::Set /tree ping "Data" "default_path" /value true
${Else}
nsJSON::Set /tree ping "Data" "default_path" /value false
${EndIf}
nsJSON::Set /tree ping "Data" "set_default" /value "$SetAsDefault"
nsJSON::Set /tree ping "Data" "new_default" /value false
nsJSON::Set /tree ping "Data" "old_default" /value false
AppAssocReg::QueryCurrentDefault "http" "protocol" "effective"
Pop $0
ReadRegStr $0 HKCR "$0\shell\open\command" ""
${If} $0 != ""
${GetPathFromString} "$0" $0
${GetParent} "$0" $1
${GetLongPath} "$1" $1
${If} $1 == $INSTDIR
nsJSON::Set /tree ping "Data" "new_default" /value true
${Else}
StrCpy $0 "$0" "" -11 # 11 == length of "firefox.exe"
${If} "$0" == "${FileMainEXE}"
nsJSON::Set /tree ping "Data" "old_default" /value true
${EndIf}
${EndIf}
${EndIf}
nsJSON::Set /tree ping "Data" "had_old_install" /value "$HadOldInstall"
${If} ${Silent}
; In silent mode, only the install phase is executed, and the GUI events
; that initialize most of the phase times are never called; only
; $InstallPhaseStart and $FinishPhaseStart have usable values.
${GetSecondsElapsed} $InstallPhaseStart $FinishPhaseStart $0
nsJSON::Set /tree ping "Data" "intro_time" /value 0
nsJSON::Set /tree ping "Data" "options_time" /value 0
nsJSON::Set /tree ping "Data" "install_time" /value "$0"
nsJSON::Set /tree ping "Data" "finish_time" /value 0
${Else}
; In GUI mode, all we can be certain of is that the intro phase has started;
; the user could have canceled at any time and phases after that won't
; have run at all. So we have to be prepared for anything after
; $IntroPhaseStart to be uninitialized. For anything that isn't filled in
; yet we'll use the current tick count. That means that any phases that
; weren't entered at all will get 0 for their times because the start and
; end tick counts will be the same.
System::Call "kernel32::GetTickCount()l .s"
Pop $0
${If} $OptionsPhaseStart == 0
StrCpy $OptionsPhaseStart $0
${EndIf}
${GetSecondsElapsed} $IntroPhaseStart $OptionsPhaseStart $1
nsJSON::Set /tree ping "Data" "intro_time" /value "$1"
${If} $InstallPhaseStart == 0
StrCpy $InstallPhaseStart $0
${EndIf}
${GetSecondsElapsed} $OptionsPhaseStart $InstallPhaseStart $1
nsJSON::Set /tree ping "Data" "options_time" /value "$1"
${If} $FinishPhaseStart == 0
StrCpy $FinishPhaseStart $0
${EndIf}
${GetSecondsElapsed} $InstallPhaseStart $FinishPhaseStart $1
nsJSON::Set /tree ping "Data" "install_time" /value "$1"
${If} $FinishPhaseEnd == 0
StrCpy $FinishPhaseEnd $0
${EndIf}
${GetSecondsElapsed} $FinishPhaseStart $FinishPhaseEnd $1
nsJSON::Set /tree ping "Data" "finish_time" /value "$1"
${EndIf}
nsJSON::Set /tree ping "Data" "new_launched" /value "$LaunchedNewApp"
nsJSON::Set /tree ping "Data" "succeeded" /value false
${If} $InstallResult == "cancel"
nsJSON::Set /tree ping "Data" "user_cancelled" /value true
${ElseIf} $InstallResult == "success"
nsJSON::Set /tree ping "Data" "succeeded" /value true
${EndIf}
; Send the ping request. This call will block until a response is received,
; but we shouldn't have any windows still open, so we won't jank anything.
nsJSON::Set /http ping
FunctionEnd
################################################################################
# Language
@ -939,9 +1132,15 @@ Function preWelcome
Delete "$PLUGINSDIR\modern-wizard.bmp"
CopyFiles /SILENT "$EXEDIR\core\distribution\modern-wizard.bmp" "$PLUGINSDIR\modern-wizard.bmp"
${EndIf}
System::Call "kernel32::GetTickCount()l .s"
Pop $IntroPhaseStart
FunctionEnd
Function preOptions
System::Call "kernel32::GetTickCount()l .s"
Pop $OptionsPhaseStart
StrCpy $PageName "Options"
${If} ${FileExists} "$EXEDIR\core\distribution\modern-header.bmp"
${AndIf} $hHeaderBitmap == ""
@ -975,6 +1174,8 @@ FunctionEnd
Function preDirectory
StrCpy $PageName "Directory"
${PreDirectoryCommon}
StrCpy $DefaultInstDir $INSTDIR
FunctionEnd
Function leaveDirectory
@ -1288,11 +1489,19 @@ FunctionEnd
; When we add an optional action to the finish page the cancel button is
; enabled. This disables it and leaves the finish button as the only choice.
Function preFinish
System::Call "kernel32::GetTickCount()l .s"
Pop $FinishPhaseStart
StrCpy $PageName ""
${EndInstallLog} "${BrandFullName}"
!insertmacro MUI_INSTALLOPTIONS_WRITE "ioSpecial.ini" "settings" "cancelenabled" "0"
FunctionEnd
Function postFinish
System::Call "kernel32::GetTickCount()l .s"
Pop $FinishPhaseEnd
FunctionEnd
################################################################################
# Initialization Functions
@ -1301,6 +1510,18 @@ Function .onInit
; This only effects LoadLibrary calls and not implicitly loaded DLLs.
System::Call 'kernel32::SetDllDirectoryW(w "")'
; Initialize the variables used for telemetry
StrCpy $SetAsDefault true
StrCpy $HadOldInstall false
StrCpy $DefaultInstDir $INSTDIR
StrCpy $IntroPhaseStart 0
StrCpy $OptionsPhaseStart 0
StrCpy $InstallPhaseStart 0
StrCpy $FinishPhaseStart 0
StrCpy $FinishPhaseEnd 0
StrCpy $InstallResult "cancel"
StrCpy $LaunchedNewApp false
StrCpy $PageName ""
StrCpy $LANGUAGE 0
${SetBrandNameVars} "$EXEDIR\core\distribution\setup.ini"
@ -1338,6 +1559,20 @@ Function .onInit
SetRegView 64
!endif
SetShellVarContext all
${GetFirstInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
${If} "$0" == "false"
SetShellVarContext current
${GetFirstInstallPath} "Software\Mozilla\${BrandFullNameInternal}" $0
${If} "$0" == "false"
StrCpy $HadOldInstall false
${Else}
StrCpy $HadOldInstall true
${EndIf}
${Else}
StrCpy $HadOldInstall true
${EndIf}
${InstallOnInitCommon} "$(WARN_MIN_SUPPORTED_OSVER_CPU_MSG)"
!insertmacro InitInstallOptionsFile "options.ini"
@ -1527,4 +1762,5 @@ FunctionEnd
Function .onGUIEnd
${OnEndCommon}
Call SendPing
FunctionEnd

Просмотреть файл

@ -1218,7 +1218,7 @@ Function LaunchFullInstaller
System::Call "kernel32::GetTickCount()l .s"
Pop $EndPreInstallPhaseTickCount
Exec "$\"$PLUGINSDIR\download.exe$\" /INI=$PLUGINSDIR\${CONFIG_INI}"
Exec "$\"$PLUGINSDIR\download.exe$\" /LaunchedFromStub /INI=$PLUGINSDIR\${CONFIG_INI}"
${NSD_CreateTimer} CheckInstall ${InstallIntervalMS}
FunctionEnd

Просмотреть файл

@ -1712,6 +1712,124 @@
!endif
!macroend
/**
* Find the first existing installation for the application.
* This is similar to GetSingleInstallPath, except that it always returns the
* first path it finds, instead of an error when more than one path exists.
*
* The shell context and the registry view should already have been set.
*
* @param _KEY
* The registry subkey (typically Software\Mozilla\App Name).
* @return _RESULT
* path to the install directory of the first location found, or
* the string "false" if no existing installation was found.
*
* $R5 = counter for the loop's EnumRegKey
* $R6 = return value from EnumRegKey
* $R7 = return value from ReadRegStr
* $R8 = storage for _KEY
* $R9 = _KEY and _RESULT
*/
!macro GetFirstInstallPath
!ifndef ${_MOZFUNC_UN}GetFirstInstallPath
!define _MOZFUNC_UN_TMP ${_MOZFUNC_UN}
!insertmacro ${_MOZFUNC_UN_TMP}GetLongPath
!insertmacro ${_MOZFUNC_UN_TMP}GetParent
!insertmacro ${_MOZFUNC_UN_TMP}RemoveQuotesFromPath
!undef _MOZFUNC_UN
!define _MOZFUNC_UN ${_MOZFUNC_UN_TMP}
!undef _MOZFUNC_UN_TMP
!verbose push
!verbose ${_MOZFUNC_VERBOSE}
!define ${_MOZFUNC_UN}GetFirstInstallPath "!insertmacro ${_MOZFUNC_UN}__GetFirstInstallPathCall"
Function ${_MOZFUNC_UN}__GetFirstInstallPath
Exch $R9
Push $R8
Push $R7
Push $R6
Push $R5
StrCpy $R8 $R9
StrCpy $R9 "false"
StrCpy $R5 0
${Do}
ClearErrors
EnumRegKey $R6 SHCTX $R8 $R5
${If} ${Errors}
${OrIf} $R6 == ""
${Break}
${EndIf}
IntOp $R5 $R5 + 1
ReadRegStr $R7 SHCTX "$R8\$R6\Main" "PathToExe"
${If} ${Errors}
${Continue}
${EndIf}
${${_MOZFUNC_UN}RemoveQuotesFromPath} "$R7" $R7
GetFullPathName $R7 "$R7"
${If} ${Errors}
${Continue}
${EndIf}
StrCpy $R9 "$R7"
${Break}
${Loop}
${If} $R9 != "false"
${${_MOZFUNC_UN}GetLongPath} "$R9" $R9
${${_MOZFUNC_UN}GetParent} "$R9" $R9
${EndIf}
Pop $R5
Pop $R6
Pop $R7
Pop $R8
Exch $R9
FunctionEnd
!verbose pop
!endif
!macroend
!macro __GetFirstInstallPathCall _KEY _RESULT
!verbose push
!verbose ${_MOZFUNC_VERBOSE}
Push "${_KEY}"
Call __GetFirstInstallPath
Pop ${_RESULT}
!verbose pop
!macroend
!macro un.__GetFirstInstallPathCall _KEY _RESULT
!verbose push
!verbose ${_MOZFUNC_VERBOSE}
Push "${_KEY}"
Call un.__GetFirstInstallPath
Pop ${_RESULT}
!verbose pop
!macroend
!macro un.__GetFirstInstallPath
!ifndef un.__GetFirstInstallPath
!verbose push
!verbose ${_MOZFUNC_VERBOSE}
!undef _MOZFUNC_UN
!define _MOZFUNC_UN "un."
!insertmacro __GetFirstInstallPath
!undef _MOZFUNC_UN
!define _MOZFUNC_UN
!verbose pop
!endif
!macroend
################################################################################
# Macros for working with the file system