From 0f30b25da070c05b212514c40cacba4515a651db Mon Sep 17 00:00:00 2001 From: Jim Mathies Date: Fri, 14 May 2010 19:24:50 -0500 Subject: [PATCH] Bug 562753 - On upgrade, update AppUserModelID for application shortcuts. r=rstrong. --- .../shell/public/nsIWindowsShellService.idl | 9 +- .../shell/src/nsWindowsShellService.cpp | 157 ++++++++++++++---- .../wintaskbar/WindowsJumpLists.jsm | 13 ++ browser/installer/windows/nsis/installer.nsi | 4 + browser/installer/windows/nsis/shared.nsh | 3 + .../installer/windows/nsis/uninstaller.nsi | 1 + .../mozapps/installer/windows/nsis/common.nsh | 110 ++++++++++++ 7 files changed, 268 insertions(+), 29 deletions(-) diff --git a/browser/components/shell/public/nsIWindowsShellService.idl b/browser/components/shell/public/nsIWindowsShellService.idl index 28e99887d2d..cabb9544e94 100644 --- a/browser/components/shell/public/nsIWindowsShellService.idl +++ b/browser/components/shell/public/nsIWindowsShellService.idl @@ -20,6 +20,7 @@ * * Contributor(s): * Ben Goodger (Original Author) + * Jim Mathies * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -37,7 +38,7 @@ #include "nsIShellService.idl" -[scriptable, uuid(3ee25e62-ad0f-464e-8225-c3b774137387)] +[scriptable, uuid(16e7e8da-8bef-4f41-be5f-045b2e9918e1)] interface nsIWindowsShellService : nsIShellService { /** @@ -46,5 +47,11 @@ interface nsIWindowsShellService : nsIShellService * @return The number of unread (new) mail messages for the current user. */ readonly attribute unsigned long unreadMailCount; + + /** + * Provides the shell service an opportunity to do some Win7+ shortcut + * maintenance needed on initial startup of the browser. + */ + void shortcutMaintenance(); }; diff --git a/browser/components/shell/src/nsWindowsShellService.cpp b/browser/components/shell/src/nsWindowsShellService.cpp index 0d7c36bf785..dd890c3db14 100644 --- a/browser/components/shell/src/nsWindowsShellService.cpp +++ b/browser/components/shell/src/nsWindowsShellService.cpp @@ -27,6 +27,7 @@ * Asaf Romano * Ryan Jones * Paul O'Shannessy + * Jim Mathies * * Alternatively, the contents of this file may be used under the terms of * either the GNU General Public License Version 2 or later (the "GPL"), or @@ -63,6 +64,8 @@ #include "nsDirectoryServiceDefs.h" #include "nsIWindowsRegKey.h" #include "nsUnicharUtils.h" +#include "nsIWinTaskbar.h" +#include "nsISupportsPrimitives.h" #include "windows.h" #include "shellapi.h" @@ -86,6 +89,8 @@ #define REG_FAILED(val) \ (val != ERROR_SUCCESS) +#define NS_TASKBAR_CONTRACTID "@mozilla.org/windows-taskbar;1" + #ifndef WINCE NS_IMPL_ISUPPORTS2(nsWindowsShellService, nsIWindowsShellService, nsIShellService) #else @@ -268,6 +273,127 @@ static SETTING gSettings[] = { { MAKE_KEY_NAME1("HTTPS", SOP), "", VAL_OPEN } }; +#if !defined(WINCE) +nsresult +GetHelperPath(nsAutoString& aPath) +{ + nsresult rv; + nsCOMPtr directoryService = + do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); + NS_ENSURE_SUCCESS(rv, rv); + + nsCOMPtr appHelper; + rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, + NS_GET_IID(nsILocalFile), + getter_AddRefs(appHelper)); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appHelper->AppendNative(NS_LITERAL_CSTRING("uninstall")); + NS_ENSURE_SUCCESS(rv, rv); + + rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe")); + NS_ENSURE_SUCCESS(rv, rv); + + return appHelper->GetPath(aPath); +} + +nsresult +LaunchHelper(nsAutoString& aPath) +{ + STARTUPINFOW si = {sizeof(si), 0}; + PROCESS_INFORMATION pi = {0}; + + BOOL ok = CreateProcessW(NULL, (LPWSTR)aPath.get(), NULL, NULL, + FALSE, 0, NULL, NULL, &si, &pi); + + if (!ok) + return NS_ERROR_FAILURE; + + CloseHandle(pi.hProcess); + CloseHandle(pi.hThread); + return NS_OK; +} +#endif // !defined(WINCE) + +NS_IMETHODIMP +nsWindowsShellService::ShortcutMaintenance() +{ +#if !defined(WINCE) + nsresult rv; + + // Launch helper.exe so it can update the application user model ids on + // shortcuts in the user's taskbar and start menu. This keeps older pinned + // shortcuts grouped correctly after major updates. Note, we also do this + // through the upgrade installer script, however, this is the only place we + // have a chance to trap links created by users who do control the install/ + // update process of the browser. + + nsCOMPtr taskbarInfo = + do_GetService(NS_TASKBAR_CONTRACTID); + if (!taskbarInfo) // If we haven't built with win7 sdk features, this fails. + return NS_OK; + + // Avoid if this isn't Win7+ + PRBool isSupported = PR_FALSE; + taskbarInfo->GetAvailable(&isSupported); + if (!isSupported) + return NS_OK; + + nsAutoString appId; + if (NS_FAILED(taskbarInfo->GetDefaultGroupId(appId))) + return NS_ERROR_UNEXPECTED; + + NS_NAMED_LITERAL_CSTRING(prefName, "browser.taskbar.lastgroupid"); + nsCOMPtr prefs = + do_GetService(NS_PREFSERVICE_CONTRACTID); + if (!prefs) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr prefBranch; + prefs->GetBranch(nsnull, getter_AddRefs(prefBranch)); + if (!prefBranch) + return NS_ERROR_UNEXPECTED; + + nsCOMPtr prefString; + rv = prefBranch->GetComplexValue(prefName.get(), + NS_GET_IID(nsISupportsString), + getter_AddRefs(prefString)); + if (NS_SUCCEEDED(rv)) { + nsAutoString version; + prefString->GetData(version); + if (!version.IsEmpty() && version.Equals(appId)) { + // We're all good, get out of here. + return NS_OK; + } + } + // Update the version in prefs + prefString = + do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID, &rv); + if (NS_FAILED(rv)) + return rv; + + prefString->SetData(appId); + rv = prefBranch->SetComplexValue(prefName.get(), + NS_GET_IID(nsISupportsString), + prefString); + if (NS_FAILED(rv)) { + NS_WARNING("Couldn't set last user model id!"); + return NS_ERROR_UNEXPECTED; + } + + nsAutoString appHelperPath; + if (NS_FAILED(GetHelperPath(appHelperPath))) + return NS_ERROR_UNEXPECTED; + + appHelperPath.AppendLiteral(" /UpdateShortcutAppUserModelIds"); + + return LaunchHelper(appHelperPath); +#else + NS_NOTREACHED("ShortcutMaintenance not implemented on wince!"); + return NS_OK; +#endif // !defined(WINCE) +} + #ifndef WINCE PRBool nsWindowsShellService::IsDefaultBrowserVista(PRBool* aIsDefaultBrowser) @@ -366,24 +492,9 @@ NS_IMETHODIMP nsWindowsShellService::SetDefaultBrowser(PRBool aClaimAllTypes, PRBool aForAllUsers) { #ifndef WINCE - nsresult rv; - nsCOMPtr directoryService = - do_GetService(NS_DIRECTORY_SERVICE_CONTRACTID, &rv); - NS_ENSURE_SUCCESS(rv, rv); - - nsCOMPtr appHelper; - rv = directoryService->Get(NS_XPCOM_CURRENT_PROCESS_DIR, NS_GET_IID(nsILocalFile), getter_AddRefs(appHelper)); - NS_ENSURE_SUCCESS(rv, rv); - - rv = appHelper->AppendNative(NS_LITERAL_CSTRING("uninstall")); - NS_ENSURE_SUCCESS(rv, rv); - - rv = appHelper->AppendNative(NS_LITERAL_CSTRING("helper.exe")); - NS_ENSURE_SUCCESS(rv, rv); - nsAutoString appHelperPath; - rv = appHelper->GetPath(appHelperPath); - NS_ENSURE_SUCCESS(rv, rv); + if (NS_FAILED(GetHelperPath(appHelperPath))) + return NS_ERROR_FAILURE; if (aForAllUsers) { appHelperPath.AppendLiteral(" /SetAsDefaultAppGlobal"); @@ -391,17 +502,7 @@ nsWindowsShellService::SetDefaultBrowser(PRBool aClaimAllTypes, PRBool aForAllUs appHelperPath.AppendLiteral(" /SetAsDefaultAppUser"); } - STARTUPINFOW si = {sizeof(si), 0}; - PROCESS_INFORMATION pi = {0}; - - BOOL ok = CreateProcessW(NULL, (LPWSTR)appHelperPath.get(), NULL, NULL, - FALSE, 0, NULL, NULL, &si, &pi); - - if (!ok) - return NS_ERROR_FAILURE; - - CloseHandle(pi.hProcess); - CloseHandle(pi.hThread); + return LaunchHelper(appHelperPath); #else SETTING* settings; SETTING* end = gSettings + sizeof(gSettings)/sizeof(SETTING); diff --git a/browser/components/wintaskbar/WindowsJumpLists.jsm b/browser/components/wintaskbar/WindowsJumpLists.jsm index f6e9e16f693..0d347b3f94d 100644 --- a/browser/components/wintaskbar/WindowsJumpLists.jsm +++ b/browser/components/wintaskbar/WindowsJumpLists.jsm @@ -101,6 +101,10 @@ XPCOMUtils.defineLazyServiceGetter(this, "_ioService", "@mozilla.org/network/io-service;1", "nsIIOService"); +XPCOMUtils.defineLazyServiceGetter(this, "_winShellService", + "@mozilla.org/browser/shell-service;1", + "nsIWindowsShellService"); + /** * Global functions */ @@ -163,6 +167,11 @@ var WinTaskbarJumpList = if (!this._initTaskbar()) return; + // Win shell shortcut maintenance. If we've gone through an update, + // this will update any pinned taskbar shortcuts. Not specific to + // jump lists, but this was a convienent place to call it. + this._shortcutMaintenance(); + // Store our task list config data this._tasks = tasksCfg; @@ -200,6 +209,10 @@ var WinTaskbarJumpList = this._free(); }, + _shortcutMaintenance: function WTBJL__maintenace() { + _winShellService.shortcutMaintenance(); + }, + /** * List building */ diff --git a/browser/installer/windows/nsis/installer.nsi b/browser/installer/windows/nsis/installer.nsi index 9ba6005aec3..708db80625d 100755 --- a/browser/installer/windows/nsis/installer.nsi +++ b/browser/installer/windows/nsis/installer.nsi @@ -119,6 +119,7 @@ VIAddVersionKey "OriginalFilename" "setup.exe" !insertmacro RegCleanMain !insertmacro RegCleanUninstall !insertmacro SetBrandNameVars +!insertmacro UpdateShortcutAppModelIDs !insertmacro UnloadUAC !insertmacro WriteRegStr2 !insertmacro WriteRegDWORD2 @@ -486,6 +487,9 @@ Section "-InstallEndCleanup" ${LogHeader} "Updating Uninstall Log With Previous Uninstall Log" + ; Win7 taskbar and start menu link maintenance + ${UpdateShortcutAppModelIDs} "$INSTDIR\${FileMainEXE}" "${AppUserModelID}" + ; Refresh desktop icons System::Call "shell32::SHChangeNotify(i, i, i, i) v (0x08000000, 0, 0, 0)" diff --git a/browser/installer/windows/nsis/shared.nsh b/browser/installer/windows/nsis/shared.nsh index fb2a8d6e098..ba16cb79f68 100755 --- a/browser/installer/windows/nsis/shared.nsh +++ b/browser/installer/windows/nsis/shared.nsh @@ -83,6 +83,9 @@ ${FixClassKeys} ${SetUninstallKeys} + ; Win7 taskbar and start menu link maintenance + ${UpdateShortcutAppModelIDs} "$INSTDIR\${FileMainEXE}" "${AppUserModelID}" + ; Remove files that may be left behind by the application in the ; VirtualStore directory. ${CleanVirtualStore} diff --git a/browser/installer/windows/nsis/uninstaller.nsi b/browser/installer/windows/nsis/uninstaller.nsi index 68afbe28935..cf25f3a9da4 100755 --- a/browser/installer/windows/nsis/uninstaller.nsi +++ b/browser/installer/windows/nsis/uninstaller.nsi @@ -101,6 +101,7 @@ VIAddVersionKey "OriginalFilename" "helper.exe" !insertmacro RegCleanMain !insertmacro RegCleanUninstall !insertmacro SetBrandNameVars +!insertmacro UpdateShortcutAppModelIDs !insertmacro UnloadUAC !insertmacro WriteRegDWORD2 !insertmacro WriteRegStr2 diff --git a/toolkit/mozapps/installer/windows/nsis/common.nsh b/toolkit/mozapps/installer/windows/nsis/common.nsh index 7c7d2edafc4..9a15c6e24de 100755 --- a/toolkit/mozapps/installer/windows/nsis/common.nsh +++ b/toolkit/mozapps/installer/windows/nsis/common.nsh @@ -4675,7 +4675,15 @@ StrCmp "$R0" "" continue +1 + ; Update this user's shortcuts with the latest app user model id. + ClearErrors + ${GetOptions} "$R0" "/UpdateShortcutAppUserModelIds" $R2 + IfErrors hideshortcuts +1 + ${UpdateShortcutAppModelIDs} "$INSTDIR\${FileMainEXE}" "${AppUserModelID}" + GoTo finish + ; Require elevation if the user can elevate + hideshortcuts: ClearErrors ${GetOptions} "$R0" "/HideShortcuts" $R2 IfErrors showshortcuts +1 @@ -5780,3 +5788,105 @@ ${DeleteFile} "$INSTDIR\uninstall\${SHORTCUTS_LOG}" !macroend !define DeleteShortcutsLogFile "!insertmacro DeleteShortcutsLogFile" + +################################################################################ +# Macros for managing Win7 install features + +/** + * Update Start Menu and Taskbar lnk files that point to the current install + * with the current application user model ID. Requires ApplicationID. + * + * @param _INSTALL_PATH + * The install path of the app + * @param _APP_ID + * The application user model ID for the current install + */ +!macro UpdateShortcutAppModelIDs + + !ifndef UpdateShortcutAppModelIDs + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + !define UpdateShortcutAppModelIDs "!insertmacro UpdateShortcutAppModelIDsCall" + + Function UpdateShortcutAppModelIDs + ClearErrors + + ; stack: path, appid + Exch $R9 ; stack: $R9, appid | $R9 = path + Exch 1 ; stack: appid, $R9 + Exch $R8 ; stack: $R8, $R9 | $R8 = appid + Push $R7 ; stack: $R7, $R8, $R9 + Push $R6 + Push $R5 + Push $R4 ; stack: $R4, $R5, $R6, $R7, $R8, $R9 + + StrCpy $R7 "$QUICKLAUNCH\User Pinned" + + ClearErrors + + ; $R9 = install path + ; $R8 = appid + ; $R7 = user pinned path + ; $R6 = find handle + ; $R5 = found filename + ; $R4 = GetShortCutTarget result + + ; Taskbar links + FindFirst $R6 $R5 "$R7\TaskBar\*.lnk" + LoopTaskBar: + ${If} ${FileExists} "$R7\TaskBar\$R5" + ShellLink::GetShortCutTarget "$R7\TaskBar\$R5" + Pop $R4 + ${If} "$R4" == "$R9" ; link path == install path + ApplicationID::Set "$R7\TaskBar\$R5" "$R8" + Pop $R4 ; pop Set result off the stack + ${EndIf} + ${EndIf} + ClearErrors + FindNext $R6 $R5 + ${Unless} ${Errors} + Goto LoopTaskBar + ${EndUnless} + FindClose $R6 + + ClearErrors + + ; Start menu links + FindFirst $R6 $R5 "$R7\StartMenu\*.lnk" + LoopStartMenu: + ${If} ${FileExists} "$R7\StartMenu\$R5" + ShellLink::GetShortCutTarget "$R7\StartMenu\$R5" + Pop $R4 + ${If} "$R4" == "$R9" ; link path == install path + ApplicationID::Set "$R7\StartMenu\$R5" "$R8" + Pop $R4 ; pop Set result off the stack + ${EndIf} + ${EndIf} + ClearErrors + FindNext $R6 $R5 + ${Unless} ${Errors} + Goto LoopStartMenu + ${EndUnless} + FindClose $R6 + + Pop $R4 ; stack: $R5, $R6, $R7, $R8, $R9 + Pop $R5 ; stack: $R6, $R7, $R8, $R9 + Pop $R6 ; stack: $R7, $R8, $R9 + Pop $R7 ; stack: $R8, $R9 + Exch $R8 ; stack: appid, $R9 | $R8 = old $R8 + Exch 1 ; stack: $R9, appid + Exch $R9 ; stack: path, appid | $R9 = old $R9 + FunctionEnd + + !verbose pop + !endif +!macroend + +!macro UpdateShortcutAppModelIDsCall _INSTALL_PATH _APP_ID + !verbose push + !verbose ${_MOZFUNC_VERBOSE} + Push "${_APP_ID}" + Push "${_INSTALL_PATH}" + Call UpdateShortcutAppModelIDs + !verbose pop +!macroend