summaryrefslogtreecommitdiffstats
path: root/chrome/installer
diff options
context:
space:
mode:
authorgab@chromium.org <gab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-17 07:54:24 +0000
committergab@chromium.org <gab@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-06-17 07:54:24 +0000
commit0cb3b25055494db705ffca1df181afbcaf64d6fb (patch)
tree57ea3c21e1489e5b3cd992d38a5e001e8d6baf81 /chrome/installer
parent9b10059674ad8b44d93ff9e9cfb43235272cdd52 (diff)
downloadchromium_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.cc47
-rw-r--r--chrome/installer/setup/uninstall.cc93
-rw-r--r--chrome/installer/setup/uninstall.h3
-rw-r--r--chrome/installer/util/install_util.cc25
-rw-r--r--chrome/installer/util/shell_util.cc331
-rw-r--r--chrome/installer/util/shell_util.h34
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 ? &quoted_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"", &reg_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"", &registry_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