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..26140bbef0676111297829e1d3db899961ef864b 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. |
grt (UTC plus 2)
2015/08/24 15:08:20
"...for, if any."
gab
2015/08/27 20:51:07
Done.
|
+ enum class RemovalFlag { |
+ // Default case, no flag. |
grt (UTC plus 2)
2015/08/24 15:08:19
// Default case: install the key/value.
gab
2015/08/27 20:51:07
Done.
|
+ 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 |
+ 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) { |
grt (UTC plus 2)
2015/08/24 15:08:19
nit: for (const auto verb_and_id& : verbs) {
gab
2015/08/27 20:51:07
Woohoo C++11 in shell_util :-)!
|
+ base::string16 sub_path(app_id_shell_key); |
+ 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. |
+ const 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")); |
+ |
+ 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,12 +328,7 @@ 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); |
+ dist->GetCommandExecuteImplClsid(&app_info.delegate_clsid); |
// TODO(grt): http://crbug.com/75152 Write a reference to a localized |
// resource for name, description, and company. |
@@ -290,56 +340,25 @@ class RegistryEntry { |
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())); |
+ if (!app_info.delegate_clsid.empty()) { |
grt (UTC plus 2)
2015/08/24 15:08:19
if (dist->GetCommandExecuteImplClsid(&app_info.del
gab
2015/08/27 20:51:07
Done.
gab
2015/08/27 21:48:53
Actually, no, GetProgIdEntries() needs app_info.de
grt (UTC plus 2)
2015/08/29 01:38:41
Ah! Subtle!
gab
2015/08/29 14:39:30
Ok will do.
|
+ ScopedVector<RegistryEntry> delegate_execute_entries = |
+ GetChromeDelegateExecuteEntries(chrome_exe, app_info); |
+ if (!IsChromeMetroSupported()) { |
+ for (RegistryEntry* entry : delegate_execute_entries) { |
+ // Need to get rid of the DelegateExecute keys (verbs, etc.) |
grt (UTC plus 2)
2015/08/24 15:08:19
nit: move the comment out of the loop body so the
gab
2015/08/27 20:51:07
Done.
|
+ // themselves (not only their values) or Chrome fails to launch as |
+ // Windows finds a registered verb hierarchy with no registered |
+ // commands underneath and reports "This file does not have a program |
+ // associated with it for performing this action. Please install a |
+ // program or, if one is already installed, create an association in |
+ // the Default Programs control panel." |
+ entry->set_removal_flag(RemovalFlag::KEY); |
} |
- 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)); |
} |
+ // Move |delegate_execute_entries| to |entries|. |
+ entries->insert(entries->end(), delegate_execute_entries.begin(), |
+ delegate_execute_entries.end()); |
+ delegate_execute_entries.weak_clear(); |
} |
} |
@@ -364,10 +383,13 @@ class RegistryEntry { |
entries->push_back(new RegistryEntry( |
prog_id_path + ShellUtil::kRegShellOpen, app_info.command_line)); |
if (!app_info.delegate_clsid.empty()) { |
- entries->push_back( |
+ scoped_ptr<RegistryEntry> delegate_execute_entry( |
new RegistryEntry(prog_id_path + ShellUtil::kRegShellOpen, |
ShellUtil::kRegDelegateExecute, |
app_info.delegate_clsid)); |
+ if (!IsChromeMetroSupported()) |
+ delegate_execute_entry->set_removal_flag(RemovalFlag::VALUE); |
grt (UTC plus 2)
2015/08/24 15:08:20
do away with the delegate_execute_entry local var
gab
2015/08/27 20:51:07
Done.
|
+ entries->push_back(delegate_execute_entry.Pass()); |
} |
// The following entries are required as of Windows 8, but do not |
@@ -646,16 +668,35 @@ 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; |
+ } |
+ |
+ // Generate work_item tasks required to create (or potentially delete based on |
grt (UTC plus 2)
2015/08/24 15:08:19
nit: Generates...
gab
2015/08/27 20:51:07
Done.
|
+ // |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_); |
grt (UTC plus 2)
2015/08/24 15:08:20
this should be best-effort, no?
gab
2015/08/27 20:51:07
I don't think so, when I was figuring out what sho
grt (UTC plus 2)
2015/08/29 01:38:41
It looks like DeleteRegValueWorkItemTest.DeleteNon
gab
2015/08/29 14:39:30
Done in follow-up CL @ https://codereview.chromium
|
+ } else if (removal_flag_ == RemovalFlag::KEY) { |
+ items->AddDeleteRegKeyWorkItem(root, key_path_, WorkItem::kWow64Default); |
grt (UTC plus 2)
2015/08/24 15:08:19
and this?
gab
2015/08/27 20:51:07
Same thing.
|
} 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); |
+ } |
} |
} |
@@ -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_|. |
@@ -1155,7 +1211,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 |