Index: chrome/installer/util/shell_util.cc |
diff --git a/chrome/installer/util/shell_util.cc b/chrome/installer/util/shell_util.cc |
index 46defa119135f89d76b0d7b2578fecf14bdeea31..2b8ed146342745ea78e8282238bdf6625d929f0c 100644 |
--- a/chrome/installer/util/shell_util.cc |
+++ b/chrome/installer/util/shell_util.cc |
@@ -77,29 +77,12 @@ enum RegistrationConfirmationLevel { |
const wchar_t kReinstallCommand[] = L"ReinstallCommand"; |
-// Returns true if Chrome Metro is supported on this OS (Win 8 8370 or greater). |
-// TODO(gab): Change this to a simple check for Win 8 once old Win8 builds |
-// become irrelevant. |
+// Returns true if Chrome Metro is supported on this version of Windows |
+// (supported as of Win8; deprecated as of Win10). |
bool IsChromeMetroSupported() { |
- OSVERSIONINFOEX min_version_info = {}; |
- min_version_info.dwOSVersionInfoSize = sizeof(OSVERSIONINFOEX); |
- min_version_info.dwMajorVersion = 6; |
- min_version_info.dwMinorVersion = 2; |
- min_version_info.dwBuildNumber = 8370; |
- min_version_info.wServicePackMajor = 0; |
- min_version_info.wServicePackMinor = 0; |
- |
- DWORDLONG condition_mask = 0; |
- VER_SET_CONDITION(condition_mask, VER_MAJORVERSION, VER_GREATER_EQUAL); |
- VER_SET_CONDITION(condition_mask, VER_MINORVERSION, VER_GREATER_EQUAL); |
- VER_SET_CONDITION(condition_mask, VER_BUILDNUMBER, VER_GREATER_EQUAL); |
- VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMAJOR, VER_GREATER_EQUAL); |
- VER_SET_CONDITION(condition_mask, VER_SERVICEPACKMINOR, VER_GREATER_EQUAL); |
- |
- DWORD type_mask = VER_MAJORVERSION | VER_MINORVERSION | VER_BUILDNUMBER | |
- VER_SERVICEPACKMAJOR | VER_SERVICEPACKMINOR; |
- |
- return VerifyVersionInfo(&min_version_info, type_mask, condition_mask) != 0; |
+ const base::win::Version win_version = base::win::GetVersion(); |
+ return win_version >= base::win::VERSION_WIN8 && |
+ win_version < base::win::VERSION_WIN10; |
} |
// Returns the current (or installed) browser's ProgId (e.g. |
@@ -192,6 +175,16 @@ class RegistryEntry { |
LOOK_IN_HKCU_THEN_HKLM = LOOK_IN_HKCU | LOOK_IN_HKLM, |
}; |
+ // Identifies the type of removal this RegistryEntry is flagged for, if any. |
+ enum class RemovalFlag { |
+ // Default case: install the key/value. |
+ NONE, |
+ // Registry value under |key_path_|\|name_| is flagged for deletion. |
+ VALUE, |
+ // Registry key under |key_path_| is flag for deletion. |
+ KEY, |
+ }; |
+ |
// Details about a Windows application, to be entered into the registry for |
// the purpose of file associations. |
struct ApplicationInfo { |
@@ -251,6 +244,68 @@ class RegistryEntry { |
return GetBrowserClientKey(dist, suffix).append(L"\\Capabilities"); |
} |
+ // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8. This is |
+ // only needed for registring a web browser, not for general associations. |
+ static ScopedVector<RegistryEntry> GetChromeDelegateExecuteEntries( |
+ const base::FilePath& chrome_exe, |
+ const ApplicationInfo& app_info) { |
+ ScopedVector<RegistryEntry> entries; |
+ |
+ base::string16 app_id_shell_key(ShellUtil::kRegClasses); |
+ app_id_shell_key.push_back(base::FilePath::kSeparators[0]); |
+ app_id_shell_key.append(app_info.app_id); |
+ app_id_shell_key.append(ShellUtil::kRegExePath); |
+ app_id_shell_key.append(ShellUtil::kRegShellPath); |
+ |
+ // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open |
+ entries.push_back( |
+ new RegistryEntry(app_id_shell_key, ShellUtil::kRegVerbOpen)); |
+ |
+ // The command to execute when opening this application via the Metro UI. |
+ const base::string16 delegate_command( |
+ ShellUtil::GetChromeDelegateCommand(chrome_exe)); |
+ |
+ // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is |
+ // registered to handle some verbs. This registration has the side-effect |
+ // that these verbs now show up in the shortcut's context menu. We |
+ // mitigate this side-effect by making the context menu entries |
+ // user readable/localized strings. See relevant MSDN article: |
+ // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx |
+ static const struct { |
+ const wchar_t* verb; |
+ int name_id; |
+ } verbs[] = { |
+ {ShellUtil::kRegVerbOpen, -1}, |
+ {ShellUtil::kRegVerbOpenNewWindow, IDS_SHORTCUT_NEW_WINDOW_BASE}, |
+ }; |
+ for (const auto& verb_and_id : verbs) { |
+ base::string16 sub_path(app_id_shell_key); |
+ sub_path.push_back(base::FilePath::kSeparators[0]); |
+ sub_path.append(verb_and_id.verb); |
+ |
+ // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb> |
+ if (verb_and_id.name_id != -1) { |
+ // TODO(grt): http://crbug.com/75152 Write a reference to a localized |
+ // resource. |
+ const base::string16 verb_name( |
+ installer::GetLocalizedString(verb_and_id.name_id)); |
+ entries.push_back(new RegistryEntry(sub_path, verb_name.c_str())); |
+ } |
+ entries.push_back( |
+ new RegistryEntry(sub_path, L"CommandId", L"Browser.Launch")); |
+ |
+ sub_path.push_back(base::FilePath::kSeparators[0]); |
+ sub_path.append(ShellUtil::kRegCommand); |
+ |
+ // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command |
+ entries.push_back(new RegistryEntry(sub_path, delegate_command)); |
+ entries.push_back(new RegistryEntry( |
+ sub_path, ShellUtil::kRegDelegateExecute, app_info.delegate_clsid)); |
+ } |
+ |
+ return entries.Pass(); |
+ } |
+ |
// This method returns a list of all the registry entries that |
// are needed to register this installation's ProgId and AppId. |
// These entries need to be registered in HKLM prior to Win8. |
@@ -273,13 +328,6 @@ class RegistryEntry { |
app_info.app_id = ShellUtil::GetBrowserModelId( |
dist, InstallUtil::IsPerUserInstall(chrome_exe)); |
- // The command to execute when opening this application via the Metro UI. |
- base::string16 delegate_command( |
- ShellUtil::GetChromeDelegateCommand(chrome_exe)); |
- bool set_delegate_execute = |
- IsChromeMetroSupported() && |
- dist->GetCommandExecuteImplClsid(&app_info.delegate_clsid); |
- |
// TODO(grt): http://crbug.com/75152 Write a reference to a localized |
// resource for name, description, and company. |
app_info.application_name = dist->GetDisplayName(); |
@@ -288,58 +336,23 @@ class RegistryEntry { |
app_info.application_description = dist->GetAppDescription(); |
app_info.publisher_name = dist->GetPublisherName(); |
- GetProgIdEntries(app_info, entries); |
- |
- // DelegateExecute ProgId. Needed for Chrome Metro in Windows 8. This is |
- // only needed for registring a web browser, not for general associations. |
- if (set_delegate_execute) { |
- base::string16 model_id_shell(ShellUtil::kRegClasses); |
- model_id_shell.push_back(base::FilePath::kSeparators[0]); |
- model_id_shell.append(app_info.app_id); |
- model_id_shell.append(ShellUtil::kRegExePath); |
- model_id_shell.append(ShellUtil::kRegShellPath); |
- |
- // <root hkey>\Software\Classes\<app_id>\.exe\shell @=open |
- entries->push_back(new RegistryEntry(model_id_shell, |
- ShellUtil::kRegVerbOpen)); |
- |
- // Each of Chrome's shortcuts has an appid; which, as of Windows 8, is |
- // registered to handle some verbs. This registration has the side-effect |
- // that these verbs now show up in the shortcut's context menu. We |
- // mitigate this side-effect by making the context menu entries |
- // user readable/localized strings. See relevant MSDN article: |
- // http://msdn.microsoft.com/en-US/library/windows/desktop/cc144171.aspx |
- const struct { |
- const wchar_t* verb; |
- int name_id; |
- } verbs[] = { |
- { ShellUtil::kRegVerbOpen, -1 }, |
- { ShellUtil::kRegVerbOpenNewWindow, IDS_SHORTCUT_NEW_WINDOW_BASE }, |
- }; |
- for (size_t i = 0; i < arraysize(verbs); ++i) { |
- base::string16 sub_path(model_id_shell); |
- sub_path.push_back(base::FilePath::kSeparators[0]); |
- sub_path.append(verbs[i].verb); |
- |
- // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb> |
- if (verbs[i].name_id != -1) { |
- // TODO(grt): http://crbug.com/75152 Write a reference to a localized |
- // resource. |
- base::string16 verb_name( |
- installer::GetLocalizedString(verbs[i].name_id)); |
- entries->push_back(new RegistryEntry(sub_path, verb_name.c_str())); |
- } |
- entries->push_back(new RegistryEntry( |
- sub_path, L"CommandId", L"Browser.Launch")); |
+ dist->GetCommandExecuteImplClsid(&app_info.delegate_clsid); |
- sub_path.push_back(base::FilePath::kSeparators[0]); |
- sub_path.append(ShellUtil::kRegCommand); |
+ GetProgIdEntries(app_info, entries); |
- // <root hkey>\Software\Classes\<app_id>\.exe\shell\<verb>\command |
- entries->push_back(new RegistryEntry(sub_path, delegate_command)); |
- entries->push_back(new RegistryEntry( |
- sub_path, ShellUtil::kRegDelegateExecute, app_info.delegate_clsid)); |
+ if (!app_info.delegate_clsid.empty()) { |
+ ScopedVector<RegistryEntry> delegate_execute_entries = |
+ GetChromeDelegateExecuteEntries(chrome_exe, app_info); |
+ if (!IsChromeMetroSupported()) { |
+ // Remove the keys (not only their values) so that Windows will continue |
+ // to launch Chrome without a pesky association error. |
+ for (RegistryEntry* entry : delegate_execute_entries) |
+ entry->set_removal_flag(RemovalFlag::KEY); |
} |
+ // Move |delegate_execute_entries| to |entries|. |
+ entries->insert(entries->end(), delegate_execute_entries.begin(), |
+ delegate_execute_entries.end()); |
+ delegate_execute_entries.weak_clear(); |
} |
} |
@@ -368,6 +381,10 @@ class RegistryEntry { |
new RegistryEntry(prog_id_path + ShellUtil::kRegShellOpen, |
ShellUtil::kRegDelegateExecute, |
app_info.delegate_clsid)); |
+ // If Metro is not supported, remove the DelegateExecute entry instead of |
+ // adding it. |
+ if (!IsChromeMetroSupported()) |
+ entries->back()->set_removal_flag(RemovalFlag::VALUE); |
} |
// The following entries are required as of Windows 8, but do not |
@@ -646,19 +663,43 @@ class RegistryEntry { |
entries->push_back(new RegistryEntry(start_menu, app_name)); |
} |
- // Generate work_item tasks required to create current registry entry and |
- // add them to the given work item list. |
+ // Flags this RegistryKey with |removal_flag|, indicating that it should be |
+ // removed rather than created. Note that this will not result in cleaning up |
+ // the entire registry hierarchy below RegistryEntry even if it is left empty |
+ // by this operation (this should thus not be used for uninstall, but only to |
+ // unregister keys that should explicitly no longer be active in the current |
+ // configuration). |
+ void set_removal_flag(RemovalFlag removal_flag) { |
+ removal_flag_ = removal_flag; |
+ } |
+ |
+ // Generates work_item tasks required to create (or potentially delete based |
+ // on |removal_flag_|) the current RegistryEntry and add them to the given |
+ // work item list. |
void AddToWorkItemList(HKEY root, WorkItemList *items) const { |
- items->AddCreateRegKeyWorkItem(root, key_path_, WorkItem::kWow64Default); |
- if (is_string_) { |
- items->AddSetRegValueWorkItem( |
- root, key_path_, WorkItem::kWow64Default, name_, value_, true); |
+ if (removal_flag_ == RemovalFlag::VALUE) { |
+ items->AddDeleteRegValueWorkItem(root, key_path_, WorkItem::kWow64Default, |
+ name_); |
+ } else if (removal_flag_ == RemovalFlag::KEY) { |
+ items->AddDeleteRegKeyWorkItem(root, key_path_, WorkItem::kWow64Default); |
} else { |
- items->AddSetRegValueWorkItem( |
- root, key_path_, WorkItem::kWow64Default, name_, int_value_, true); |
+ DCHECK(removal_flag_ == RemovalFlag::NONE); |
+ items->AddCreateRegKeyWorkItem(root, key_path_, WorkItem::kWow64Default); |
+ if (is_string_) { |
+ items->AddSetRegValueWorkItem(root, key_path_, WorkItem::kWow64Default, |
+ name_, value_, true); |
+ } else { |
+ items->AddSetRegValueWorkItem(root, key_path_, WorkItem::kWow64Default, |
+ name_, int_value_, true); |
+ } |
} |
} |
+ // Returns true if this key is flagged for removal. |
+ bool IsFlaggedForRemoval() const { |
+ return removal_flag_ != RemovalFlag::NONE; |
+ } |
+ |
// 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. |
@@ -710,23 +751,34 @@ class RegistryEntry { |
// Create a object that represent default value of a key |
RegistryEntry(const base::string16& key_path, const base::string16& value) |
- : key_path_(key_path), name_(), |
- is_string_(true), value_(value), int_value_(0) { |
- } |
+ : key_path_(key_path), |
+ name_(), |
+ is_string_(true), |
+ value_(value), |
+ int_value_(0), |
+ removal_flag_(RemovalFlag::NONE) {} |
// Create a object that represent a key of type REG_SZ |
- RegistryEntry(const base::string16& key_path, const base::string16& name, |
+ RegistryEntry(const base::string16& key_path, |
+ const base::string16& name, |
const base::string16& value) |
- : key_path_(key_path), name_(name), |
- is_string_(true), value_(value), int_value_(0) { |
- } |
+ : key_path_(key_path), |
+ name_(name), |
+ is_string_(true), |
+ value_(value), |
+ int_value_(0), |
+ removal_flag_(RemovalFlag::NONE) {} |
// Create a object that represent a key of integer type |
- RegistryEntry(const base::string16& key_path, const base::string16& name, |
+ RegistryEntry(const base::string16& key_path, |
+ const base::string16& name, |
DWORD value) |
- : key_path_(key_path), name_(name), |
- is_string_(false), value_(), int_value_(value) { |
- } |
+ : key_path_(key_path), |
+ name_(name), |
+ is_string_(false), |
+ value_(), |
+ int_value_(value), |
+ removal_flag_(RemovalFlag::NONE) {} |
base::string16 key_path_; // key path for the registry entry |
base::string16 name_; // name of the registry entry |
@@ -734,6 +786,10 @@ class RegistryEntry { |
base::string16 value_; // string value (useful if is_string_ = true) |
DWORD int_value_; // integer value (useful if is_string_ = false) |
+ // Identifies whether this RegistryEntry is flagged for removal (i.e. no |
+ // longer relevant on the configuration it was created under). |
+ RemovalFlag removal_flag_; |
+ |
// Helper function for ExistsInRegistry(). |
// Returns the RegistryStatus of the current registry entry in |
// |root|\|key_path_|\|name_|. |
@@ -784,24 +840,21 @@ bool AddRegistryEntries(HKEY root, const ScopedVector<RegistryEntry>& entries) { |
return true; |
} |
-// Checks that all |entries| are present on this computer. |
-// |look_for_in| is passed to RegistryEntry::ExistsInRegistry(). Documentation |
-// for it can be found there. |
-bool AreEntriesRegistered(const ScopedVector<RegistryEntry>& entries, |
+// Checks that all |entries| are present on this computer (or absent if their |
+// |removal_flag_| is set). |look_for_in| is passed to |
+// RegistryEntry::ExistsInRegistry(). Documentation for it can be found there. |
+bool AreEntriesAsDesired(const ScopedVector<RegistryEntry>& entries, |
uint32 look_for_in) { |
- bool registered = true; |
- for (ScopedVector<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(look_for_in); |
+ for (const auto* entry : entries) { |
+ if (entry->ExistsInRegistry(look_for_in) != !entry->IsFlaggedForRemoval()) |
+ return false; |
} |
- return registered; |
+ return true; |
} |
-// Checks that all required registry entries for Chrome are already present |
-// on this computer. See RegistryEntry::ExistsInRegistry for the behavior of |
-// |look_for_in|. |
+// Checks that all required registry entries for Chrome are already present on |
+// this computer (or absent if their |removal_flag_| is set). |
+// See RegistryEntry::ExistsInRegistry for the behavior of |look_for_in|. |
// Note: between r133333 and r154145 we were registering parts of Chrome in HKCU |
// and parts in HKLM for user-level installs; we now always register everything |
// under a single registry root. Not doing so caused http://crbug.com/144910 for |
@@ -817,7 +870,7 @@ bool IsChromeRegistered(BrowserDistribution* dist, |
RegistryEntry::GetChromeProgIdEntries(dist, chrome_exe, suffix, &entries); |
RegistryEntry::GetShellIntegrationEntries(dist, chrome_exe, suffix, &entries); |
RegistryEntry::GetChromeAppRegistrationEntries(chrome_exe, suffix, &entries); |
- return AreEntriesRegistered(entries, look_for_in); |
+ return AreEntriesAsDesired(entries, look_for_in); |
} |
// This method checks if Chrome is already registered on the local machine |
@@ -829,7 +882,7 @@ bool IsChromeRegisteredForProtocol(BrowserDistribution* dist, |
uint32 look_for_in) { |
ScopedVector<RegistryEntry> entries; |
RegistryEntry::GetProtocolCapabilityEntries(dist, suffix, protocol, &entries); |
- return AreEntriesRegistered(entries, look_for_in); |
+ return AreEntriesAsDesired(entries, look_for_in); |
} |
// This method registers Chrome on Vista by launching an elevated setup.exe. |
@@ -1155,7 +1208,7 @@ base::win::ShortcutProperties TranslateShortcutProperties( |
// <root>\Software\Classes\Chrome<.suffix>\.exe\shell\run on Windows 8. |
void RemoveRunVerbOnWindows8(BrowserDistribution* dist, |
const base::FilePath& chrome_exe) { |
- if (IsChromeMetroSupported()) { |
+ if (base::win::GetVersion() >= base::win::VERSION_WIN8) { |
bool is_per_user_install = InstallUtil::IsPerUserInstall(chrome_exe); |
HKEY root_key = DetermineRegistrationRoot(is_per_user_install); |
// There's no need to rollback, so forgo the usual work item lists and just |
@@ -2258,7 +2311,7 @@ bool ShellUtil::RegisterChromeBrowser(BrowserDistribution* dist, |
// 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 (!AreEntriesAsDesired(entries, RegistryEntry::LOOK_IN_HKCU)) { |
if (!suffix.empty()) { |
entries.clear(); |
RegistryEntry::GetChromeProgIdEntries( |