summaryrefslogtreecommitdiffstats
path: root/chrome/installer
diff options
context:
space:
mode:
authorgrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-30 19:07:35 +0000
committergrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-30 19:07:35 +0000
commit08ded4d4482f1f8e11040cc7116230626ae52e7e (patch)
tree4cb66596cb4a06dbe06fe2393cf90a5ad9815b60 /chrome/installer
parentc6d9422188208e9034891c3cb46d37171e33208c (diff)
downloadchromium_src-08ded4d4482f1f8e11040cc7116230626ae52e7e.zip
chromium_src-08ded4d4482f1f8e11040cc7116230626ae52e7e.tar.gz
chromium_src-08ded4d4482f1f8e11040cc7116230626ae52e7e.tar.bz2
On uninstall, remove the registry keys that Chrome lays down to make itself the default browser on pre-Vista systems (and/or for software running on Vista that checks legacy registry keys).
BUG=67355 TEST=install Chrome, making it the default browser. uninstall it and check the box to make Firefox or IE the new default. try following a link in some application which should result in a browser starting up. notice that the chosen default browser starts. Review URL: http://codereview.chromium.org/6746006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@79875 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/installer')
-rw-r--r--chrome/installer/setup/setup_main.cc2
-rw-r--r--chrome/installer/setup/setup_util.cc86
-rw-r--r--chrome/installer/setup/setup_util.h31
-rw-r--r--chrome/installer/setup/setup_util_unittest.cc46
-rw-r--r--chrome/installer/setup/uninstall.cc67
-rw-r--r--chrome/installer/setup/uninstall.h15
-rw-r--r--chrome/installer/util/install_util.cc50
-rw-r--r--chrome/installer/util/install_util.h42
-rw-r--r--chrome/installer/util/install_util_unittest.cc181
9 files changed, 490 insertions, 30 deletions
diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc
index 00188d4..65d8a3ee 100644
--- a/chrome/installer/setup/setup_main.cc
+++ b/chrome/installer/setup/setup_main.cc
@@ -875,7 +875,7 @@ bool HandleNonInstallCmdLineOptions(const InstallationState& original_state,
DCHECK(chrome_install);
if (chrome_install) {
installer::DeleteChromeRegistrationKeys(chrome_install->distribution(),
- HKEY_LOCAL_MACHINE, suffix, tmp);
+ HKEY_LOCAL_MACHINE, suffix, installer_state->target_path(), &tmp);
}
*exit_code = tmp;
} else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) {
diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc
index 9395c28..dbe895c 100644
--- a/chrome/installer/setup/setup_util.cc
+++ b/chrome/installer/setup/setup_util.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
@@ -14,10 +14,12 @@
#include "courgette/courgette.h"
#include "third_party/bspatch/mbspatch.h"
-int installer::ApplyDiffPatch(const FilePath& src,
- const FilePath& patch,
- const FilePath& dest,
- const InstallerState* installer_state) {
+namespace installer {
+
+int ApplyDiffPatch(const FilePath& src,
+ const FilePath& patch,
+ const FilePath& dest,
+ const InstallerState* installer_state) {
VLOG(1) << "Applying patch " << patch.value() << " to file " << src.value()
<< " and generating file " << dest.value();
@@ -42,7 +44,7 @@ int installer::ApplyDiffPatch(const FilePath& src,
dest.value().c_str());
}
-Version* installer::GetMaxVersionFromArchiveDir(const FilePath& chrome_path) {
+Version* GetMaxVersionFromArchiveDir(const FilePath& chrome_path) {
VLOG(1) << "Looking for Chrome version folder under " << chrome_path.value();
Version* version = NULL;
file_util::FileEnumerator version_enum(chrome_path, false,
@@ -69,3 +71,75 @@ Version* installer::GetMaxVersionFromArchiveDir(const FilePath& chrome_path) {
return (version_found ? max_version.release() : NULL);
}
+
+// Open |path| with minimal access to obtain information about it, returning
+// true and populating |handle| on success.
+// static
+bool ProgramCompare::OpenForInfo(const FilePath& path,
+ base::win::ScopedHandle* handle) {
+ DCHECK(handle);
+ handle->Set(base::CreatePlatformFile(path, base::PLATFORM_FILE_OPEN, NULL,
+ NULL));
+ return handle->IsValid();
+}
+
+// Populate |info| for |handle|, returning true on success.
+// static
+bool ProgramCompare::GetInfo(const base::win::ScopedHandle& handle,
+ BY_HANDLE_FILE_INFORMATION* info) {
+ DCHECK(handle.IsValid());
+ return GetFileInformationByHandle(
+ const_cast<base::win::ScopedHandle&>(handle), info) != 0;
+}
+
+ProgramCompare::ProgramCompare(const FilePath& path_to_match)
+ : path_to_match_(path_to_match),
+ file_handle_(base::kInvalidPlatformFileValue),
+ file_info_() {
+ DCHECK(!path_to_match_.empty());
+ if (!OpenForInfo(path_to_match_, &file_handle_)) {
+ PLOG(WARNING) << "Failed opening " << path_to_match_.value()
+ << "; falling back to path string comparisons.";
+ } else if (!GetInfo(file_handle_, &file_info_)) {
+ PLOG(WARNING) << "Failed getting information for "
+ << path_to_match_.value()
+ << "; falling back to path string comparisons.";
+ file_handle_.Close();
+ }
+}
+
+ProgramCompare::~ProgramCompare() {
+}
+
+bool ProgramCompare::Evaluate(const std::wstring& value) const {
+ // Suss out the exe portion of the value, which is expected to be a command
+ // line kinda (or exactly) like:
+ // "c:\foo\bar\chrome.exe" -- "%1"
+ FilePath program(CommandLine::FromString(value).GetProgram());
+ if (program.empty()) {
+ LOG(WARNING) << "Failed to parse an executable name from command line: \""
+ << value << "\"";
+ return false;
+ }
+
+ // Try the simple thing first: do the paths happen to match?
+ if (FilePath::CompareEqualIgnoreCase(path_to_match_.value(), program.value()))
+ return true;
+
+ // If the paths don't match and we couldn't open the expected file, we've done
+ // our best.
+ if (!file_handle_.IsValid())
+ return false;
+
+ // Open the program and see if it references the expected file.
+ base::win::ScopedHandle handle;
+ BY_HANDLE_FILE_INFORMATION info = {};
+
+ return (OpenForInfo(program, &handle) &&
+ GetInfo(handle, &info) &&
+ info.dwVolumeSerialNumber == file_info_.dwVolumeSerialNumber &&
+ info.nFileIndexHigh == file_info_.nFileIndexHigh &&
+ info.nFileIndexLow == file_info_.nFileIndexLow);
+}
+
+} // namespace installer
diff --git a/chrome/installer/setup/setup_util.h b/chrome/installer/setup/setup_util.h
index 267235f..d9dceae 100644
--- a/chrome/installer/setup/setup_util.h
+++ b/chrome/installer/setup/setup_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
@@ -8,9 +8,10 @@
#define CHROME_INSTALLER_SETUP_SETUP_UTIL_H_
#pragma once
+#include "base/file_path.h"
#include "base/version.h"
-
-class FilePath;
+#include "base/win/scoped_handle.h"
+#include "chrome/installer/util/install_util.h"
namespace installer {
@@ -30,6 +31,30 @@ int ApplyDiffPatch(const FilePath& src,
// Returns the maximum version found or NULL if no version is found.
Version* GetMaxVersionFromArchiveDir(const FilePath& chrome_path);
+// A predicate that compares the program portion of a command line with a given
+// file path. First, the file paths are compared directly. If they do not
+// match, the filesystem is consulted to determine if the paths reference the
+// same file.
+class ProgramCompare : public InstallUtil::RegistryValuePredicate {
+ public:
+ explicit ProgramCompare(const FilePath& path_to_match);
+ virtual ~ProgramCompare();
+ virtual bool Evaluate(const std::wstring& value) const OVERRIDE;
+
+ protected:
+ static bool OpenForInfo(const FilePath& path,
+ base::win::ScopedHandle* handle);
+ static bool GetInfo(const base::win::ScopedHandle& handle,
+ BY_HANDLE_FILE_INFORMATION* info);
+
+ FilePath path_to_match_;
+ base::win::ScopedHandle file_handle_;
+ BY_HANDLE_FILE_INFORMATION file_info_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ProgramCompare);
+}; // class ProgramCompare
+
} // namespace installer
#endif // CHROME_INSTALLER_SETUP_SETUP_UTIL_H_
diff --git a/chrome/installer/setup/setup_util_unittest.cc b/chrome/installer/setup/setup_util_unittest.cc
index 04694c5..8dacb9a 100644
--- a/chrome/installer/setup/setup_util_unittest.cc
+++ b/chrome/installer/setup/setup_util_unittest.cc
@@ -8,6 +8,7 @@
#include "base/file_util.h"
#include "base/memory/scoped_temp_dir.h"
#include "base/path_service.h"
+#include "base/string_util.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/installer/setup/setup_util.h"
#include "chrome/installer/util/master_preferences.h"
@@ -93,3 +94,48 @@ TEST_F(SetupUtilTest, GetMaxVersionFromArchiveDirTest) {
version.reset(installer::GetMaxVersionFromArchiveDir(test_dir_.path()));
ASSERT_EQ(version->GetString(), "9.9.9.9");
}
+
+TEST_F(SetupUtilTest, ProgramCompare) {
+ using installer::ProgramCompare;
+ ScopedTempDir temp_dir;
+
+ ASSERT_TRUE(temp_dir.CreateUniqueTempDir());
+
+ FilePath some_long_dir(temp_dir.path().Append(L"Some Long Directory Name"));
+ FilePath expect(some_long_dir.Append(L"file.txt"));
+ FilePath expect_upcase(some_long_dir.Append(L"FILE.txt"));
+ FilePath other(some_long_dir.Append(L"otherfile.txt"));
+
+ // Tests where the expected file doesn't exist.
+
+ // Paths don't match.
+ EXPECT_FALSE(ProgramCompare(expect).Evaluate(L"\"" + other.value() + L"\""));
+ // Paths match exactly.
+ EXPECT_TRUE(ProgramCompare(expect).Evaluate(L"\"" + expect.value() + L"\""));
+ // Paths differ by case.
+ EXPECT_TRUE(ProgramCompare(expect).Evaluate(
+ L"\"" + expect_upcase.value() + L"\""));
+
+ // Tests where the expected file exists.
+ static const char data[] = "data";
+ ASSERT_TRUE(file_util::CreateDirectory(some_long_dir));
+ ASSERT_NE(-1, file_util::WriteFile(expect, data, arraysize(data) - 1));
+ // Paths don't match.
+ EXPECT_FALSE(ProgramCompare(expect).Evaluate(L"\"" + other.value() + L"\""));
+ // Paths match exactly.
+ EXPECT_TRUE(ProgramCompare(expect).Evaluate(L"\"" + expect.value() + L"\""));
+ // Paths differ by case.
+ EXPECT_TRUE(ProgramCompare(expect).Evaluate(
+ L"\"" + expect_upcase.value() + L"\""));
+
+ // Test where strings don't match, but the same file is indicated.
+ std::wstring short_expect;
+ DWORD short_len = GetShortPathName(expect.value().c_str(),
+ WriteInto(&short_expect, MAX_PATH),
+ MAX_PATH);
+ ASSERT_NE(static_cast<DWORD>(0), short_len);
+ ASSERT_GT(static_cast<DWORD>(MAX_PATH), short_len);
+ short_expect.resize(short_len);
+ ASSERT_FALSE(FilePath::CompareEqualIgnoreCase(expect.value(), short_expect));
+ EXPECT_TRUE(ProgramCompare(expect).Evaluate(L"\"" + short_expect + L"\""));
+}
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
index 5d4f129..e145a8c 100644
--- a/chrome/installer/setup/uninstall.cc
+++ b/chrome/installer/setup/uninstall.cc
@@ -19,6 +19,7 @@
#include "chrome/installer/setup/install.h"
#include "chrome/installer/setup/install_worker.h"
#include "chrome/installer/setup/setup_constants.h"
+#include "chrome/installer/setup/setup_util.h"
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/channel_info.h"
#include "chrome/installer/util/delete_after_reboot_helper.h"
@@ -485,7 +486,9 @@ bool ShouldDeleteProfile(const InstallerState& installer_state,
bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root,
const std::wstring& browser_entry_suffix,
- InstallStatus& exit_code) {
+ const FilePath& target_path,
+ InstallStatus* exit_code) {
+ DCHECK(exit_code);
if (!dist->CanSetAsDefault()) {
// We should have never set those keys.
return true;
@@ -526,15 +529,50 @@ bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root,
InstallUtil::DeleteRegistryKey(root, app_path_key);
// Cleanup OpenWithList
+ std::wstring open_with_key;
for (int i = 0; ShellUtil::kFileAssociations[i] != NULL; i++) {
- std::wstring open_with_key(ShellUtil::kRegClasses);
+ open_with_key.assign(ShellUtil::kRegClasses);
file_util::AppendToPath(&open_with_key, ShellUtil::kFileAssociations[i]);
file_util::AppendToPath(&open_with_key, L"OpenWithList");
file_util::AppendToPath(&open_with_key, installer::kChromeExe);
InstallUtil::DeleteRegistryKey(root, open_with_key);
}
- exit_code = installer::UNINSTALL_SUCCESSFUL;
+ // Cleanup in case Chrome had been made the default browser.
+
+ // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
+ // references this Chrome.
+ InstallUtil::DeleteRegistryValueIf(
+ root, ShellUtil::kRegStartMenuInternet, L"",
+ InstallUtil::ValueEquals(dist->GetApplicationName() +
+ browser_entry_suffix));
+
+ // Delete each protocol association if it references this Chrome.
+ ProgramCompare open_command_pred(target_path.Append(kChromeExe));
+ std::wstring parent_key(ShellUtil::kRegClasses);
+ const std::wstring::size_type base_length = parent_key.size();
+ std::wstring child_key;
+ for (const wchar_t* const* proto = &ShellUtil::kProtocolAssociations[0];
+ *proto != NULL; ++proto) {
+ parent_key.resize(base_length);
+ file_util::AppendToPath(&parent_key, *proto);
+ child_key.assign(parent_key).append(ShellUtil::kRegShellOpen);
+ InstallUtil::DeleteRegistryKeyIf(root, parent_key, child_key, L"",
+ open_command_pred);
+ }
+
+ // Delete each filetype association if it references this Chrome.
+ InstallUtil::ValueEquals prog_id_pred(ShellUtil::kChromeHTMLProgId +
+ browser_entry_suffix);
+ for (const wchar_t* const* filetype = &ShellUtil::kFileAssociations[0];
+ *filetype != NULL; ++filetype) {
+ parent_key.resize(base_length);
+ file_util::AppendToPath(&parent_key, *filetype);
+ InstallUtil::DeleteRegistryKeyIf(root, parent_key, parent_key, L"",
+ prog_id_pred);
+ }
+
+ *exit_code = installer::UNINSTALL_SUCCESSFUL;
return true;
}
@@ -670,8 +708,20 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
product.SetMsiMarker(installer_state.system_install(), false);
// Remove all Chrome registration keys.
+ // Registration data is put in HKCU for both system level and user level
+ // installs.
InstallStatus ret = installer::UNKNOWN_STATUS;
- DeleteChromeRegistrationKeys(product.distribution(), reg_root, suffix, ret);
+ DeleteChromeRegistrationKeys(product.distribution(), HKEY_CURRENT_USER,
+ suffix, installer_state.target_path(), &ret);
+
+ // Registration data is put in HKLM for system level and possibly user level
+ // installs (when Chrome is made the default browser at install-time).
+ if (installer_state.system_install() || remove_all &&
+ (!suffix.empty() || CurrentUserHasDefaultBrowser(installer_state,
+ product))) {
+ DeleteChromeRegistrationKeys(product.distribution(), HKEY_LOCAL_MACHINE,
+ suffix, installer_state.target_path(), &ret);
+ }
if (!is_chrome) {
ProcessChromeFrameWorkItems(original_state, installer_state, setup_path,
@@ -681,15 +731,6 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
if (installer_state.is_multi_install())
ProcessGoogleUpdateItems(original_state, installer_state, product);
- // For user level install also we end up creating some keys in HKLM if user
- // sets Chrome as default browser. So delete those as well (needs admin).
- if (remove_all && !installer_state.system_install() &&
- (!suffix.empty() || CurrentUserHasDefaultBrowser(installer_state,
- product))) {
- DeleteChromeRegistrationKeys(product.distribution(), HKEY_LOCAL_MACHINE,
- suffix, ret);
- }
-
ProcessQuickEnableWorkItems(installer_state, original_state);
// Get the state of the installed product (if any)
diff --git a/chrome/installer/setup/uninstall.h b/chrome/installer/setup/uninstall.h
index a3f1ec1..49fd352 100644
--- a/chrome/installer/setup/uninstall.h
+++ b/chrome/installer/setup/uninstall.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2006-2009 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
@@ -12,14 +12,18 @@
#include <string>
-#include "base/command_line.h"
-#include "chrome/installer/util/product.h"
#include "chrome/installer/util/util_constants.h"
+class BrowserDistribution;
+class CommandLine;
+class FilePath;
+class InstallUtil;
+
namespace installer {
class InstallationState;
class InstallerState;
+class Product;
// This function removes all Chrome registration related keys. It returns true
// if successful, otherwise false. The error code is set in |exit_code|.
@@ -27,7 +31,8 @@ class InstallerState;
// suffix for default browser entry name in the registry (optional).
bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root,
const std::wstring& browser_entry_suffix,
- InstallStatus& exit_code);
+ const FilePath& target_path,
+ InstallStatus* exit_code);
// 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
@@ -56,6 +61,6 @@ installer::InstallStatus UninstallProduct(
bool force_uninstall,
const CommandLine& cmd_line);
-} // namespace installer_setup
+} // namespace installer
#endif // CHROME_INSTALLER_SETUP_UNINSTALL_H_
diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc
index 2e2f564..999d84a 100644
--- a/chrome/installer/util/install_util.cc
+++ b/chrome/installer/util/install_util.cc
@@ -294,6 +294,56 @@ bool InstallUtil::DeleteRegistryValue(HKEY reg_root,
}
// static
+bool InstallUtil::DeleteRegistryKeyIf(
+ HKEY root_key,
+ const std::wstring& key_to_delete_path,
+ const std::wstring& key_to_test_path,
+ const wchar_t* value_name,
+ const RegistryValuePredicate& predicate) {
+ DCHECK(root_key);
+ DCHECK(value_name);
+ RegKey key;
+ std::wstring actual_value;
+ if (key.Open(root_key, key_to_test_path.c_str(),
+ KEY_QUERY_VALUE) == ERROR_SUCCESS &&
+ key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
+ predicate.Evaluate(actual_value)) {
+ key.Close();
+ return DeleteRegistryKey(root_key, key_to_delete_path);
+ }
+ return true;
+}
+
+// static
+bool InstallUtil::DeleteRegistryValueIf(
+ HKEY root_key,
+ const wchar_t* key_path,
+ const wchar_t* value_name,
+ const RegistryValuePredicate& predicate) {
+ DCHECK(root_key);
+ DCHECK(key_path);
+ DCHECK(value_name);
+ RegKey key;
+ std::wstring actual_value;
+ if (key.Open(root_key, key_path,
+ KEY_QUERY_VALUE | KEY_SET_VALUE) == ERROR_SUCCESS &&
+ key.ReadValue(value_name, &actual_value) == ERROR_SUCCESS &&
+ predicate.Evaluate(actual_value)) {
+ LONG result = key.DeleteValue(value_name);
+ if (result != ERROR_SUCCESS) {
+ LOG(ERROR) << "Failed to delete registry value: " << value_name
+ << " error: " << result;
+ return false;
+ }
+ }
+ return true;
+}
+
+bool InstallUtil::ValueEquals::Evaluate(const std::wstring& value) const {
+ return value == value_to_match_;
+}
+
+// static
int InstallUtil::GetInstallReturnCode(installer::InstallStatus status) {
switch (status) {
case installer::FIRST_INSTALL_SUCCESS:
diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h
index 4b525b2..5592b40 100644
--- a/chrome/installer/util/install_util.h
+++ b/chrome/installer/util/install_util.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
@@ -108,6 +108,46 @@ class InstallUtil {
static bool DeleteRegistryValue(HKEY reg_root, const std::wstring& key_path,
const std::wstring& value_name);
+ // An interface to a predicate function for use by DeleteRegistryKeyIf and
+ // DeleteRegistryValueIf.
+ class RegistryValuePredicate {
+ public:
+ virtual ~RegistryValuePredicate() { }
+ virtual bool Evaluate(const std::wstring& value) const = 0;
+ };
+
+ // Deletes the key |key_to_delete_path| under |root_key| iff the value
+ // |value_name| in the key |key_to_test_path| under |root_key| satisfies
+ // |predicate|. |value_name| must be an empty string to test the key's
+ // default value. Returns false if the value satisfied the predicate but
+ // could not be deleted, otherwise returns true.
+ static bool DeleteRegistryKeyIf(HKEY root_key,
+ const std::wstring& key_to_delete_path,
+ const std::wstring& key_to_test_path,
+ const wchar_t* value_name,
+ const RegistryValuePredicate& predicate);
+
+ // Deletes the value |value_name| in the key |key_path| under |root_key| iff
+ // its current value satisfies |predicate|. |value_name| must be an empty
+ // string to test the key's default value. Returns false if the value
+ // satisfied the predicate but could not be deleted, otherwise returns true.
+ static bool DeleteRegistryValueIf(HKEY root_key,
+ const wchar_t* key_path,
+ const wchar_t* value_name,
+ const RegistryValuePredicate& predicate);
+
+ // A predicate that performs a case-sensitive string comparison.
+ class ValueEquals : public RegistryValuePredicate {
+ public:
+ explicit ValueEquals(const std::wstring& value_to_match)
+ : value_to_match_(value_to_match) { }
+ virtual bool Evaluate(const std::wstring& value) const OVERRIDE;
+ protected:
+ std::wstring value_to_match_;
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ValueEquals);
+ };
+
// Returns zero on install success, or an InstallStatus value otherwise.
static int GetInstallReturnCode(installer::InstallStatus install_status);
diff --git a/chrome/installer/util/install_util_unittest.cc b/chrome/installer/util/install_util_unittest.cc
index f67d689..f05a9dd 100644
--- a/chrome/installer/util/install_util_unittest.cc
+++ b/chrome/installer/util/install_util_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -14,11 +14,20 @@
#include "chrome/installer/util/install_util.h"
#include "chrome/installer/util/master_preferences.h"
#include "chrome/installer/util/product_unittest.h"
+#include "testing/gmock/include/gmock/gmock.h"
using base::win::RegKey;
using installer::InstallationState;
using installer::InstallerState;
using installer::MasterPreferences;
+using ::testing::_;
+using ::testing::Return;
+using ::testing::StrEq;
+
+class MockRegistryValuePredicate : public InstallUtil::RegistryValuePredicate {
+ public:
+ MOCK_CONST_METHOD1(Evaluate, bool(const std::wstring&));
+};
class InstallUtilTest : public TestWithTempDirAndDeleteTempOverrideKeys {
protected:
@@ -172,3 +181,173 @@ TEST_F(InstallUtilTest, UpdateInstallerStage) {
}
TempRegKeyOverride::DeleteAllTempKeys();
}
+
+TEST_F(InstallUtilTest, DeleteRegistryKeyIf) {
+ const HKEY root = HKEY_CURRENT_USER;
+ std::wstring parent_key_path(L"SomeKey\\ToDelete");
+ std::wstring child_key_path(parent_key_path);
+ child_key_path.append(L"\\ChildKey\\WithAValue");
+ const wchar_t value_name[] = L"some_value_name";
+ const wchar_t value[] = L"hi mom";
+
+ {
+ TempRegKeyOverride override(root, L"root_key");
+ // Nothing to delete if the keys aren't even there.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(_)).Times(0);
+ ASSERT_FALSE(RegKey(root, parent_key_path.c_str(),
+ KEY_QUERY_VALUE).Valid());
+ EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf(
+ root, parent_key_path, child_key_path, value_name, pred));
+ EXPECT_FALSE(RegKey(root, parent_key_path.c_str(),
+ KEY_QUERY_VALUE).Valid());
+ }
+
+ // Parent exists, but not child: no delete.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(_)).Times(0);
+ ASSERT_TRUE(RegKey(root, parent_key_path.c_str(), KEY_SET_VALUE).Valid());
+ EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf(
+ root, parent_key_path, child_key_path, value_name, pred));
+ EXPECT_TRUE(RegKey(root, parent_key_path.c_str(),
+ KEY_QUERY_VALUE).Valid());
+ }
+
+ // Child exists, but no value: no delete.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(_)).Times(0);
+ ASSERT_TRUE(RegKey(root, child_key_path.c_str(), KEY_SET_VALUE).Valid());
+ EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf(
+ root, parent_key_path, child_key_path, value_name, pred));
+ EXPECT_TRUE(RegKey(root, parent_key_path.c_str(),
+ KEY_QUERY_VALUE).Valid());
+ }
+
+ // Value exists, but doesn't match: no delete.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(StrEq(L"foosball!"))).WillOnce(Return(false));
+ ASSERT_EQ(ERROR_SUCCESS,
+ RegKey(root, child_key_path.c_str(),
+ KEY_SET_VALUE).WriteValue(value_name, L"foosball!"));
+ EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf(
+ root, parent_key_path, child_key_path, value_name, pred));
+ EXPECT_TRUE(RegKey(root, parent_key_path.c_str(),
+ KEY_QUERY_VALUE).Valid());
+ }
+
+ // Value exists, and matches: delete.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(StrEq(value))).WillOnce(Return(true));
+ ASSERT_EQ(ERROR_SUCCESS,
+ RegKey(root, child_key_path.c_str(),
+ KEY_SET_VALUE).WriteValue(value_name, value));
+ EXPECT_TRUE(InstallUtil::DeleteRegistryKeyIf(
+ root, parent_key_path, child_key_path, value_name, pred));
+ EXPECT_FALSE(RegKey(root, parent_key_path.c_str(),
+ KEY_QUERY_VALUE).Valid());
+ }
+ }
+ TempRegKeyOverride::DeleteAllTempKeys();
+}
+
+TEST_F(InstallUtilTest, DeleteRegistryValueIf) {
+ const HKEY root = HKEY_CURRENT_USER;
+ std::wstring key_path(L"SomeKey\\ToDelete");
+ const wchar_t value_name[] = L"some_value_name";
+ const wchar_t value[] = L"hi mom";
+
+ {
+ TempRegKeyOverride override(root, L"root_key");
+ // Nothing to delete if the key isn't even there.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(_)).Times(0);
+ ASSERT_FALSE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid());
+ EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf(
+ root, key_path.c_str(), value_name, pred));
+ EXPECT_FALSE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid());
+ }
+
+ // Key exists, but no value: no delete.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(_)).Times(0);
+ ASSERT_TRUE(RegKey(root, key_path.c_str(), KEY_SET_VALUE).Valid());
+ EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf(
+ root, key_path.c_str(), value_name, pred));
+ EXPECT_TRUE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid());
+ }
+
+ // Value exists, but doesn't match: no delete.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(StrEq(L"foosball!"))).WillOnce(Return(false));
+ ASSERT_EQ(ERROR_SUCCESS,
+ RegKey(root, key_path.c_str(),
+ KEY_SET_VALUE).WriteValue(value_name, L"foosball!"));
+ EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf(
+ root, key_path.c_str(), value_name, pred));
+ EXPECT_TRUE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid());
+ EXPECT_TRUE(RegKey(root, key_path.c_str(),
+ KEY_QUERY_VALUE).ValueExists(value_name));
+ }
+
+ // Value exists, and matches: delete.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(StrEq(value))).WillOnce(Return(true));
+ ASSERT_EQ(ERROR_SUCCESS,
+ RegKey(root, key_path.c_str(),
+ KEY_SET_VALUE).WriteValue(value_name, value));
+ EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf(
+ root, key_path.c_str(), value_name, pred));
+ EXPECT_TRUE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid());
+ EXPECT_FALSE(RegKey(root, key_path.c_str(),
+ KEY_QUERY_VALUE).ValueExists(value_name));
+ }
+ }
+ TempRegKeyOverride::DeleteAllTempKeys();
+
+ {
+ TempRegKeyOverride override(root, L"root_key");
+ // Default value matches: delete.
+ {
+ MockRegistryValuePredicate pred;
+
+ EXPECT_CALL(pred, Evaluate(StrEq(value))).WillOnce(Return(true));
+ ASSERT_EQ(ERROR_SUCCESS,
+ RegKey(root, key_path.c_str(),
+ KEY_SET_VALUE).WriteValue(L"", value));
+ EXPECT_TRUE(InstallUtil::DeleteRegistryValueIf(
+ root, key_path.c_str(), L"", pred));
+ EXPECT_TRUE(RegKey(root, key_path.c_str(), KEY_QUERY_VALUE).Valid());
+ EXPECT_FALSE(RegKey(root, key_path.c_str(),
+ KEY_QUERY_VALUE).ValueExists(L""));
+ }
+ }
+ TempRegKeyOverride::DeleteAllTempKeys();
+}
+
+TEST_F(InstallUtilTest, ValueEquals) {
+ InstallUtil::ValueEquals pred(L"howdy");
+
+ EXPECT_FALSE(pred.Evaluate(L""));
+ EXPECT_FALSE(pred.Evaluate(L"Howdy"));
+ EXPECT_FALSE(pred.Evaluate(L"howdy!"));
+ EXPECT_FALSE(pred.Evaluate(L"!howdy"));
+ EXPECT_TRUE(pred.Evaluate(L"howdy"));
+}