| 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(
|
|
|