From 2e2b979b5e8276d7bec9b2634f4c70ecb2509dfe Mon Sep 17 00:00:00 2001 From: "rob_strong@exchangecode.com" Date: Fri, 30 Nov 2007 11:02:09 -0800 Subject: [PATCH] Bug 398036 - Add file in use install support. r=sspitzer, blocking-firefox3=mconnor --- browser/installer/windows/nsis/installer.nsi | 274 ++++++-------- browser/installer/windows/nsis/shared.nsh | 29 ++ .../installer/windows/nsis/uninstaller.nsi | 30 +- .../locales/en-US/installer/custom.properties | 9 +- .../mozapps/installer/windows/nsis/common.nsh | 340 +++++++++++++++++- 5 files changed, 503 insertions(+), 179 deletions(-) diff --git a/browser/installer/windows/nsis/installer.nsi b/browser/installer/windows/nsis/installer.nsi index bb3b796a1ff..7a65ebaea16 100755 --- a/browser/installer/windows/nsis/installer.nsi +++ b/browser/installer/windows/nsis/installer.nsi @@ -111,7 +111,9 @@ VIAddVersionKey "FileDescription" "${BrandShortName} Installer" !insertmacro AddDDEHandlerValues !insertmacro ChangeMUIHeaderImage +!insertmacro CheckForFilesInUse !insertmacro CloseApp +!insertmacro CopyFilesFromDir !insertmacro CreateRegKey !insertmacro GetPathFromString !insertmacro GetParent @@ -194,6 +196,7 @@ Page custom preShortcuts leaveShortcuts ; Start Menu Folder Page Configuration !define MUI_PAGE_CUSTOMFUNCTION_PRE preStartMenu +!define MUI_PAGE_CUSTOMFUNCTION_LEAVE leaveStartMenu !define MUI_STARTMENUPAGE_NODISABLE !define MUI_STARTMENUPAGE_REGISTRY_ROOT "HKLM" !define MUI_STARTMENUPAGE_REGISTRY_KEY "Software\Mozilla\${BrandFullNameInternal}\${AppVersion} (${AB_CD})\Main" @@ -207,7 +210,6 @@ Page custom preSummary leaveSummary !insertmacro MUI_PAGE_INSTFILES ; Finish Page -!define MUI_FINISHPAGE_NOREBOOTSUPPORT !define MUI_FINISHPAGE_TITLE_3LINES !define MUI_FINISHPAGE_RUN !define MUI_FINISHPAGE_RUN_FUNCTION LaunchApp @@ -215,6 +217,9 @@ Page custom preSummary leaveSummary !define MUI_PAGE_CUSTOMFUNCTION_PRE preFinish !insertmacro MUI_PAGE_FINISH +; Use the default dialog for IDD_VERIFY for a simple Banner +ChangeUI IDD_VERIFY "${NSISDIR}\Contrib\UIs\default.exe" + ################################################################################ # Install Sections @@ -227,55 +232,17 @@ Section "-InstallStartCleanup" SetOutPath "$INSTDIR" ${StartInstallLog} "${BrandFullName}" "${AB_CD}" "${AppVersion}" "${GREVersion}" - ; Try to delete the app's main executable and if we can't delete it try to - ; close the app. This allows running an instance that is located in another - ; directory and prevents the launching of the app during the installation. - ; A copy of the executable is placed in a temporary directory so it can be - ; copied back in the case where a specific file is checked / found to be in - ; use that would prevent a successful install. - - ; Create a temporary backup directory. - GetTempFileName $TmpVal "$TEMP" - ${DeleteFile} "$TmpVal" - SetOutPath "$TmpVal" - - ${If} ${FileExists} "$INSTDIR\${FileMainEXE}" - ClearErrors - CopyFiles /SILENT "$INSTDIR\${FileMainEXE}" "$TmpVal\${FileMainEXE}" - Delete "$INSTDIR\${FileMainEXE}" - ${If} ${Errors} - ClearErrors - ${CloseApp} "true" $(WARN_APP_RUNNING_INSTALL) - ; Try to delete it again to prevent launching the app while we are - ; installing. - ClearErrors - CopyFiles /SILENT "$INSTDIR\${FileMainEXE}" "$TmpVal\${FileMainEXE}" - Delete "$INSTDIR\${FileMainEXE}" - ${If} ${Errors} - ClearErrors - ; Try closing the app a second time - ${CloseApp} "true" $(WARN_APP_RUNNING_INSTALL) - StrCpy $R1 "${FileMainEXE}" - Call CheckInUse - ${EndIf} - ${EndIf} - ${EndIf} - - StrCpy $R1 "freebl3.dll" - Call CheckInUse - - StrCpy $R1 "nssckbi.dll" - Call CheckInUse - - StrCpy $R1 "nspr4.dll" - Call CheckInUse - - StrCpy $R1 "xpicleanup.exe" - Call CheckInUse - - SetOutPath "$INSTDIR" - RmDir /r "$TmpVal" + ; Delete the app exe to prevent launching the app while we are installing. ClearErrors + ${DeleteFile} "$INSTDIR\${FileMainEXE}" + ${If} ${Errors} + ; If the user closed the application it can take several seconds for it to + ; shut down completely. If the application is being used by another user we + ; can rename the file and then delete is when the system is restarted. + Sleep 5000 + ${DeleteFile} "$INSTDIR\${FileMainEXE}" + ClearErrors + ${EndIf} ${If} $InstallType == ${INSTALLTYPE_CUSTOM} ; Custom installs. @@ -299,9 +266,9 @@ Section "-Application" APP_IDX SetDetailsPrint none ${LogHeader} "Installing Main Files" - StrCpy $R0 "$EXEDIR\nonlocalized" - StrCpy $R1 "$INSTDIR" - Call DoCopyFiles + ${CopyFilesFromDir} "$EXEDIR\nonlocalized" "$INSTDIR" \ + "$(ERROR_CREATE_DIRECTORY_PREFIX)" \ + "$(ERROR_CREATE_DIRECTORY_SUFFIX)" ; Register DLLs ; XXXrstrong - AccessibleMarshal.dll can be used by multiple applications but @@ -336,9 +303,9 @@ Section "-Application" APP_IDX SetDetailsPrint none ${LogHeader} "Installing Localized Files" - StrCpy $R0 "$EXEDIR\localized" - StrCpy $R1 "$INSTDIR" - Call DoCopyFiles + ${CopyFilesFromDir} "$EXEDIR\localized" "$INSTDIR" \ + "$(ERROR_CREATE_DIRECTORY_PREFIX)" \ + "$(ERROR_CREATE_DIRECTORY_SUFFIX)" ${LogHeader} "Adding Additional Files" ; Check if QuickTime is installed and copy the nsIQTScriptablePlugin.xpt from @@ -505,9 +472,10 @@ Section /o "Developer Tools" DOMI_IDX ${RemoveDir} "$INSTDIR\extensions\inspector@mozilla.org" ClearErrors ${LogHeader} "Installing Developer Tools" - StrCpy $R0 "$EXEDIR\optional\extensions\inspector@mozilla.org" - StrCpy $R1 "$INSTDIR\extensions\inspector@mozilla.org" - Call DoCopyFiles + ${CopyFilesFromDir} "$EXEDIR\optional\extensions\inspector@mozilla.org" \ + "$INSTDIR\extensions\inspector@mozilla.org" \ + "$(ERROR_CREATE_DIRECTORY_PREFIX)" \ + "$(ERROR_CREATE_DIRECTORY_SUFFIX)" ${EndIf} SectionEnd @@ -518,112 +486,82 @@ Section "-InstallEndCleanup" SetDetailsPrint none ${LogHeader} "Updating Uninstall Log With Previous Uninstall Log" - ${InstallEndCleanupCommon} - ; Refresh desktop icons System::Call "shell32::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)" + + ${InstallEndCleanupCommon} + + ; If we have to reboot give SHChangeNotify time to finish the refreshing + ; the icons so the OS doesn't display the icons from helper.exe + ${If} ${RebootFlag} + Sleep 10000 + ${LogHeader} "Reboot Required To Finish Installation" + ; ${FileMainEXE}.moz-upgrade should never exist but just in case... + ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}.moz-upgrade" + Rename "$INSTDIR\${FileMainEXE}" "$INSTDIR\${FileMainEXE}.moz-upgrade" + ${EndUnless} + + ${If} ${FileExists} "$INSTDIR\${FileMainEXE}" + ClearErrors + Rename "$INSTDIR\${FileMainEXE}" "$INSTDIR\${FileMainEXE}.moz-delete" + ${Unless} ${Errors} + Delete /REBOOTOK "$INSTDIR\${FileMainEXE}.moz-delete" + ${EndUnless} + ${EndUnless} + + ${Unless} ${FileExists} "$INSTDIR\${FileMainEXE}" + CopyFiles /SILENT "$INSTDIR\uninstall\helper.exe" "$INSTDIR" + FileOpen $0 "$INSTDIR\${FileMainEXE}" w + FileWrite $0 "Will be deleted on restart" + Rename /REBOOTOK "$INSTDIR\${FileMainEXE}.moz-upgrade" "$INSTDIR\${FileMainEXE}" + FileClose $0 + Delete "$INSTDIR\${FileMainEXE}" + Rename "$INSTDIR\helper.exe" "$INSTDIR\${FileMainEXE}" + ${EndUnless} + ${EndIf} SectionEnd ################################################################################ # Helper Functions -; Copies a file to a temporary backup directory and then checks if it is in use -; by attempting to delete the file. If the file is in use an error is displayed -; and the user is given the options to either retry or cancel. If cancel is -; selected then the files are restored. -Function CheckInUse - ${If} ${FileExists} "$INSTDIR\$R1" - retry: - ClearErrors - CopyFiles /SILENT "$INSTDIR\$R1" "$TmpVal\$R1" - ${Unless} ${Errors} - Delete "$INSTDIR\$R1" - ${EndUnless} - ${If} ${Errors} - StrCpy $0 "$INSTDIR\$R1" - ${WordReplace} "$(^FileError_NoIgnore)" "\r\n" "$\r$\n" "+*" $0 - MessageBox MB_RETRYCANCEL|MB_ICONQUESTION "$0" IDRETRY retry - Delete "$TmpVal\$R1" - CopyFiles /SILENT "$TmpVal\*" "$INSTDIR\" - SetOutPath "$INSTDIR" - RmDir /r "$TmpVal" - ${OnEndCommon} - Quit +Function CheckExistingInstall + ; If there is a pending file copy from a previous uninstall don't allow + ; installing until after the system has rebooted. + IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +4 + MessageBox MB_YESNO "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2 + Reboot + Quit + + ; If there is a pending file deletion from a previous uninstall don't allow + ; installing until after the system has rebooted. + IfFileExists "$INSTDIR\${FileMainEXE}.moz-delete" +1 +4 + MessageBox MB_YESNO "$(WARN_RESTART_REQUIRED_UNINSTALL)" IDNO +2 + Reboot + Quit + + ${If} ${FileExists} "$INSTDIR\${FileMainEXE}" + Banner::show /NOUNLOAD "$(BANNER_CHECK_EXISTING)" + + ${If} "$TmpVal" == "FoundMessageWindow" + Sleep 5000 + ${EndIf} + + ${PushFilesToCheck} + + ; Store the return value in $TmpVal so it is less likely to be accidentally + ; overwritten elsewhere. + ${CheckForFilesInUse} $TmpVal + + Banner::destroy + + ${If} "$TmpVal" == "true" + StrCpy $TmpVal "FoundMessageWindow" + ${ManualCloseAppPrompt} "${WindowClass}" "$(WARN_MANUALLY_CLOSE_APP_INSTALL)" + StrCpy $TmpVal "true" ${EndIf} ${EndIf} FunctionEnd -Function DoCopyFiles - StrLen $R2 $R0 - ${LocateNoDetails} "$R0" "/L=FD" "CopyFile" -FunctionEnd - -Function CopyFile - StrCpy $R3 $R8 "" $R2 - retry: - ClearErrors - ${If} $R6 == "" - ${Unless} ${FileExists} "$R1$R3\$R7" - ClearErrors - CreateDirectory "$R1$R3\$R7" - ${If} ${Errors} - ${LogMsg} "** ERROR Creating Directory: $R1$R3\$R7 **" - StrCpy $0 "$R1$R3\$R7" - StrCpy $0 "$(ERROR_CREATE_DIRECTORY)" - MessageBox MB_RETRYCANCEL|MB_ICONQUESTION "$0" IDRETRY retry - Quit - ${Else} - ${LogMsg} "Created Directory: $R1$R3\$R7" - ${EndIf} - ${EndUnless} - ${Else} - ${Unless} ${FileExists} "$R1$R3" - ClearErrors - CreateDirectory "$R1$R3" - ${If} ${Errors} - ${LogMsg} "** ERROR Creating Directory: $R1$R3 **" - StrCpy $0 "$R1$R3" - StrCpy $0 "$(ERROR_CREATE_DIRECTORY)" - MessageBox MB_RETRYCANCEL|MB_ICONQUESTION "$0" IDRETRY retry - Quit - ${Else} - ${LogMsg} "Created Directory: $R1$R3" - ${EndIf} - ${EndUnless} - ${If} ${FileExists} "$R1$R3\$R7" - ClearErrors - Delete "$R1$R3\$R7" - ${If} ${Errors} - ${LogMsg} "** ERROR Deleting File: $R1$R3\$R7 **" - StrCpy $0 "$R1$R3\$R7" - ${WordReplace} "$(^FileError_NoIgnore)" "\r\n" "$\r$\n" "+*" $0 - MessageBox MB_RETRYCANCEL|MB_ICONQUESTION "$0" IDRETRY retry - Quit - ${EndIf} - ${EndIf} - ClearErrors - - CopyFiles /SILENT $R9 "$R1$R3" - - ${If} ${Errors} - ${LogMsg} "** ERROR Installing File: $R1$R3\$R7 **" - StrCpy $0 "$R1$R3\$R7" - ${WordReplace} "$(^FileError_NoIgnore)" "\r\n" "$\r$\n" "+*" $0 - MessageBox MB_RETRYCANCEL|MB_ICONQUESTION "$0" IDRETRY retry - Quit - ${Else} - ${LogMsg} "Installed File: $R1$R3\$R7" - ${EndIf} - ; If the file is installed into the installation directory remove the - ; installation directory's path from the file path when writing to the - ; uninstall.log so it will be a relative path. This allows the same - ; helper.exe to be used with zip builds if we supply an uninstall.log. - ${WordReplace} "$R1$R3\$R7" "$INSTDIR" "" "+" $R3 - ${LogUninstall} "File: $R3" - ${EndIf} - Push 0 -FunctionEnd - Function LaunchApp ClearErrors ${GetParameters} $0 @@ -701,6 +639,10 @@ Function leaveOptions ${MUI_INSTALLOPTIONS_READ} $R0 "options.ini" "Field 3" "State" StrCmp $R0 "1" +1 +2 StrCpy $InstallType ${INSTALLTYPE_CUSTOM} + + ${If} $InstallType != ${INSTALLTYPE_CUSTOM} + Call CheckExistingInstall + ${EndIf} FunctionEnd Function preComponents @@ -751,6 +693,12 @@ Function preStartMenu ${EndIf} FunctionEnd +Function leaveStartMenu + ${If} $InstallType == ${INSTALLTYPE_CUSTOM} + Call CheckExistingInstall + ${EndIf} +FunctionEnd + Function preSummary !insertmacro createSummaryINI !insertmacro MUI_HEADER_TEXT "$(SUMMARY_PAGE_TITLE)" "$(SUMMARY_PAGE_SUBTITLE)" @@ -760,21 +708,27 @@ Function preSummary !insertmacro MUI_INSTALLOPTIONS_INITDIALOG "summary.ini" GetDlgItem $0 $HWNDPARENT 1 System::Call "user32::SetFocus(i r0, i 0x0007, i,i)i" + ${MUI_INSTALLOPTIONS_READ} $1 "summary.ini" "Field 2" "HWND" + SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR" !insertmacro MUI_INSTALLOPTIONS_SHOW FunctionEnd Function leaveSummary - ; If there is a pending deletion from a previous uninstall don't allow - ; installing until after the system has rebooted. - IfFileExists "$INSTDIR\${FileMainEXE}.moz-delete" +1 +4 - MessageBox MB_YESNO "$(WARN_RESTART_REQUIRED_UNINSTALL)" IDNO +2 - Reboot - Quit - ${If} $InstallType != ${INSTALLTYPE_CUSTOM} ; Set DOMi to be installed SectionSetFlags ${DOMI_IDX} 1 ${EndIf} + + ; Try to delete the app executable and if we can't delete it try to find the + ; app's message window and prompt the user to close the app. This allows + ; running an instance that is located in another directory. If for whatever + ; reason there is no message window we will just rename the app's files and + ; then remove them on restart. + ClearErrors + ${DeleteFile} "$INSTDIR\${FileMainEXE}" + ${If} ${Errors} + ${ManualCloseAppPrompt} "${WindowClass}" "$(WARN_MANUALLY_CLOSE_APP_INSTALL)" + ${EndIf} FunctionEnd ; When we add an optional action to the finish page the cancel button is diff --git a/browser/installer/windows/nsis/shared.nsh b/browser/installer/windows/nsis/shared.nsh index 9a439f88d12..948932e7cd5 100755 --- a/browser/installer/windows/nsis/shared.nsh +++ b/browser/installer/windows/nsis/shared.nsh @@ -549,3 +549,32 @@ ${EndIf} !macroend !define RemoveDeprecatedKeys "!insertmacro RemoveDeprecatedKeys" + +; The files to check if they are in use during (un)install so the restart is +; required message is displayed. +!macro PushFilesToCheck + ; The first string to be pushed onto the stack MUST be "end" to indicate + ; that there are no more relative file paths to check and the last string + ; should be ${FileMainEXE} so if it is in use the CheckForFilesInUse macro + ; returns after the first check. + Push "end" + Push "chrome\browser.jar" + Push "chrome\classic.jar" + Push "chrome\comm.jar" + Push "chrome\${AB_CD}.jar" + Push "chrome\pippki.jar" + Push "chrome\reporter.jar" + Push "chrome\toolkit.jar" + Push "AccessibleMarshal.dll" + Push "freebl3.dll" + Push "nssckbi.dll" + Push "nspr4.dll" + Push "nssdbm3.dll" + Push "sqlite3.dll" + Push "xpcom.dll" + Push "crashreporter.exe" + Push "updater.exe" + Push "xpicleanup.exe" + Push "${FileMainEXE}" +!macroend +!define PushFilesToCheck "!insertmacro PushFilesToCheck" diff --git a/browser/installer/windows/nsis/uninstaller.nsi b/browser/installer/windows/nsis/uninstaller.nsi index f5517e200e3..909d0920e32 100755 --- a/browser/installer/windows/nsis/uninstaller.nsi +++ b/browser/installer/windows/nsis/uninstaller.nsi @@ -109,6 +109,7 @@ VIAddVersionKey "FileDescription" "${BrandShortName} Helper" !insertmacro WriteRegStr2 !insertmacro un.ChangeMUIHeaderImage +!insertmacro un.CheckForFilesInUse !insertmacro un.CleanVirtualStore !insertmacro un.DeleteRelativeProfiles !insertmacro un.GetLongPath @@ -160,6 +161,7 @@ ShowUnInstDetails nevershow */ ; Welcome Page !define MUI_PAGE_CUSTOMFUNCTION_PRE un.preWelcome +!define MUI_PAGE_CUSTOMFUNCTION_LEAVE un.leaveWelcome !insertmacro MUI_UNPAGE_WELCOME ; Uninstall Confirm Page @@ -182,6 +184,9 @@ UninstPage custom un.preConfirm un.leaveConfirm !insertmacro MUI_UNPAGE_FINISH +; Use the default dialog for IDD_VERIFY for a simple Banner +ChangeUI IDD_VERIFY "${NSISDIR}\Contrib\UIs\default.exe" + ################################################################################ # Install Sections ; Empty section required for the installer to compile as an uninstaller @@ -375,6 +380,28 @@ Function un.preWelcome ${EndIf} FunctionEnd +Function un.leaveWelcome + ${If} ${FileExists} "$INSTDIR\${FileMainEXE}" + Banner::show /NOUNLOAD "$(BANNER_CHECK_EXISTING)" + + ${If} "$TmpVal" == "FoundMessageWindow" + Sleep 5000 + ${EndIf} + + ${PushFilesToCheck} + + ${un.CheckForFilesInUse} $TmpVal + + Banner::destroy + + ${If} "$TmpVal" == "true" + StrCpy $TmpVal "FoundMessageWindow" + ${un.ManualCloseAppPrompt} "${WindowClass}" "$(WARN_MANUALLY_CLOSE_APP_UNINSTALL)" + StrCpy $TmpVal "true" + ${EndIf} + ${EndIf} +FunctionEnd + Function un.preConfirm ${If} ${FileExists} "$INSTDIR\distribution\modern-header.bmp" ${AndIf} $hHeaderBitmap == "" @@ -393,6 +420,8 @@ Function un.preConfirm SetCtlColors $1 0x000000 0xFFFFEE ShowWindow $1 ${SW_HIDE} System::Call "user32::SetFocus(i r0, i 0x0007, i,i)i" + ${MUI_INSTALLOPTIONS_READ} $1 "unconfirm.ini" "Field 2" "HWND" + SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR" !insertmacro MUI_INSTALLOPTIONS_SHOW FunctionEnd @@ -415,7 +444,6 @@ Function un.leaveConfirm ; running an instance that is located in another directory. If for whatever ; reason there is no message window we will just rename the app's files and ; then remove them on restart if they are in use. - StrCpy $TmpVal "" ClearErrors ${DeleteFile} "$INSTDIR\${FileMainEXE}" ${If} ${Errors} diff --git a/browser/locales/en-US/installer/custom.properties b/browser/locales/en-US/installer/custom.properties index dede5dcb327..5a183a6fe14 100755 --- a/browser/locales/en-US/installer/custom.properties +++ b/browser/locales/en-US/installer/custom.properties @@ -66,6 +66,8 @@ SHORTCUTS_PAGE_SUBTITLE=Create Program Icons SUMMARY_PAGE_TITLE=Summary SUMMARY_PAGE_SUBTITLE=Ready to start installing $BrandShortName SUMMARY_INSTALLED_TO=$BrandShortName will be installed to the following location: +SUMMARY_REBOOT_REQUIRED_INSTALL=A restart of your computer may be required to complete the installation. +SUMMARY_REBOOT_REQUIRED_UNINSTALL=A restart of your computer may be required to complete the uninstall. SUMMARY_CLICK=Click Install to continue. SURVEY_TEXT=&Tell us what you thought of $BrandShortName LAUNCH_TEXT=&Launch $BrandShortName now @@ -74,13 +76,16 @@ CREATE_ICONS_DESC=Create icons for $BrandShortName: ICONS_DESKTOP=On my &Desktop ICONS_STARTMENU=In my &Start Menu Programs folder ICONS_QUICKLAUNCH=In my &Quick Launch bar +WARN_MANUALLY_CLOSE_APP_INSTALL=$BrandShortName must be closed to proceed with the installation.\n\nPlease close $BrandShortName to continue. WARN_MANUALLY_CLOSE_APP_UNINSTALL=$BrandShortName must be closed to proceed with the uninstall.\n\nPlease close $BrandShortName to continue. WARN_MANUALLY_CLOSE_APP_LAUNCH=$BrandShortName is already running.\n\nPlease close $BrandShortName prior to launching the version you have just installed. WARN_WRITE_ACCESS=You don't have access to write to the installation directory.\n\nClick OK to select a different directory. WARN_DISK_SPACE=You don't have sufficient disk space to install to this location.\n\nClick OK to select a different location. WARN_UNSUPPORTED_MSG=Sorry, $BrandShortName can't be installed. This version of $BrandShortName requires ${MinUnsupportedVer} or newer. WARN_RESTART_REQUIRED_UNINSTALL=Your computer must be restarted to complete a previous uninstall of $BrandShortName. Do you want to reboot now? -ERROR_CREATE_DIRECTORY=Error creating directory:\n\n$0\n\nClick Cancel to stop the installation or\nRetry to try again. +WARN_RESTART_REQUIRED_UPGRADE=Your computer must be restarted to complete a previous upgrade of $BrandShortName. Do you want to reboot now? +ERROR_CREATE_DIRECTORY_PREFIX=Error creating directory: +ERROR_CREATE_DIRECTORY_SUFFIX=Click Cancel to stop the installation or\nRetry to try again. UN_CONFIRM_PAGE_TITLE=Uninstall $BrandFullName UN_CONFIRM_PAGE_SUBTITLE=Remove $BrandFullName from your computer. @@ -89,6 +94,8 @@ UN_CONFIRM_CLICK=Click Uninstall to continue. UN_REMOVE_PROFILES=&Remove my $BrandShortName personal data and customizations UN_REMOVE_PROFILES_DESC=This will permanently remove your bookmarks, saved passwords, cookies and customizations. You may wish to keep this information if you plan on installing another version of $BrandShortName in the future. +BANNER_CHECK_EXISTING=Checking existing installation… + STATUS_INSTALL_APP=Installing $BrandShortName… STATUS_INSTALL_LANG=Installing Language Files (${AB_CD})… STATUS_INSTALL_OPTIONAL=Installing Optional Components… diff --git a/toolkit/mozapps/installer/windows/nsis/common.nsh b/toolkit/mozapps/installer/windows/nsis/common.nsh index b32cf09c8a8..b72ea078951 100755 --- a/toolkit/mozapps/installer/windows/nsis/common.nsh +++ b/toolkit/mozapps/installer/windows/nsis/common.nsh @@ -490,13 +490,11 @@ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Top "5" WriteINIStr "$PLUGINSDIR\summary.ini" "Field 1" Bottom "15" - ; XXXrstrong - a side affect of using a READONLY textbox is if the path is - ; longer than the visible area of the textbox it will display the characters - ; at the end and the beginning of the path will be hidden. Since the path has - ; to be greater than 74 characters in length I'm not going to spend any - ; cycles trying to come up with a workaround. WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Type "text" - WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" state "$INSTDIR" + ; The contents of this control must be set as follows in the pre function + ; ${MUI_INSTALLOPTIONS_READ} $1 "summary.ini" "Field 2" "HWND" + ; SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" state "" WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Left "0" WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Right "-1" WriteINIStr "$PLUGINSDIR\summary.ini" "Field 2" Top "17" @@ -509,6 +507,18 @@ WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Right "-1" WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Top "130" WriteINIStr "$PLUGINSDIR\summary.ini" "Field 3" Bottom "150" + + ${If} "$TmpVal" == "true" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Type "label" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Text "$(SUMMARY_REBOOT_REQUIRED_INSTALL)" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Left "0" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Right "-1" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Top "35" + WriteINIStr "$PLUGINSDIR\summary.ini" "Field 4" Bottom "45" + + WriteINIStr "$PLUGINSDIR\summary.ini" "Settings" NumFields "4" + ${EndIf} + !macroend !macro un.createUnConfirmINI @@ -521,13 +531,11 @@ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Top "5" WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 1" Bottom "15" - ; XXXrstrong - a side affect of using a READONLY textbox is if the path is - ; longer than the visible area of the textbox it will display the characters - ; at the end and the beginning of the path will be hidden. Since the path has - ; to be greater than 74 characters in length I'm not going to spend any - ; cycles trying to come up with a workaround. WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Type "text" - WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" State "$INSTDIR" + ; The contents of this control must be set as follows in the pre function + ; ${MUI_INSTALLOPTIONS_READ} $1 "unconfirm.ini" "Field 2" "HWND" + ; SendMessage $1 ${WM_SETTEXT} 0 "STR:$INSTDIR" + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" State "" WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Left "0" WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Right "-1" WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 2" Top "17" @@ -557,6 +565,22 @@ WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 5" Right "-1" WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 5" Top "130" WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 5" Bottom "150" + + ${If} "$TmpVal" == "true" + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Type "label" + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Text "$(SUMMARY_REBOOT_REQUIRED_UNINSTALL)" + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Left "0" + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Right "-1" + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Top "35" + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 6" Bottom "45" + + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Settings" NumFields "6" + + ; To insert this control reset Top / Bottom for controls below this one + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Top "55" + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 3" Bottom "65" + WriteINIStr "$PLUGINSDIR\unconfirm.ini" "Field 4" Top "67" + ${EndIf} !macroend /** @@ -630,6 +654,105 @@ ################################################################################ # Macros for handling files in use +/** + * Checks for files in use. + * + * Example usage: + * + * ; The first string to be pushed onto the stack MUST be "end" to indicate + * ; that there are no more relative file paths to check. + * Push "end" + * Push "chrome\toolkit.jar" + * Push "freebl3.dll" + * ; The last file pushed should be the app's main exe so if it is in use this + * ; macro will return after the first check. + * Push "${FileMainEXE}" + * ${CheckForFilesInUse} $R9 + * + * !IMPORTANT - this macro uses the $R7, $R8, and $R9 registers and makes no + * attempt to restore their original values. + * + * @return _RESULT + * false if all of the files popped from the stack are not in use. + * True if any of the files popped from the stack are in use. + * $R7 = Temporary backup directory where the files will be copied to. + * $R8 = value popped from the stack. This will either be a relative file path + * from the $INSTDIR or "end" to indicate that there are no additional + * files to check. + * $R9 = _RESULT + */ +!macro CheckForFilesInUse + + !ifndef ${_MOZFUNC_UN}CheckForFilesInUse + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define ${_MOZFUNC_UN}CheckForFilesInUse "!insertmacro ${_MOZFUNC_UN}CheckForFilesInUseCall" + + Function ${_MOZFUNC_UN}CheckForFilesInUse + ; Create a temporary backup directory. + GetTempFileName $R7 "$INSTDIR" + Delete "$R7" + SetOutPath "$R7" + StrCpy $R9 "false" + + start: + Pop $R8 + StrCmp "$R8" "end" end +1 + IfFileExists "$INSTDIR\$R8" +1 start + + ClearErrors + CopyFiles /SILENT "$INSTDIR\$R8" "$R7\$R8" + IfErrors +4 +1 + Delete "$INSTDIR\$R8" + IfErrors +1 start + Delete "$R7\$R8" + + StrCpy $R9 "true" + + end: + SetOutPath "$INSTDIR" + CopyFiles /SILENT "$R7\*" "$INSTDIR\" + RmDir /r "$R7" + ClearErrors + + Push $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CheckForFilesInUseCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call CheckForFilesInUse + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.CheckForFilesInUseCall _RESULT + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Call un.CheckForFilesInUse + Pop ${_RESULT} + !verbose pop +!macroend + +!macro un.CheckForFilesInUse + !ifndef un.CheckForFilesInUse + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !undef _MOZFUNC_UN + !define _MOZFUNC_UN "un." + + !insertmacro CheckForFilesInUse + + !undef _MOZFUNC_UN + !define _MOZFUNC_UN + !verbose pop + !endif +!macroend + /** * The macros below will automatically prepend un. to the function names when * they are defined (e.g. !define un.RegCleanMain). @@ -3470,6 +3593,176 @@ !verbose pop !macroend +/** + * Copies files from a source directory to a destination directory with logging + * to the uninstall.log. If any destination files are in use a reboot will be + * necessary to complete the installation and the reboot flag (see IfRebootFlag + * in the NSIS documentation). + * + * @param _PATH_TO_SOURCE + * Source path to copy the files from. This must not end with a \. + * + * @param _PATH_TO_DESTINATION + * Destination path to copy the files to. This must not end with a \. + * + * @param _PREFIX_ERROR_CREATEDIR + * Prefix for the directory creation error message. The directory path + * will be inserted below this string. + * + * @param _SUFFIX_ERROR_CREATEDIR + * Prefix for the directory creation error message. The directory path + * will be inserted above this string. + * + * $0 = destination file's parent directory used in the create_dir label + * $R0 = copied value from $R6 (e.g. _PATH_TO_SOURCE) + * $R1 = copied value from $R7 (e.g. _PATH_TO_DESTINATION) + * $R2 = string length of the path to source + * $R3 = relative path from the path to source + * $R4 = copied value from $R8 (e.g. _PREFIX_ERROR_CREATEDIR) + * $R5 = copied value from $R9 (e.g. _SUFFIX_ERROR_CREATEDIR) + * note: the LocateNoDetails macro uses these registers so we copy the values + * to other registers. + * $R6 = initially _PATH_TO_SOURCE and then set to "size" ($R6="" if directory, + * $R6="0" if file with /S=)"path\name" in callback + * $R7 = initially _PATH_TO_DESTINATION and then set to "name" in callback + * $R8 = initially _PREFIX_ERROR_CREATEDIR and then set to "path" in callback + * $R9 = initially _SUFFIX_ERROR_CREATEDIR and then set to "path\name" in + * callback + */ +!macro CopyFilesFromDir + + !ifndef CopyFilesFromDir + !insertmacro LocateNoDetails + !insertmacro OnEndCommon + !insertmacro WordReplace + + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define CopyFilesFromDir "!insertmacro CopyFilesFromDirCall" + + Function CopyFilesFromDir + Exch $R9 + Exch 1 + Exch $R8 + Exch 2 + Exch $R7 + Exch 3 + Exch $R6 + Push $R5 + Push $R4 + Push $R3 + Push $R2 + Push $R1 + Push $R0 + Push $0 + + StrCpy $R0 "$R6" + StrCpy $R1 "$R7" + StrCpy $R4 "$R8" + StrCpy $R5 "$R9" + + StrLen $R2 $R0 + + ${LocateNoDetails} "$R0" "/L=FD" "CopyFileCallback" + + Pop $0 + Pop $R0 + Pop $R1 + Pop $R2 + Pop $R3 + Pop $R4 + Pop $R5 + Exch $R6 + Exch 3 + Exch $R7 + Exch 2 + Exch $R8 + Exch 1 + Exch $R9 + FunctionEnd + + Function CopyFileCallback + StrCpy $R3 $R8 "" $R2 ; $R3 always begins with a \. + + retry: + ClearErrors + StrCmp $R6 "" +1 copy_file + IfFileExists "$R1$R3\$R7" end +1 + StrCpy $0 "$R1$R3\$R7" + + create_dir: + ClearErrors + CreateDirectory "$0" + IfFileExists "$0" +1 err_create_dir ; protect against looping. + ${LogMsg} "Created Directory: $0" + StrCmp $R6 "" end copy_file + + err_create_dir: + ${LogMsg} "** ERROR Creating Directory: $0 **" + MessageBox MB_RETRYCANCEL|MB_ICONQUESTION "$R4$\r$\n$\r$\n$0$\r$\n$\r$\n$R5" IDRETRY retry + ${OnEndCommon} + Quit + + copy_file: + StrCpy $0 "$R1$R3" + IfFileExists "$0" +1 create_dir + + ClearErrors + ${DeleteFile} "$R1$R3\$R7" + IfErrors +1 dest_clear + ClearErrors + Rename "$R1$R3\$R7" "$R1$R3\$R7.moz-delete" + IfErrors +1 reboot_delete + + ; file will replace destination file on reboot + Rename "$R9" "$R9.moz-upgrade" + CopyFiles /SILENT "$R9.moz-upgrade" "$R1$R3" + Rename /REBOOTOK "$R1$R3\$R7.moz-upgrade" "$R1$R3\$R7" + ${LogMsg} "Copied File: $R1$R3\$R7.moz-upgrade" + ${LogMsg} "Delayed Install File (Reboot Required): $R1$R3\$R7" + GoTo log_uninstall + + ; file will be deleted on reboot + reboot_delete: + CopyFiles /SILENT $R9 "$R1$R3" + Delete /REBOOTOK "$R1$R3\$R7.moz-delete" + ${LogMsg} "Installed File: $R1$R3\$R7" + ${LogMsg} "Delayed Delete File (Reboot Required): $R1$R3\$R7.moz-delete" + GoTo log_uninstall + + ; destination file doesn't exist - coast is clear + dest_clear: + CopyFiles /SILENT $R9 "$R1$R3" + ${LogMsg} "Installed File: $R1$R3\$R7" + + log_uninstall: + ; If the file is installed into the installation directory remove the + ; installation directory's path from the file path when writing to the + ; uninstall.log so it will be a relative path. This allows the same + ; helper.exe to be used with zip builds if we supply an uninstall.log. + ${WordReplace} "$R1$R3\$R7" "$INSTDIR" "" "+" $R3 + ${LogUninstall} "File: $R3" + + end: + Push 0 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro CopyFilesFromDirCall _PATH_TO_SOURCE _PATH_TO_DESTINATION \ + _PREFIX_ERROR_CREATEDIR _SUFFIX_ERROR_CREATEDIR + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_PATH_TO_SOURCE}" + Push "${_PATH_TO_DESTINATION}" + Push "${_PREFIX_ERROR_CREATEDIR}" + Push "${_SUFFIX_ERROR_CREATEDIR}" + Call CopyFilesFromDir + !verbose pop +!macroend + /** * Parses the uninstall.log to unregister dll's, remove files, and remove * empty directories for this installation. @@ -4132,10 +4425,24 @@ Function UninstallOnInitCommon + ; Prevent launching the application when a reboot is required and this + ; executable is the main application executable + IfFileExists "$EXEDIR\${FileMainEXE}.moz-upgrade" +1 +4 + MessageBox MB_YESNO "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2 + Reboot + Quit ; Nothing initialized so no need to call OnEndCommon + GetFullPathName $INSTDIR "$EXEDIR\.." ${GetLongPath} "$INSTDIR" $INSTDIR IfFileExists "$INSTDIR\${FileMainEXE}" +2 +1 - Abort + Quit ; Nothing initialized so no need to call OnEndCommon + + ; Prevent all operations (e.g. set as default, postupdate, etc.) when a + ; reboot is required and the executable launched is helper.exe + IfFileExists "$INSTDIR\${FileMainEXE}.moz-upgrade" +1 +4 + MessageBox MB_YESNO "$(WARN_RESTART_REQUIRED_UPGRADE)" IDNO +2 + Reboot + Quit ; Nothing initialized so no need to call OnEndCommon ${GetParameters} $R0 @@ -4209,14 +4516,14 @@ finish: ${UnloadUAC} System::Call "shell32::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)" - Quit + Quit ; Nothing initialized so no need to call OnEndCommon continue: ; If the uninstall.log does not exist don't perform uninstall ; operations. This prevents running the uninstaller for zip builds. IfFileExists "$INSTDIR\uninstall\uninstall.log" +2 +1 - Quit + Quit ; Nothing initialized so no need to call OnEndCommon ; Require elevation if the user can elevate ${ElevateUAC} @@ -4234,7 +4541,7 @@ ${DeleteFile} "$EXEDIR\uninstaller.exe" ${UnloadUAC} SetErrorLevel 0 - Quit + Quit ; Nothing initialized so no need to call OnEndCommon FunctionEnd @@ -4328,7 +4635,6 @@ * @param _WARN_DISK_SPACE * Message displayed when there isn't enough disk space to perform the * installation. - * $INSTDIR. * @param _WARN_WRITE_ACCESS * Message displayed when the installer does not have write access to * $INSTDIR.