diff options
author | gab@chromium.org <gab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-17 07:54:24 +0000 |
---|---|---|
committer | gab@chromium.org <gab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-06-17 07:54:24 +0000 |
commit | 0cb3b25055494db705ffca1df181afbcaf64d6fb (patch) | |
tree | 57ea3c21e1489e5b3cd992d38a5e001e8d6baf81 /chrome/installer | |
parent | 9b10059674ad8b44d93ff9e9cfb43235272cdd52 (diff) | |
download | chromium_src-0cb3b25055494db705ffca1df181afbcaf64d6fb.zip chromium_src-0cb3b25055494db705ffca1df181afbcaf64d6fb.tar.gz chromium_src-0cb3b25055494db705ffca1df181afbcaf64d6fb.tar.bz2 |
Always suffix ChromeHTML entries on Windows for user-level installs.
This also adds the same suffixing to Chrome's appname for Default Programs registration (this suffix is not user-facing though as we don't suffix the actual string representing Chrome in the UI... obviously!)
Design doc: https://docs.google.com/a/chromium.org/document/d/1qmcV3uYBh3JwvXhYkI7asg0nN7KfVMWVOzND4p0jQ3E/edit
BUG=125362,124013,133173
TEST=http://goo.gl/ZZ7gE
Review URL: https://chromiumcodereview.appspot.com/10451074
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@142634 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer')
-rw-r--r-- | chrome/installer/setup/setup_main.cc | 47 | ||||
-rw-r--r-- | chrome/installer/setup/uninstall.cc | 93 | ||||
-rw-r--r-- | chrome/installer/setup/uninstall.h | 3 | ||||
-rw-r--r-- | chrome/installer/util/install_util.cc | 25 | ||||
-rw-r--r-- | chrome/installer/util/shell_util.cc | 331 | ||||
-rw-r--r-- | chrome/installer/util/shell_util.h | 34 |
6 files changed, 317 insertions, 216 deletions
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index ad881c2..4e709f6 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc @@ -7,8 +7,6 @@ #include <shellapi.h> #include <shlobj.h> -#include <string> - #include "base/at_exit.h" #include "base/basictypes.h" #include "base/command_line.h" @@ -17,6 +15,7 @@ #include "base/path_service.h" #include "base/process_util.h" #include "base/scoped_temp_dir.h" +#include "base/string16.h" #include "base/string_number_conversions.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" @@ -99,7 +98,7 @@ DWORD UnPackArchive(const FilePath& archive, // First uncompress the payload. This could be a differential // update (patch.7z) or full archive (chrome.7z). If this uncompress fails // return with error. - std::wstring unpacked_file; + string16 unpacked_file; int32 ret = LzmaUtil::UnPackArchive(archive.value(), temp_path.value(), &unpacked_file); if (ret != NO_ERROR) @@ -234,7 +233,7 @@ installer::InstallStatus RenameChromeExecutables( // Add work items to delete the "opv", "cpv", and "cmd" values from all // distributions. HKEY reg_root = installer_state->root_key(); - std::wstring version_key; + string16 version_key; for (int i = 0; i < num_dists; ++i) { version_key = dists[i]->GetVersionKey(); install_list->AddDeleteRegValueWorkItem( @@ -663,7 +662,8 @@ installer::InstallStatus InstallProductsHelper( temp_path.path(), prefs_source_path, prefs, *installer_version); int install_msg_base = IDS_INSTALL_FAILED_BASE; - std::wstring chrome_exe; + string16 chrome_exe; + string16 quoted_chrome_exe; if (install_status == installer::SAME_VERSION_REPAIR_FAILED) { if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_CF_BASE; @@ -679,7 +679,7 @@ installer::InstallStatus InstallProductsHelper( } else { chrome_exe = installer_state.target_path() .Append(installer::kChromeExe).value(); - chrome_exe = L"\"" + chrome_exe + L"\""; + quoted_chrome_exe = L"\"" + chrome_exe + L"\""; install_msg_base = 0; } } @@ -707,7 +707,7 @@ installer::InstallStatus InstallProductsHelper( install_status != installer::IN_USE_UPDATED); installer_state.WriteInstallerResult(install_status, install_msg_base, - write_chrome_launch_string ? &chrome_exe : NULL); + write_chrome_launch_string ? "ed_chrome_exe : NULL); if (install_status == installer::FIRST_INSTALL_SUCCESS) { VLOG(1) << "First install successful."; @@ -724,8 +724,11 @@ installer::InstallStatus InstallProductsHelper( (install_status == installer::IN_USE_UPDATED)) { const Product* chrome = installer_state.FindProduct( BrowserDistribution::CHROME_BROWSER); - if (chrome != NULL) - installer::RemoveChromeLegacyRegistryKeys(chrome->distribution()); + if (chrome != NULL) { + DCHECK_NE(chrome_exe, string16()); + installer::RemoveChromeLegacyRegistryKeys(chrome->distribution(), + chrome_exe); + } } } } @@ -901,9 +904,9 @@ installer::InstallStatus UninstallProducts( return install_status; } -installer::InstallStatus ShowEULADialog(const std::wstring& inner_frame) { +installer::InstallStatus ShowEULADialog(const string16& inner_frame) { VLOG(1) << "About to show EULA"; - std::wstring eula_path = installer::GetLocalizedEulaResource(); + string16 eula_path = installer::GetLocalizedEulaResource(); if (eula_path.empty()) { LOG(ERROR) << "No EULA path available"; return installer::EULA_REJECTED; @@ -945,10 +948,10 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, if (!temp_path.CreateUniqueTempDir()) { PLOG(ERROR) << "Could not create temporary path."; } else { - std::wstring setup_patch = cmd_line.GetSwitchValueNative( + string16 setup_patch = cmd_line.GetSwitchValueNative( installer::switches::kUpdateSetupExe); VLOG(1) << "Opening archive " << setup_patch; - std::wstring uncompressed_patch; + string16 uncompressed_patch; if (LzmaUtil::UnPackArchive(setup_patch, temp_path.path().value(), &uncompressed_patch) == NO_ERROR) { FilePath old_setup_exe = cmd_line.GetProgram(); @@ -980,7 +983,7 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) { // Check if we need to show the EULA. If it is passed as a command line // then the dialog is shown and regardless of the outcome setup exits here. - std::wstring inner_frame = + string16 inner_frame = cmd_line.GetSwitchValueNative(installer::switches::kShowEula); *exit_code = ShowEULADialog(inner_frame); if (installer::EULA_REJECTED != *exit_code) { @@ -1009,9 +1012,9 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, // These options should only be used when setup.exe is launched with admin // rights. We do not make any user specific changes with this option. DCHECK(IsUserAnAdmin()); - std::wstring chrome_exe(cmd_line.GetSwitchValueNative( + string16 chrome_exe(cmd_line.GetSwitchValueNative( installer::switches::kRegisterChromeBrowser)); - std::wstring suffix; + string16 suffix; if (cmd_line.HasSwitch( installer::switches::kRegisterChromeBrowserSuffix)) { suffix = cmd_line.GetSwitchValueNative( @@ -1019,7 +1022,7 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, } if (cmd_line.HasSwitch( installer::switches::kRegisterURLProtocol)) { - std::wstring protocol = cmd_line.GetSwitchValueNative( + string16 protocol = cmd_line.GetSwitchValueNative( installer::switches::kRegisterURLProtocol); // ShellUtil::RegisterChromeForProtocol performs all registration // done by ShellUtil::RegisterChromeBrowser, as well as registering @@ -1046,7 +1049,7 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, // Here we delete Chrome browser registration. This option should only // be used when setup.exe is launched with admin rights. We do not // make any user specific changes in this option. - std::wstring suffix; + string16 suffix; if (cmd_line.HasSwitch( installer::switches::kRegisterChromeBrowserSuffix)) { suffix = cmd_line.GetSwitchValueNative( @@ -1181,8 +1184,8 @@ class AutoCom { // Returns the Custom information for the client identified by the exe path // passed in. This information is used for crash reporting. google_breakpad::CustomClientInfo* GetCustomInfo(const wchar_t* exe_path) { - std::wstring product; - std::wstring version; + string16 product; + string16 version; scoped_ptr<FileVersionInfo> version_info(FileVersionInfo::CreateFileVersionInfo(FilePath(exe_path))); if (version_info.get()) { @@ -1227,7 +1230,7 @@ google_breakpad::ExceptionHandler* InitializeCrashReporting( // Build the pipe name. It can be either: // System-wide install: "NamedPipe\GoogleCrashServices\S-1-5-18" // Per-user install: "NamedPipe\GoogleCrashServices\<user SID>" - std::wstring user_sid = kSystemPrincipalSid; + string16 user_sid = kSystemPrincipalSid; if (!system_install) { if (!base::win::GetUserSidString(&user_sid)) { @@ -1235,7 +1238,7 @@ google_breakpad::ExceptionHandler* InitializeCrashReporting( } } - std::wstring pipe_name = kGoogleUpdatePipeName; + string16 pipe_name = kGoogleUpdatePipeName; pipe_name += user_sid; google_breakpad::ExceptionHandler* breakpad = diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc index 8a56753..33cc700 100644 --- a/chrome/installer/setup/uninstall.cc +++ b/chrome/installer/setup/uninstall.cc @@ -220,33 +220,6 @@ void CloseChromeFrameHelperProcess() { } } -// This method tries to figure out if current user has registered Chrome. -// It returns true iff there is a registered browser that will launch the -// same chrome.exe as the current installation. -bool CurrentUserHasDefaultBrowser(const InstallerState& installer_state) { - using base::win::RegistryKeyIterator; - const HKEY root = HKEY_LOCAL_MACHINE; - InstallUtil::ProgramCompare open_command_pred( - installer_state.target_path().Append(kChromeExe)); - string16 client_open_path; - RegKey client_open_key; - string16 reg_exe; - for (RegistryKeyIterator iter(root, ShellUtil::kRegStartMenuInternet); - iter.Valid(); ++iter) { - client_open_path.assign(ShellUtil::kRegStartMenuInternet) - .append(1, L'\\') - .append(iter.Name()) - .append(ShellUtil::kRegShellOpen); - if (client_open_key.Open(root, client_open_path.c_str(), - KEY_QUERY_VALUE) == ERROR_SUCCESS && - client_open_key.ReadValue(L"", ®_exe) == ERROR_SUCCESS && - open_command_pred.Evaluate(reg_exe)) { - return true; - } - } - return false; -} - // This method deletes Chrome shortcut folder from Windows Start menu. It // checks system_uninstall to see if the shortcut is in all users start menu // or current user start menu. @@ -664,7 +637,8 @@ bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root, return true; } -void RemoveChromeLegacyRegistryKeys(BrowserDistribution* dist) { +void RemoveChromeLegacyRegistryKeys(BrowserDistribution* dist, + const string16& chrome_exe) { // We used to register Chrome to handle crx files, but this turned out // to be not worth the hassle. Remove these old registry entries if // they exist. See: http://codereview.chromium.org/210007 @@ -678,9 +652,8 @@ const wchar_t kChromeExtProgId[] = L"ChromiumExt"; HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER }; for (size_t i = 0; i < arraysize(roots); ++i) { string16 suffix; - if (roots[i] == HKEY_LOCAL_MACHINE && - !ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) - suffix = L""; + if (roots[i] == HKEY_LOCAL_MACHINE) + suffix = ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe); // Delete Software\Classes\ChromeExt, string16 ext_prog_id(ShellUtil::kRegClasses); @@ -730,12 +703,13 @@ InstallStatus UninstallProduct(const InstallationState& original_state, bool force_uninstall, const CommandLine& cmd_line) { InstallStatus status = installer::UNINSTALL_CONFIRMED; - string16 suffix; - if (!ShellUtil::GetUserSpecificDefaultBrowserSuffix(product.distribution(), - &suffix)) - suffix = L""; - BrowserDistribution* browser_dist = product.distribution(); + const string16 chrome_exe( + installer_state.target_path().Append(installer::kChromeExe).value()); + + const string16 suffix(ShellUtil::GetCurrentInstallationSuffix(browser_dist, + chrome_exe)); + bool is_chrome = product.is_chrome(); VLOG(1) << "UninstallProduct: " << browser_dist->GetApplicationName(); @@ -753,11 +727,15 @@ InstallStatus UninstallProduct(const InstallationState& original_state, status != installer::UNINSTALL_DELETE_PROFILE) return status; - // Check if we need admin rights to cleanup HKLM. If we do, try to launch - // another uninstaller (silent) in elevated mode to do HKLM cleanup. + // Check if we need admin rights to cleanup HKLM (the conditions for + // requiring a cleanup are the same as the conditions to do the actual + // cleanup where DeleteChromeRegistrationKeys() is invoked for + // HKEY_LOCAL_MACHINE below). If we do, try to launch another uninstaller + // (silent) in elevated mode to do HKLM cleanup. // And continue uninstalling in the current process also to do HKCU cleanup. if (remove_all && - (!suffix.empty() || CurrentUserHasDefaultBrowser(installer_state)) && + ShellUtil::QuickIsChromeRegisteredInHKLM( + browser_dist, chrome_exe, suffix) && !::IsUserAnAdmin() && base::win::GetVersion() >= base::win::VERSION_VISTA && !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) { @@ -811,21 +789,40 @@ InstallStatus UninstallProduct(const InstallationState& original_state, // Registration data is put in HKCU for both system level and user level // installs. InstallStatus ret = installer::UNKNOWN_STATUS; - DeleteChromeRegistrationKeys(product.distribution(), HKEY_CURRENT_USER, - suffix, installer_state.target_path(), &ret); + DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER, suffix, + installer_state.target_path(), &ret); + + // If the user's Chrome is registered with a suffix: it is possible that old + // unsuffixed registrations were left in HKCU (e.g. if this install was + // previously installed with no suffix in HKCU (old suffix rules if the user + // is not an admin (or declined UAC at first run)) and later had to be + // suffixed when fully registered in HKLM (e.g. when later making Chrome + // default through the UI)). + // Remove remaining HKCU entries with no suffix if any. + if (!suffix.empty()) { + DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER, string16(), + installer_state.target_path(), &ret); + } // Chrome is registered in HKLM for all system-level installs and for // user-level installs for which Chrome has been made the default browser. // Always remove the HKLM registration for system-level installs. For - // user-level installs, only remove it if both: 1) this uninstall isn't a - // self-destruct following the installation of system-level Chrome (because - // the system-level Chrome owns the HKLM registration now), and 2) this user - // had made Chrome their default browser. + // user-level installs, only remove it if both: 1) this uninstall isn't a self + // destruct following the installation of a system-level Chrome (because the + // system-level Chrome owns the HKLM registration now), and 2) this user has + // made Chrome their default browser (i.e. has system entries registered with + // |suffix| (note: |suffix| will be the empty string if required as it is + // obtained by GetCurrentInstallationSuffix() above)). + // TODO(gab): This can still leave parts of a suffixed install behind. To be + // able to remove them we would need to be able to remove only suffixed + // entries (as it is now some of the system entries are unsuffixed; thus + // removing suffixed installs is prohibited in HKLM if !|remove_all| for now). if (installer_state.system_install() || (remove_all && - (!suffix.empty() || CurrentUserHasDefaultBrowser(installer_state)))) { - DeleteChromeRegistrationKeys(product.distribution(), HKEY_LOCAL_MACHINE, - suffix, installer_state.target_path(), &ret); + ShellUtil::QuickIsChromeRegisteredInHKLM( + browser_dist, chrome_exe, suffix))) { + DeleteChromeRegistrationKeys(browser_dist, HKEY_LOCAL_MACHINE, suffix, + installer_state.target_path(), &ret); } ProcessDelegateExecuteWorkItems(installer_state, product); diff --git a/chrome/installer/setup/uninstall.h b/chrome/installer/setup/uninstall.h index a11ec024..7867bb4 100644 --- a/chrome/installer/setup/uninstall.h +++ b/chrome/installer/setup/uninstall.h @@ -35,7 +35,8 @@ bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root, // Removes any legacy registry keys from earlier versions of Chrome that are no // longer needed. This is used during autoupdate since we don't do full // uninstalls/reinstalls to update. -void RemoveChromeLegacyRegistryKeys(BrowserDistribution* dist); +void RemoveChromeLegacyRegistryKeys(BrowserDistribution* dist, + const string16& chrome_exe); // This function uninstalls a product. Hence we came up with this awesome // name for it. diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc index d61c0b9..4b096ef 100644 --- a/chrome/installer/util/install_util.cc +++ b/chrome/installer/util/install_util.cc @@ -360,8 +360,8 @@ bool InstallUtil::HasDelegateExecuteHandler(BrowserDistribution* dist, } // This method tries to delete a registry key and logs an error message -// in case of failure. It returns true if deletion is successful, -// otherwise false. +// in case of failure. It returns true if deletion is successful (or the key did +// not exist), otherwise false. bool InstallUtil::DeleteRegistryKey(HKEY root_key, const string16& key_path) { VLOG(1) << "Deleting registry key " << key_path; @@ -375,20 +375,19 @@ bool InstallUtil::DeleteRegistryKey(HKEY root_key, } // This method tries to delete a registry value and logs an error message -// in case of failure. It returns true if deletion is successful, -// otherwise false. +// in case of failure. It returns true if deletion is successful (or the key did +// not exist), otherwise false. bool InstallUtil::DeleteRegistryValue(HKEY reg_root, const string16& key_path, const string16& value_name) { - RegKey key(reg_root, key_path.c_str(), KEY_ALL_ACCESS); - VLOG(1) << "Deleting registry value " << value_name; - if (key.HasValue(value_name.c_str())) { - LONG result = key.DeleteValue(value_name.c_str()); - if (result != ERROR_SUCCESS) { - LOG(ERROR) << "Failed to delete registry value: " << value_name - << " error: " << result; - return false; - } + RegKey key; + LONG result = key.Open(reg_root, key_path.c_str(), KEY_SET_VALUE); + if (result == ERROR_SUCCESS) + result = key.DeleteValue(value_name.c_str()); + if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { + LOG(ERROR) << "Failed to delete registry value: " << value_name + << " error: " << result; + return false; } return true; } diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc index 7222cd0..ba52f96 100644 --- a/chrome/installer/util/shell_util.cc +++ b/chrome/installer/util/shell_util.cc @@ -40,6 +40,22 @@ using base::win::RegKey; namespace { +// An enum used to tell QuickIsChromeRegistered() which level of registration +// the caller wants to confirm. +enum RegistrationConfirmationLevel { + // Only look for Chrome's ProgIds. + // This is sufficient when we are trying to determine the suffix of the + // currently running Chrome as system registrations might not be present. + CONFIRM_PROGID_REGISTRATION = 0, + // Confirm that Chrome is registered on the system (i.e. registered with + // Defaut Programs). These registrations can be in HKCU as of Windows 8. + CONFIRM_SYSTEM_REGISTRATION, + // Same as CONFIRM_SYSTEM_REGISTRATION, but only look in HKLM (used when + // uninstalling to know whether elevation is required to clean up the + // registry). + CONFIRM_SYSTEM_REGISTRATION_IN_HKLM, +}; + const wchar_t kReinstallCommand[] = L"ReinstallCommand"; // Returns true if Chrome Metro is supported on this OS (Win 8 8370 or greater). @@ -74,6 +90,13 @@ bool IsChromeMetroSupported() { // class. class RegistryEntry { public: + // A bit-field enum of places to look for this key in the Windows registry. + enum LookForIn { + LOOK_IN_HKCU = 1 << 0, + LOOK_IN_HKLM = 1 << 1, + LOOK_IN_HKCU_THEN_HKLM = LOOK_IN_HKCU | LOOK_IN_HKLM, + }; + // Returns the Windows browser client registration key for Chrome. For // example: "Software\Clients\StartMenuInternet\Chromium[.user]". Strictly // speaking, we should use the name of the executable (e.g., "chrome.exe"), @@ -250,11 +273,11 @@ class RegistryEntry { entries->push_front(new RegistryEntry(install_info, L"IconsVisible", 1)); // Register with Default Programs. - string16 app_name(dist->GetApplicationName().append(suffix)); + string16 reg_app_name(dist->GetApplicationName().append(suffix)); // Tell Windows where to find Chrome's Default Programs info. string16 capabilities(GetCapabilitiesKey(dist, suffix)); entries->push_front(new RegistryEntry(ShellUtil::kRegRegisteredApplications, - app_name, capabilities)); + reg_app_name, capabilities)); // Write out Chrome's Default Programs info. // TODO(grt): http://crbug.com/75152 Write a reference to a localized // resource rather than this. @@ -264,10 +287,11 @@ class RegistryEntry { entries->push_front(new RegistryEntry( capabilities, ShellUtil::kRegApplicationIcon, icon_path)); entries->push_front(new RegistryEntry( - capabilities, ShellUtil::kRegApplicationName, app_name)); + capabilities, ShellUtil::kRegApplicationName, + dist->GetAppShortCutName())); entries->push_front(new RegistryEntry(capabilities + L"\\Startmenu", - L"StartMenuInternet", app_name)); + L"StartMenuInternet", reg_app_name)); string16 html_prog_id(ShellUtil::kChromeHTMLProgId); html_prog_id.append(suffix); @@ -377,17 +401,24 @@ class RegistryEntry { // Checks if the current registry entry exists in HKCU\|_key_path|\|_name| // and value is |_value|. If the key does NOT exist in HKCU, checks for // the correct name and value in HKLM. - // This mimics Windows' behavior when searching in HKCR (HKCU takes precedence - // over HKLM). For registrations outside of HKCR on versions of Windows up - // to Win7, Chrome's values go in HKLM. This function will make unnecessary - // (but harmless) queries into HKCU in that case. Starting with Windows 8, - // Chrome's values go in HKCU for user-level installs, which takes precedence - // over HKLM. - bool ExistsInRegistry() { - RegistryStatus hkcu_status = StatusInRegistryUnderRoot(HKEY_CURRENT_USER); - return (hkcu_status == SAME_VALUE || - (hkcu_status == DOES_NOT_EXIST && - StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE) == SAME_VALUE)); + // |look_for_in| specifies roots (HKCU and/or HKLM) in which to look for the + // key, unspecified roots are not looked into (i.e. the the key is assumed not + // to exist in them). + // |look_for_in| must at least specify one root to look into. + // If |look_for_in| is LOOK_IN_HKCU_THEN_HKLM, this method mimics Windows' + // behavior when searching in HKCR (HKCU takes precedence over HKLM). For + // registrations outside of HKCR on versions of Windows prior to Win8, + // Chrome's values go in HKLM. This function will make unnecessary (but + // harmless) queries into HKCU in that case. + bool ExistsInRegistry(uint32 look_for_in) const { + DCHECK(look_for_in); + + RegistryStatus status = DOES_NOT_EXIST; + if (look_for_in & LOOK_IN_HKCU) + status = StatusInRegistryUnderRoot(HKEY_CURRENT_USER); + if (status == DOES_NOT_EXIST && (look_for_in & LOOK_IN_HKLM)) + status = StatusInRegistryUnderRoot(HKEY_LOCAL_MACHINE); + return status == SAME_VALUE; } private: @@ -471,13 +502,16 @@ bool AddRegistryEntries(HKEY root, const std::list<RegistryEntry*>& entries) { } // Checks that all |entries| are present on this computer. -bool AreEntriesRegistered(const std::list<RegistryEntry*>& entries) { +// |look_for_in| is passed to RegistryEntry::ExistsInRegistry(). Documentation +// for it can be found there. +bool AreEntriesRegistered(const std::list<RegistryEntry*>& entries, + uint32 look_for_in) { bool registered = true; for (std::list<RegistryEntry*>::const_iterator itr = entries.begin(); registered && itr != entries.end(); ++itr) { // We do not need registered = registered && ... since the loop condition // is set to exit early. - registered = (*itr)->ExistsInRegistry(); + registered = (*itr)->ExistsInRegistry(look_for_in); } return registered; } @@ -491,7 +525,7 @@ bool IsChromeRegistered(BrowserDistribution* dist, STLElementDeleter<std::list<RegistryEntry*> > entries_deleter(&entries); RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries); RegistryEntry::GetSystemEntries(dist, chrome_exe, suffix, &entries); - return AreEntriesRegistered(entries); + return AreEntriesRegistered(entries, RegistryEntry::LOOK_IN_HKCU_THEN_HKLM); } // This method checks if Chrome is already registered on the local machine @@ -502,7 +536,7 @@ bool IsChromeRegisteredForProtocol(BrowserDistribution* dist, std::list<RegistryEntry*> entries; STLElementDeleter<std::list<RegistryEntry*> > entries_deleter(&entries); RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol, &entries); - return AreEntriesRegistered(entries); + return AreEntriesRegistered(entries, RegistryEntry::LOOK_IN_HKCU_THEN_HKLM); } // This method registers Chrome on Vista by launching an elevated setup.exe. @@ -555,62 +589,6 @@ bool ElevateAndRegisterChrome(BrowserDistribution* dist, return false; } -// This method tries to figure out if another user has already registered her -// own copy of Chrome so that we can avoid overwriting it and append current -// user's login name to default browser registry entries. This function is -// not meant to detect all cases. It just tries to handle the most common case. -// All the conditions below have to be true for it to return true: -// - Software\Clients\StartMenuInternet\Chromium\"" key should have a valid -// value. -// - The value should not be same as given value in |chrome_exe| -// - Finally to handle the default install path (C:\Document and Settings\ -// <user>\Local Settings\Application Data\Chromium\Application) the value -// of the above key should differ from |chrome_exe| only in user name. -bool AnotherUserHasDefaultBrowser(BrowserDistribution* dist, - const string16& chrome_exe) { - const string16 reg_key( - RegistryEntry::GetBrowserClientKey(dist, string16()) - .append(ShellUtil::kRegShellOpen)); - RegKey key(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_READ); - string16 registry_chrome_exe; - if ((key.ReadValue(L"", ®istry_chrome_exe) != ERROR_SUCCESS) || - registry_chrome_exe.length() < 2) - return false; - - registry_chrome_exe = registry_chrome_exe.substr(1, - registry_chrome_exe.length() - 2); - if ((registry_chrome_exe.size() == chrome_exe.size()) && - (std::equal(chrome_exe.begin(), chrome_exe.end(), - registry_chrome_exe.begin(), - base::CaseInsensitiveCompare<wchar_t>()))) { - return false; - } - - std::vector<string16> v1, v2; - base::SplitString(registry_chrome_exe, L'\\', &v1); - base::SplitString(chrome_exe, L'\\', &v2); - if (v1.empty() || v2.empty() || v1.size() != v2.size()) - return false; - - // Now check that only one of the values within two '\' chars differ. - std::vector<string16>::iterator itr1 = v1.begin(); - std::vector<string16>::iterator itr2 = v2.begin(); - bool one_mismatch = false; - for ( ; itr1 < v1.end() && itr2 < v2.end(); ++itr1, ++itr2) { - string16 s1 = *itr1; - string16 s2 = *itr2; - if ((s1.size() != s2.size()) || - (!std::equal(s1.begin(), s1.end(), - s2.begin(), base::CaseInsensitiveCompare<wchar_t>()))) { - if (one_mismatch) - return false; - else - one_mismatch = true; - } - } - return true; -} - // Launches the Windows 7 and Windows 8 dialog for picking the application to // handle the given protocol. Most importantly, this is used to set the default // handler for http (and, implicitly with it, https). In that case it is also @@ -664,8 +642,7 @@ uint32 ConvertShellUtilShortcutOptionsToFileUtil(uint32 options) { // removal date remains correct). void RemoveBadWindows8RegistrationIfNeeded( BrowserDistribution* dist, - const string16& chrome_exe, - const string16& suffix) { + const string16& chrome_exe) { string16 handler_guid; if (dist->GetDelegateExecuteHandlerData(&handler_guid, NULL, NULL, NULL) && @@ -675,6 +652,10 @@ void RemoveBadWindows8RegistrationIfNeeded( // remove the values from the registry. const HKEY root_key = InstallUtil::IsPerUserInstall(chrome_exe.c_str()) ? HKEY_CURRENT_USER : HKEY_LOCAL_MACHINE; + // Use the current installation's suffix, not the about-to-be-installed + // suffix. + const string16 installation_suffix( + ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe)); const string16 app_id(dist->GetBrowserAppId()); // <root hkey>\Software\Classes\<app_id> @@ -687,13 +668,115 @@ void RemoveBadWindows8RegistrationIfNeeded( key = ShellUtil::kRegClasses; key.push_back(FilePath::kSeparators[0]); key.append(ShellUtil::kChromeHTMLProgId); - key.append(suffix); + key.append(installation_suffix); key.append(ShellUtil::kRegShellOpen); InstallUtil::DeleteRegistryValue(root_key, key, ShellUtil::kRegDelegateExecute); } } +// Returns true if the current install's |chrome_exe| has been registered with +// |suffix|. +// |confirmation_level| is the level of verification desired as described in +// the RegistrationConfirmationLevel enum above. +// |suffix| can be the empty string (this is used to support old installs +// where we used to not suffix user-level installs if they were the first to +// request the non-suffixed registry entries on the machine). +// NOTE: This a quick check that only validates that a single registry entry +// points to |chrome_exe|. This should only be used at run-time to determine +// how Chrome is registered, not to know whether the registration is complete +// at install-time (IsChromeRegistered() can be used for that). +bool QuickIsChromeRegistered(BrowserDistribution* dist, + const string16& chrome_exe, + const string16& suffix, + RegistrationConfirmationLevel confirmation_level) { + // Get the appropriate key to look for based on the level desired. + string16 reg_key; + switch (confirmation_level) { + case CONFIRM_PROGID_REGISTRATION: + // Software\Classes\ChromeHTML|suffix| + reg_key = ShellUtil::kRegClasses; + reg_key.push_back(FilePath::kSeparators[0]); + reg_key.append(ShellUtil::kChromeHTMLProgId); + reg_key.append(suffix); + break; + case CONFIRM_SYSTEM_REGISTRATION: + case CONFIRM_SYSTEM_REGISTRATION_IN_HKLM: + // Software\Clients\StartMenuInternet\Google Chrome|suffix| + reg_key = RegistryEntry::GetBrowserClientKey(dist, suffix); + break; + default: + NOTREACHED(); + break; + } + reg_key.append(ShellUtil::kRegShellOpen); + + // ProgId registrations are allowed to reside in HKCU for user-level installs + // (and values there have priority over values in HKLM). The same is true for + // system entries as of Windows 8. + if (confirmation_level == CONFIRM_PROGID_REGISTRATION || + (confirmation_level == CONFIRM_SYSTEM_REGISTRATION && + base::win::GetVersion() >= base::win::VERSION_WIN8)) { + const RegKey key_hkcu(HKEY_CURRENT_USER, reg_key.c_str(), KEY_QUERY_VALUE); + string16 hkcu_value; + // If |reg_key| is present in HKCU, assert that it points to |chrome_exe|. + // Otherwise, fall back on an HKLM lookup below. + if (key_hkcu.ReadValue(L"", &hkcu_value) == ERROR_SUCCESS) { + return InstallUtil::ProgramCompare( + FilePath(chrome_exe)).Evaluate(hkcu_value); + } + } + + // Assert that |reg_key| points to |chrome_exe| in HKLM. + const RegKey key_hklm(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_QUERY_VALUE); + string16 hklm_value; + if (key_hklm.ReadValue(L"", &hklm_value) == ERROR_SUCCESS) { + return InstallUtil::ProgramCompare( + FilePath(chrome_exe)).Evaluate(hklm_value); + } + return false; +} + +// Sets |suffix| to this user's username preceded by a dot. This suffix is then +// meant to be added to all registration that may conflict with another +// user-level Chrome install. +// Returns true unless the OS call to retrieve the username fails. +bool GetUserSpecificRegistrySuffix(string16* suffix) { + wchar_t user_name[256]; + DWORD size = arraysize(user_name); + if (::GetUserName(user_name, &size) == 0 || size < 1) { + PLOG(DFATAL) << "GetUserName failed"; + return false; + } + suffix->reserve(size); + suffix->assign(1, L'.'); + suffix->append(user_name, size - 1); + return true; +} + +// Sets |suffix| to the current user's username, preceded by a dot, on +// user-level installs. +// To support old-style user-level installs however, |suffix| is cleared if +// the user currently owns the non-suffixed HKLM registrations. +// |suffix| is also cleared on system-level installs. +// |suffix| should then be appended to all Chrome properties that may conflict +// with other Chrome user-level installs. +// Returns true unless one of the underlying calls fails. +bool GetInstallationSpecificSuffix(BrowserDistribution* dist, + const string16& chrome_exe, + string16* suffix) { + if (!InstallUtil::IsPerUserInstall(chrome_exe.c_str()) || + QuickIsChromeRegistered(dist, chrome_exe, string16(), + CONFIRM_SYSTEM_REGISTRATION)) { + // No suffix on system-level installs and user-level installs already + // registered with no suffix. + suffix->clear(); + return true; + } else { + return GetUserSpecificRegistrySuffix(suffix); + } +} + } // namespace const wchar_t* ShellUtil::kRegDefaultIcon = L"\\DefaultIcon"; @@ -740,6 +823,13 @@ const wchar_t* ShellUtil::kRegVerbRun = L"run"; const wchar_t* ShellUtil::kRegCommand = L"command"; const wchar_t* ShellUtil::kRegDelegateExecute = L"DelegateExecute"; +bool ShellUtil::QuickIsChromeRegisteredInHKLM(BrowserDistribution* dist, + const string16& chrome_exe, + const string16& suffix) { + return QuickIsChromeRegistered(dist, chrome_exe, suffix, + CONFIRM_SYSTEM_REGISTRATION_IN_HKLM); +} + bool ShellUtil::CreateChromeDesktopShortcut(BrowserDistribution* dist, const string16& chrome_exe, const string16& description, @@ -950,19 +1040,16 @@ void ShellUtil::GetRegisteredBrowsers( } } -bool ShellUtil::GetUserSpecificDefaultBrowserSuffix(BrowserDistribution* dist, - string16* entry) { - wchar_t user_name[256]; - DWORD size = arraysize(user_name); - if (::GetUserName(user_name, &size) == 0 || size < 1) - return false; - entry->reserve(size); - entry->assign(1, L'.'); - entry->append(user_name, size - 1); - - return RegKey(HKEY_LOCAL_MACHINE, - RegistryEntry::GetBrowserClientKey(dist, *entry).c_str(), - KEY_READ).Valid(); +string16 ShellUtil::GetCurrentInstallationSuffix(BrowserDistribution* dist, + const string16& chrome_exe) { + string16 tested_suffix; + if (!InstallUtil::IsPerUserInstall(chrome_exe.c_str()) || + !GetUserSpecificRegistrySuffix(&tested_suffix) || + !QuickIsChromeRegistered(dist, chrome_exe, tested_suffix, + CONFIRM_PROGID_REGISTRATION)) { + return string16(); + } + return tested_suffix; } bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, @@ -980,15 +1067,18 @@ bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, return false; } - ShellUtil::RegisterChromeBrowser(dist, chrome_exe, L"", elevate_if_not_admin); + if (!ShellUtil::RegisterChromeBrowser( + dist, chrome_exe, string16(), elevate_if_not_admin)) { + return false; + } bool ret = true; // First use the new "recommended" way on Vista to make Chrome default // browser. string16 app_name = dist->GetApplicationName(); - string16 app_suffix; - if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &app_suffix)) - app_name += app_suffix; + const string16 app_suffix( + ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe)); + app_name += app_suffix; if (base::win::GetVersion() >= base::win::VERSION_VISTA) { // On Windows Vista and Win7 we still can set ourselves via the @@ -1028,10 +1118,7 @@ bool ShellUtil::MakeChromeDefault(BrowserDistribution* dist, std::list<RegistryEntry*> entries; STLElementDeleter<std::list<RegistryEntry*> > entries_deleter(&entries); - string16 suffix; - if (!GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) - suffix = L""; - RegistryEntry::GetUserEntries(dist, chrome_exe, suffix, &entries); + RegistryEntry::GetUserEntries(dist, chrome_exe, app_suffix, &entries); // Change the default browser for current user. if ((shell_change & ShellUtil::CURRENT_USER) && !AddRegistryEntries(HKEY_CURRENT_USER, entries)) { @@ -1089,9 +1176,7 @@ bool ShellUtil::MakeChromeDefaultProtocolClient(BrowserDistribution* dist, NULL, CLSCTX_INPROC); if (SUCCEEDED(hr)) { string16 app_name = dist->GetApplicationName(); - string16 suffix; - if (ShellUtil::GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) - app_name += suffix; + app_name += ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe); hr = pAAR->SetAppAsDefault(app_name.c_str(), protocol.c_str(), AT_URLPROTOCOL); @@ -1109,11 +1194,9 @@ bool ShellUtil::MakeChromeDefaultProtocolClient(BrowserDistribution* dist, std::list<RegistryEntry*> entries; STLElementDeleter<std::list<RegistryEntry*> > entries_deleter(&entries); - string16 suffix; - if (!GetUserSpecificDefaultBrowserSuffix(dist, &suffix)) - suffix = L""; - string16 chrome_open = ShellUtil::GetChromeShellOpenCmd(chrome_exe); - string16 chrome_icon = ShellUtil::GetChromeIcon(dist, chrome_exe); + const string16 suffix(GetCurrentInstallationSuffix(dist, chrome_exe)); + const string16 chrome_open(ShellUtil::GetChromeShellOpenCmd(chrome_exe)); + const string16 chrome_icon(ShellUtil::GetChromeIcon(dist, chrome_exe)); RegistryEntry::GetUserProtocolEntries(protocol, chrome_icon, chrome_open, &entries); // Change the default protocol handler for current user. @@ -1132,19 +1215,15 @@ bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist, if (!dist->CanSetAsDefault()) return false; - // First figure out we need to append a suffix to the registry entries to - // make them unique. string16 suffix; if (!unique_suffix.empty()) { suffix = unique_suffix; - } else if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) && - !GetUserSpecificDefaultBrowserSuffix(dist, &suffix) && - !AnotherUserHasDefaultBrowser(dist, chrome_exe)) { - suffix = L""; + } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) { + return false; } // TODO(grt): remove this on or after 2012-08-01; see impl for details. - RemoveBadWindows8RegistrationIfNeeded(dist, chrome_exe, suffix); + RemoveBadWindows8RegistrationIfNeeded(dist, chrome_exe); // Check if Chromium is already registered with this suffix. if (IsChromeRegistered(dist, chrome_exe, suffix)) @@ -1171,12 +1250,22 @@ bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist, ElevateAndRegisterChrome(dist, chrome_exe, suffix, L"")) return true; - // If we got to this point then all we can do is create ProgIds under HKCU - // on XP as well as Vista. + // If we got to this point then all we can do is create ProgIds under HKCU. std::list<RegistryEntry*> entries; STLElementDeleter<std::list<RegistryEntry*> > entries_deleter(&entries); RegistryEntry::GetProgIdEntries(dist, chrome_exe, L"", &entries); - return AddRegistryEntries(HKEY_CURRENT_USER, entries); + // Prefer to use |suffix|; unless Chrome's ProgIds are already registered with + // no suffix (as per the old registration style): in which case some other + // registry entries could refer to them and since we were not able to set our + // HKLM entries above, we are better off not altering these here. + if (!AreEntriesRegistered(entries, RegistryEntry::LOOK_IN_HKCU)) { + if (!suffix.empty()) { + STLDeleteElements(&entries); + RegistryEntry::GetProgIdEntries(dist, chrome_exe, suffix, &entries); + } + return AddRegistryEntries(HKEY_CURRENT_USER, entries); + } + return true; } bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist, @@ -1187,15 +1276,11 @@ bool ShellUtil::RegisterChromeForProtocol(BrowserDistribution* dist, if (!dist->CanSetAsDefault()) return false; - // Figure out we need to append a suffix to the registry entries to - // make them unique. string16 suffix; if (!unique_suffix.empty()) { suffix = unique_suffix; - } else if (InstallUtil::IsPerUserInstall(chrome_exe.c_str()) && - !GetUserSpecificDefaultBrowserSuffix(dist, &suffix) && - !AnotherUserHasDefaultBrowser(dist, chrome_exe)) { - suffix = L""; + } else if (!GetInstallationSpecificSuffix(dist, chrome_exe, &suffix)) { + return false; } // Check if Chromium is already registered with this suffix. diff --git a/chrome/installer/util/shell_util.h b/chrome/installer/util/shell_util.h index 0e567e9..b711d5a 100644 --- a/chrome/installer/util/shell_util.h +++ b/chrome/installer/util/shell_util.h @@ -122,6 +122,13 @@ class ShellUtil { // Registry value name for the DelegateExecute verb handler. static const wchar_t* kRegDelegateExecute; + // Returns true if |chrome_exe| is registered in HKLM with |suffix|. + // Note: This only checks one deterministic key in HKLM for |chrome_exe| and + // doesn't otherwise validate a full Chrome install in HKLM. + static bool QuickIsChromeRegisteredInHKLM(BrowserDistribution* dist, + const string16& chrome_exe, + const string16& suffix); + // Creates Chrome shortcut on the Desktop. // |dist| gives the type of browser distribution currently in use. // |chrome_exe| provides the target path information. @@ -208,15 +215,24 @@ class ShellUtil { static void GetRegisteredBrowsers(BrowserDistribution* dist, std::map<string16, string16>* browsers); - // This function gets a suffix (user's login name) that can be added - // to Chromium default browser entry in the registry to create a unique name - // if there are multiple users on the machine, each with their own copy of - // Chromium that they want to set as default browser. - // This suffix value is assigned to |entry|. The function also checks for - // existence of Default Browser registry key with this suffix and - // returns true if it exists. In all other cases it returns false. - static bool GetUserSpecificDefaultBrowserSuffix(BrowserDistribution* dist, - string16* entry); + // Returns the suffix this user's Chrome install is registered with. + // Always returns the empty string on system-level installs. + // + // This method is meant for external methods which need to know the suffix of + // the current install at run-time, not for install-time decisions. + // There are no guarantees that this suffix will not change later: + // (e.g. if two user-level installs were previously installed in parallel on + // the same machine, both without admin rights and with no user-level install + // having claimed the non-suffixed HKLM registrations, they both have no + // suffix in their progId entries (as per the old suffix rules). If they were + // to both fully register (i.e. click "Make Chrome Default" and go through + // UAC; or upgrade to Win8 and get the automatic no UAC full registration) + // they would then both get a suffixed registration as per the new suffix + // rules). + // + // |chrome_exe| The path to the currently installed (or running) chrome.exe. + static string16 GetCurrentInstallationSuffix(BrowserDistribution* dist, + const string16& chrome_exe); // Make Chrome the default browser. This function works by going through // the url protocols and file associations that are related to general |