diff options
author | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-03 20:05:51 +0000 |
---|---|---|
committer | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-03-03 20:05:51 +0000 |
commit | 995708fd774716311b6feeaa08d5855c302ecf18 (patch) | |
tree | 54aa413dab26c5e4df97f6026d3f99d3f39e5400 /chrome | |
parent | f130ef429844fc2f99089fe3ffc09af0b187577b (diff) | |
download | chromium_src-995708fd774716311b6feeaa08d5855c302ecf18.zip chromium_src-995708fd774716311b6feeaa08d5855c302ecf18.tar.gz chromium_src-995708fd774716311b6feeaa08d5855c302ecf18.tar.bz2 |
Add support for the quick-enable-cf command to the installer. This encompases:
- Adding facilities for new-style Google Update commands (ProductCommand, ProductCommands)
- Adding support to the validator to validate the quick-enable-cf command
- Adding juju to the installation and uninstallation flows to put the command into place when installing/upgrading Chrome in multi-install mode when CF is either not installed or is in ready-mode, and making sure the command is not there when Chrome Frame is installed.
BUG=none
TEST=Install Chrome in multi-install mode and see if the Google Update version key for the binaries (app guid {4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}) contains a key named "quick-enable-cf" that has a CommandLine value indicating setup.exe w/ --multi-install [ --system-level ] --quick-enable-cf, a SendsPings value of 1, and a WebAccessible value of 1. Then try the other variations, like install CF in ready-mode and make sure that quick-enable-cf is still there. Make sure that installing CF causes it to be removed, etc.
Review URL: http://codereview.chromium.org/6588003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@76783 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
21 files changed, 1365 insertions, 109 deletions
diff --git a/chrome/chrome_installer_util.gypi b/chrome/chrome_installer_util.gypi index 5064ddc..83f6ae1 100644 --- a/chrome/chrome_installer_util.gypi +++ b/chrome/chrome_installer_util.gypi @@ -57,6 +57,10 @@ 'installer/util/master_preferences_constants.h', 'installer/util/move_tree_work_item.cc', 'installer/util/move_tree_work_item.h', + 'installer/util/app_command.cc', + 'installer/util/app_command.h', + 'installer/util/app_commands.cc', + 'installer/util/app_commands.h', 'installer/util/self_reg_work_item.cc', 'installer/util/self_reg_work_item.h', 'installer/util/set_reg_value_work_item.cc', diff --git a/chrome/installer/setup/chrome_frame_quick_enable.cc b/chrome/installer/setup/chrome_frame_quick_enable.cc index d100392..8743fc8 100644 --- a/chrome/installer/setup/chrome_frame_quick_enable.cc +++ b/chrome/installer/setup/chrome_frame_quick_enable.cc @@ -42,7 +42,7 @@ InstallStatus CheckQuickEnablePreconditions( return NON_MULTI_INSTALLATION_EXISTS; } - // Chrome Frame must not be installed. + // Chrome Frame must not be installed (ready-mode doesn't count). const ProductState* cf_state = machine_state.GetProductState(installer_state.system_install(), BrowserDistribution::CHROME_FRAME); @@ -52,7 +52,9 @@ InstallStatus CheckQuickEnablePreconditions( BrowserDistribution::CHROME_FRAME); } - if (cf_state != NULL) { + if (cf_state != NULL && + !cf_state->uninstall_command().HasSwitch( + switches::kChromeFrameReadyMode)) { LOG(ERROR) << "Chrome Frame already installed."; return installer_state.system_install() ? SYSTEM_LEVEL_INSTALL_EXISTS : USER_LEVEL_INSTALL_EXISTS; @@ -120,6 +122,12 @@ InstallStatus ChromeFrameQuickEnable(const InstallationState& machine_state, *chrome_state); AddGoogleUpdateWorkItems(*installer_state, item_list.get()); + // Add the items to remove the quick-enable-cf command from the registry. + AddQuickEnableWorkItems(*installer_state, machine_state, + &chrome_state->uninstall_command().GetProgram(), + &chrome_state->version(), + item_list.get()); + if (!item_list->Do()) { item_list->Rollback(); status = INSTALL_FAILED; diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc index dc66c70..495d3b1 100644 --- a/chrome/installer/setup/install_worker.cc +++ b/chrome/installer/setup/install_worker.cc @@ -615,6 +615,9 @@ void AddInstallWorkItems(const InstallationState& original_state, AddGoogleUpdateWorkItems(installer_state, install_list); + AddQuickEnableWorkItems(installer_state, original_state, &setup_path, + &new_version, install_list); + // Append the tasks that run after the installation. AppendPostInstallTasks(installer_state, setup_path, @@ -901,4 +904,139 @@ void RefreshElevationPolicy() { } } +void AddQuickEnableWorkItems(const InstallerState& installer_state, + const InstallationState& machine_state, + const FilePath* setup_path, + const Version* new_version, + WorkItemList* work_item_list) { + DCHECK(setup_path || + installer_state.operation() == InstallerState::UNINSTALL); + DCHECK(new_version || + installer_state.operation() == InstallerState::UNINSTALL); + DCHECK(work_item_list); + + const bool system_install = installer_state.system_install(); + bool have_multi_chrome = false; + bool have_chrome_frame = false; + + // STEP 1: Figure out the state of the machine before the operation. + const ProductState* product_state = NULL; + + // Is multi-install Chrome already on the machine? + product_state = + machine_state.GetProductState(system_install, + BrowserDistribution::CHROME_BROWSER); + if (product_state != NULL && product_state->is_multi_install()) + have_multi_chrome = true; + + // Is Chrome Frame !ready-mode already on the machine? + product_state = + machine_state.GetProductState(system_install, + BrowserDistribution::CHROME_FRAME); + if (product_state != NULL && + !product_state->uninstall_command().HasSwitch( + switches::kChromeFrameReadyMode)) + have_chrome_frame = true; + + // STEP 2: Now take into account the current operation. + const Product* product = NULL; + + if (installer_state.operation() == InstallerState::UNINSTALL) { + // Forget about multi-install Chrome if it is being uninstalled. + product = + installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER); + if (product != NULL && installer_state.is_multi_install()) + have_multi_chrome = false; + + // Forget about Chrome Frame if it is being uninstalled. Note that we don't + // bother to check !HasOption(kOptionReadyMode) since have_chrome_frame + // should have been false for that case in the first place. It's odd if it + // wasn't, but the right thing to do in that case is to proceed with the + // thought that CF will not be installed in any sense when we reach the + // finish line. + if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME) != NULL) + have_chrome_frame = false; + } else { + // Check if we're installing multi-install Chrome. + product = + installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER); + if (product != NULL && installer_state.is_multi_install()) + have_multi_chrome = true; + + // Check if we're installing Chrome Frame !ready-mode. + product = installer_state.FindProduct(BrowserDistribution::CHROME_FRAME); + if (product != NULL && !product->HasOption(kOptionReadyMode)) + have_chrome_frame = true; + } + + // STEP 3: Decide what to do based on the final state of things. + enum QuickEnableOperation { + DO_NOTHING, + ADD_COMMAND, + REMOVE_COMMAND + } operation = DO_NOTHING; + FilePath binaries_setup_path; + + if (have_chrome_frame) { + // Chrome Frame !ready-mode is or will be installed. Unconditionally remove + // the quick-enable-cf command from the binaries. We do this even if + // multi-install Chrome isn't installed since we don't want them left + // behind in any case. + operation = REMOVE_COMMAND; + } else if (have_multi_chrome) { + // Chrome Frame isn't (to be) installed or is (to be) installed only in + // ready-mode, while multi-install Chrome is (to be) installed. Add the + // quick-enable-cf command to the binaries. + operation = ADD_COMMAND; + // The path to setup.exe contains the version of the Chrome binaries, so it + // takes a little work to get it right. + if (installer_state.operation() == InstallerState::UNINSTALL) { + // Chrome Frame is being uninstalled. Use the path to the currently + // installed Chrome setup.exe. + product_state = + machine_state.GetProductState(system_install, + BrowserDistribution::CHROME_BROWSER); + DCHECK(product_state); + binaries_setup_path = product_state->uninstall_command().GetProgram(); + } else { + // Chrome is being installed, updated, or otherwise being operated on. + // Use the path to the given |setup_path| in the normal location of + // multi-install Chrome of the given |version|. + DCHECK(installer_state.is_multi_install()); + binaries_setup_path = + installer_state.GetInstallerDirectory(*new_version).Append( + setup_path->BaseName()); + } + } + + // STEP 4: Take action. + if (operation != DO_NOTHING) { + // Get the path to the quick-enable-cf command for the binaries. + BrowserDistribution* binaries = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES); + std::wstring cmd_key(binaries->GetVersionKey()); + cmd_key.append(1, L'\\').append(google_update::kRegCommandsKey) + .append(1, L'\\').append(kCmdQuickEnableCf); + + if (operation == ADD_COMMAND) { + DCHECK(!binaries_setup_path.empty()); + CommandLine cmd_line(binaries_setup_path); + cmd_line.AppendSwitch(switches::kMultiInstall); + if (installer_state.system_install()) + cmd_line.AppendSwitch(switches::kSystemLevel); + if (installer_state.verbose_logging()) + cmd_line.AppendSwitch(switches::kVerboseLogging); + cmd_line.AppendSwitch(switches::kChromeFrameQuickEnable); + AppCommand cmd(cmd_line.command_line_string(), true, true); + cmd.AddWorkItems(installer_state.root_key(), cmd_key, work_item_list); + } else { + DCHECK(operation == REMOVE_COMMAND); + work_item_list->AddDeleteRegKeyWorkItem(installer_state.root_key(), + cmd_key)->set_log_message( + "removing quick-enable-cf command"); + } + } +} + } // namespace installer diff --git a/chrome/installer/setup/install_worker.h b/chrome/installer/setup/install_worker.h index fd78922..414510d 100644 --- a/chrome/installer/setup/install_worker.h +++ b/chrome/installer/setup/install_worker.h @@ -136,6 +136,19 @@ void AppendUninstallCommandLineFlags(const InstallerState& installer_state, // Refreshes the elevation policy on platforms where it is supported. void RefreshElevationPolicy(); +// Add work items to add or remove the "quick-enable-cf" to the multi-installer +// binaries' version key on the basis of the current operation (represented in +// |installer_state|) and the pre-existing machine configuration (represented in +// |machine_state|). |setup_path| (the path to the executable currently being +// run) and |new_version| (the version of the product(s) currently being +// installed) are required when processing product installation; they are unused +// (and may therefore be NULL) when uninstalling. +void AddQuickEnableWorkItems(const InstallerState& installer_state, + const InstallationState& machine_state, + const FilePath* setup_path, + const Version* new_version, + WorkItemList* work_item_list); + } // namespace installer #endif // CHROME_INSTALLER_SETUP_INSTALL_WORKER_H_ diff --git a/chrome/installer/setup/install_worker_unittest.cc b/chrome/installer/setup/install_worker_unittest.cc index c08acbc..ea040b1 100644 --- a/chrome/installer/setup/install_worker_unittest.cc +++ b/chrome/installer/setup/install_worker_unittest.cc @@ -7,8 +7,12 @@ #include "base/win/registry.h" #include "base/version.h" #include "chrome/common/chrome_constants.h" +#include "chrome/installer/util/delete_reg_key_work_item.h" +#include "chrome/installer/util/create_reg_key_work_item.h" +#include "chrome/installer/util/helper.h" #include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/installer_state.h" +#include "chrome/installer/util/set_reg_value_work_item.h" #include "chrome/installer/util/util_constants.h" #include "chrome/installer/util/work_item_list.h" @@ -17,11 +21,17 @@ using installer::InstallationState; using installer::InstallerState; +using installer::Product; using installer::ProductState; using ::testing::_; using ::testing::AtLeast; +using ::testing::AtMost; +using ::testing::Eq; +using ::testing::Return; +using ::testing::StrCaseEq; using ::testing::StrEq; +using ::testing::StrictMock; // Mock classes to help with testing //------------------------------------------------------------------------------ @@ -81,6 +91,9 @@ class MockProductState : public ProductState { // Takes ownership of |version|. void set_version(Version* version) { version_.reset(version); } void set_multi_install(bool multi) { multi_install_ = multi; } + void SetUninstallProgram(const FilePath& setup_exe) { + uninstall_command_ = CommandLine(setup_exe); + } void AddUninstallSwitch(const std::string& option) { uninstall_command_.AppendSwitch(option); } @@ -142,26 +155,83 @@ class InstallWorkerTest : public testing::Test { virtual void TearDown() { } - MockInstallationState* BuildChromeInstallationState(bool system_level, - bool multi_install) { - scoped_ptr<MockInstallationState> installation_state( - new MockInstallationState()); - + void AddChromeToInstallationState( + bool system_level, + bool multi_install, + bool with_chrome_frame_ready_mode, + MockInstallationState* installation_state) { MockProductState product_state; product_state.set_version(current_version_->Clone()); product_state.set_multi_install(multi_install); + BrowserDistribution* dist = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER); + FilePath install_path = + installer::GetChromeInstallPath(system_level, dist); + product_state.SetUninstallProgram( + install_path.Append(installer::kSetupExe)); + product_state.AddUninstallSwitch(installer::switches::kUninstall); + if (system_level) + product_state.AddUninstallSwitch(installer::switches::kSystemLevel); if (multi_install) { product_state.AddUninstallSwitch(installer::switches::kMultiInstall); + product_state.AddUninstallSwitch(installer::switches::kChrome); + if (with_chrome_frame_ready_mode) { + product_state.AddUninstallSwitch(installer::switches::kChromeFrame); + product_state.AddUninstallSwitch( + installer::switches::kChromeFrameReadyMode); + } } installation_state->SetProductState(system_level, BrowserDistribution::CHROME_BROWSER, product_state); + } + void AddChromeFrameToInstallationState( + bool system_level, + bool multi_install, + bool ready_mode, + MockInstallationState* installation_state) { + MockProductState product_state; + product_state.set_version(current_version_->Clone()); + product_state.set_multi_install(multi_install); + BrowserDistribution* dist = + BrowserDistribution::GetSpecificDistribution( + multi_install ? BrowserDistribution::CHROME_BINARIES : + BrowserDistribution::CHROME_FRAME); + FilePath install_path = + installer::GetChromeInstallPath(system_level, dist); + product_state.SetUninstallProgram( + install_path.Append(installer::kSetupExe)); + product_state.AddUninstallSwitch(installer::switches::kUninstall); + product_state.AddUninstallSwitch(installer::switches::kChromeFrame); + if (system_level) + product_state.AddUninstallSwitch(installer::switches::kSystemLevel); + if (multi_install) { + product_state.AddUninstallSwitch(installer::switches::kMultiInstall); + if (ready_mode) { + product_state.AddUninstallSwitch( + installer::switches::kChromeFrameReadyMode); + } + } + + installation_state->SetProductState(system_level, + BrowserDistribution::CHROME_FRAME, + product_state); + } + + MockInstallationState* BuildChromeInstallationState(bool system_level, + bool multi_install) { + scoped_ptr<MockInstallationState> installation_state( + new MockInstallationState()); + AddChromeToInstallationState(system_level, multi_install, false, + installation_state.get()); return installation_state.release(); } - MockInstallerState* BuildChromeInstallerState(bool system_install, + static MockInstallerState* BuildBasicInstallerState( + bool system_install, bool multi_install, const InstallationState& machine_state, InstallerState::Operation operation) { @@ -173,14 +243,81 @@ class InstallWorkerTest : public testing::Test { installer_state->set_operation(operation); // Hope this next one isn't checked for now. installer_state->set_state_key(L"PROBABLY_INVALID_REG_PATH"); - if (multi_install) { - installer_state->set_package_type(InstallerState::MULTI_PACKAGE); - } + installer_state->set_package_type(multi_install ? + InstallerState::MULTI_PACKAGE : + InstallerState::SINGLE_PACKAGE); + return installer_state.release(); + } + + static void AddChromeToInstallerState( + const InstallationState& machine_state, + MockInstallerState* installer_state) { + // Fresh install or upgrade? const ProductState* chrome = - machine_state.GetProductState(system_install, + machine_state.GetProductState(installer_state->system_install(), BrowserDistribution::CHROME_BROWSER); - installer_state->AddProductFromState(BrowserDistribution::CHROME_BROWSER, - *chrome); + if (chrome != NULL) { + installer_state->AddProductFromState(BrowserDistribution::CHROME_BROWSER, + *chrome); + } else { + BrowserDistribution* dist = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER); + scoped_ptr<Product> product(new Product(dist)); + if (installer_state->is_multi_install()) + product->SetOption(installer::kOptionMultiInstall, true); + installer_state->AddProduct(&product); + } + } + + static void AddChromeFrameToInstallerState( + const InstallationState& machine_state, + bool ready_mode, + MockInstallerState* installer_state) { + // Fresh install or upgrade? + const ProductState* cf = + machine_state.GetProductState(installer_state->system_install(), + BrowserDistribution::CHROME_FRAME); + if (cf != NULL) { + installer_state->AddProductFromState(BrowserDistribution::CHROME_FRAME, + *cf); + } else { + BrowserDistribution* dist = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_FRAME); + scoped_ptr<Product> product(new Product(dist)); + if (installer_state->is_multi_install()) { + product->SetOption(installer::kOptionMultiInstall, true); + if (ready_mode) + product->SetOption(installer::kOptionReadyMode, true); + } + installer_state->AddProduct(&product); + } + } + + static MockInstallerState* BuildChromeInstallerState( + bool system_install, + bool multi_install, + const InstallationState& machine_state, + InstallerState::Operation operation) { + scoped_ptr<MockInstallerState> installer_state( + BuildBasicInstallerState(system_install, multi_install, machine_state, + operation)); + AddChromeToInstallerState(machine_state, installer_state.get()); + return installer_state.release(); + } + + static MockInstallerState* BuildChromeFrameInstallerState( + bool system_install, + bool multi_install, + bool ready_mode, + const InstallationState& machine_state, + InstallerState::Operation operation) { + scoped_ptr<MockInstallerState> installer_state( + BuildBasicInstallerState(system_install, multi_install, machine_state, + operation)); + AddChromeFrameToInstallerState(machine_state, ready_mode, + installer_state.get()); return installer_state.release(); } @@ -368,3 +505,224 @@ TEST_F(InstallWorkerTest, ElevationPolicyExistingSingleCFNoop) { *new_version_.get(), &work_item_list); } + +// Test scenarios under which the quick-enable-cf command should not exist after +// the run. We're permissive in that we allow the DeleteRegKeyWorkItem even if +// it isn't strictly needed. +class QuickEnableAbsentTest : public InstallWorkerTest { + public: + virtual void SetUp() { + InstallWorkerTest::SetUp(); + root_key_ = system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + delete_reg_key_item_.reset( + WorkItem::CreateDeleteRegKeyWorkItem(root_key_, kRegKeyPath)); + machine_state_.reset(new MockInstallationState()); + EXPECT_CALL(work_item_list_, + AddDeleteRegKeyWorkItem(Eq(root_key_), StrCaseEq(kRegKeyPath))) + .Times(AtMost(1)) + .WillRepeatedly(Return(delete_reg_key_item_.get())); + } + virtual void TearDown() { + machine_state_.reset(); + delete_reg_key_item_.reset(); + root_key_ = NULL; + InstallWorkerTest::TearDown(); + } + protected: + static const bool system_level_ = false; + static const wchar_t kRegKeyPath[]; + HKEY root_key_; + scoped_ptr<DeleteRegKeyWorkItem> delete_reg_key_item_; + scoped_ptr<MockInstallationState> machine_state_; + StrictMock<MockWorkItemList> work_item_list_; +}; + +const wchar_t QuickEnableAbsentTest::kRegKeyPath[] = + L"Software\\Google\\Update\\Clients\\" + L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}\\Commands\\quick-enable-cf"; + +TEST_F(QuickEnableAbsentTest, CleanInstallSingleChrome) { + // Install single Chrome on a clean system. + scoped_ptr<MockInstallerState> installer_state( + BuildChromeInstallerState(system_level_, false, *machine_state_, + InstallerState::SINGLE_INSTALL_OR_UPDATE)); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +TEST_F(QuickEnableAbsentTest, CleanInstallSingleChromeFrame) { + // Install single Chrome Frame on a clean system. + scoped_ptr<MockInstallerState> installer_state( + BuildChromeFrameInstallerState(system_level_, false, false, + *machine_state_, + InstallerState::SINGLE_INSTALL_OR_UPDATE)); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +TEST_F(QuickEnableAbsentTest, CleanInstallMultiChromeFrame) { + // Install multi Chrome Frame on a clean system. + scoped_ptr<MockInstallerState> installer_state( + BuildChromeFrameInstallerState(system_level_, true, false, + *machine_state_, + InstallerState::MULTI_INSTALL)); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +TEST_F(QuickEnableAbsentTest, CleanInstallMultiChromeChromeFrame) { + // Install multi Chrome and Chrome Frame on a clean system. + scoped_ptr<MockInstallerState> installer_state( + BuildBasicInstallerState(system_level_, true, *machine_state_, + InstallerState::MULTI_INSTALL)); + AddChromeToInstallerState(*machine_state_, installer_state.get()); + AddChromeFrameToInstallerState(*machine_state_, false, + installer_state.get()); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +TEST_F(QuickEnableAbsentTest, UninstallMultiChromeLeaveMultiChromeFrame) { + // Uninstall multi Chrome on a machine with multi Chrome Frame. + AddChromeToInstallationState(system_level_, true, false, + machine_state_.get()); + AddChromeFrameToInstallationState(system_level_, true, false, + machine_state_.get()); + scoped_ptr<MockInstallerState> installer_state( + BuildBasicInstallerState(system_level_, true, *machine_state_, + InstallerState::UNINSTALL)); + AddChromeToInstallerState(*machine_state_, installer_state.get()); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +TEST_F(QuickEnableAbsentTest, UninstallMultiChromeLeaveSingleChromeFrame) { + // Uninstall multi Chrome on a machine with single Chrome Frame. + AddChromeToInstallationState(system_level_, true, false, + machine_state_.get()); + AddChromeFrameToInstallationState(system_level_, false, false, + machine_state_.get()); + scoped_ptr<MockInstallerState> installer_state( + BuildBasicInstallerState(system_level_, true, *machine_state_, + InstallerState::UNINSTALL)); + AddChromeToInstallerState(*machine_state_, installer_state.get()); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +TEST_F(QuickEnableAbsentTest, AcceptReadyMode) { + // Accept ready-mode. + AddChromeToInstallationState(system_level_, true, true, + machine_state_.get()); + AddChromeFrameToInstallationState(system_level_, true, true, + machine_state_.get()); + scoped_ptr<MockInstallerState> installer_state( + BuildBasicInstallerState(system_level_, true, *machine_state_, + InstallerState::UNINSTALL)); + AddChromeToInstallerState(*machine_state_, installer_state.get()); + AddChromeFrameToInstallerState(*machine_state_, false, installer_state.get()); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +// Test scenarios under which the quick-enable-cf command should exist after the +// run. +class QuickEnablePresentTest : public InstallWorkerTest { + public: + virtual void SetUp() { + InstallWorkerTest::SetUp(); + root_key_ = system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + create_reg_key_work_item_.reset( + WorkItem::CreateCreateRegKeyWorkItem(root_key_, kRegKeyPath)); + set_reg_value_work_item_.reset( + WorkItem::CreateSetRegValueWorkItem(root_key_, kRegKeyPath, L"", L"", + false)); + machine_state_.reset(new MockInstallationState()); + EXPECT_CALL(work_item_list_, + AddCreateRegKeyWorkItem(Eq(root_key_), StrCaseEq(kRegKeyPath))) + .Times(1) + .WillOnce(Return(create_reg_key_work_item_.get())); + EXPECT_CALL(work_item_list_, + AddSetRegStringValueWorkItem(Eq(root_key_), + StrCaseEq(kRegKeyPath), + StrEq(L"CommandLine"), _, + Eq(true))) + .Times(1) + .WillOnce(Return(set_reg_value_work_item_.get())); + EXPECT_CALL(work_item_list_, + AddSetRegDwordValueWorkItem(Eq(root_key_), + StrCaseEq(kRegKeyPath), _, + Eq(static_cast<DWORD>(1)), + Eq(true))) + .Times(2) + .WillRepeatedly(Return(set_reg_value_work_item_.get())); + } + virtual void TearDown() { + machine_state_.reset(); + set_reg_value_work_item_.reset(); + create_reg_key_work_item_.reset(); + root_key_ = NULL; + InstallWorkerTest::TearDown(); + } + protected: + static const bool system_level_ = false; + static const wchar_t kRegKeyPath[]; + HKEY root_key_; + scoped_ptr<CreateRegKeyWorkItem> create_reg_key_work_item_; + scoped_ptr<SetRegValueWorkItem> set_reg_value_work_item_; + scoped_ptr<MockInstallationState> machine_state_; + StrictMock<MockWorkItemList> work_item_list_; +}; + +const wchar_t QuickEnablePresentTest::kRegKeyPath[] = + L"Software\\Google\\Update\\Clients\\" + L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}\\Commands\\quick-enable-cf"; + +TEST_F(QuickEnablePresentTest, CleanInstallMultiChrome) { + // Install multi Chrome on a clean system. + scoped_ptr<MockInstallerState> installer_state( + BuildChromeInstallerState(system_level_, true, *machine_state_, + InstallerState::MULTI_INSTALL)); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +TEST_F(QuickEnablePresentTest, CleanInstallMultiChromeReadyMode) { + // Install multi Chrome with Chrome Frame ready-mode on a clean system. + scoped_ptr<MockInstallerState> installer_state( + BuildBasicInstallerState(system_level_, true, *machine_state_, + InstallerState::MULTI_INSTALL)); + AddChromeToInstallerState(*machine_state_, installer_state.get()); + AddChromeFrameToInstallerState(*machine_state_, true, + installer_state.get()); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +TEST_F(QuickEnablePresentTest, UninstallSingleChromeFrame) { + // Uninstall single Chrome Frame on a machine with multi Chrome. + AddChromeToInstallationState(system_level_, true, false, + machine_state_.get()); + AddChromeFrameToInstallationState(system_level_, false, false, + machine_state_.get()); + scoped_ptr<MockInstallerState> installer_state( + BuildBasicInstallerState(system_level_, false, *machine_state_, + InstallerState::UNINSTALL)); + AddChromeFrameToInstallerState(*machine_state_, false, installer_state.get()); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} + +TEST_F(QuickEnablePresentTest, UninstallMultiChromeFrame) { + // Uninstall multi Chrome Frame on a machine with multi Chrome. + AddChromeToInstallationState(system_level_, true, false, + machine_state_.get()); + AddChromeFrameToInstallationState(system_level_, true, false, + machine_state_.get()); + scoped_ptr<MockInstallerState> installer_state( + BuildBasicInstallerState(system_level_, true, *machine_state_, + InstallerState::UNINSTALL)); + AddChromeFrameToInstallerState(*machine_state_, false, installer_state.get()); + AddQuickEnableWorkItems(*installer_state, *machine_state_, &setup_path_, + new_version_.get(), &work_item_list_); +} diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index 96093cd..b8a841d 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc @@ -1083,7 +1083,8 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, cmd_line.HasSwitch( installer::switches::kRemoveChromeRegistration) || cmd_line.HasSwitch(installer::switches::kInactiveUserToast) || - cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) { + cmd_line.HasSwitch(installer::switches::kSystemLevelToast) || + cmd_line.HasSwitch(installer::switches::kChromeFrameQuickEnable)) { return installer::SXS_OPTION_NOT_SUPPORTED; } } diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc index 6bb6873..2b71dcb 100644 --- a/chrome/installer/setup/uninstall.cc +++ b/chrome/installer/setup/uninstall.cc @@ -102,6 +102,20 @@ void ProcessGoogleUpdateItems( } } +// Adds or removes the quick-enable-cf command to the binaries' version key in +// the registry as needed. +void ProcessQuickEnableWorkItems( + const installer::InstallerState& installer_state, + const installer::InstallationState& machine_state) { + scoped_ptr<WorkItemList> work_item_list( + WorkItem::CreateNoRollbackWorkItemList()); + + AddQuickEnableWorkItems(installer_state, machine_state, NULL, NULL, + work_item_list.get()); + if (!work_item_list->Do()) + LOG(ERROR) << "Failed to update quick-enable-cf command."; +} + } // namespace namespace installer { @@ -358,9 +372,9 @@ bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, DeleteResult DeleteFilesAndFolders(const InstallerState& installer_state, const Version& installed_version) { - VLOG(1) << "DeleteFilesAndFolders: " << installer_state.target_path().value(); - if (installer_state.target_path().empty()) { - LOG(ERROR) << "Could not get installation destination path."; + const FilePath& target_path = installer_state.target_path(); + if (target_path.empty()) { + LOG(ERROR) << "DeleteFilesAndFolders: no installation destination path."; return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. } @@ -369,32 +383,30 @@ DeleteResult DeleteFilesAndFolders(const InstallerState& installer_state, // Avoid leaving behind a Temp dir. If one exists, ask SelfCleaningTempDir to // clean it up for us. This may involve scheduling it for deletion after // reboot. Don't report that a reboot is required in this case, however. - FilePath temp_path( - installer_state.target_path().DirName().Append(kInstallTempDir)); + FilePath temp_path(target_path.DirName().Append(kInstallTempDir)); if (file_util::DirectoryExists(temp_path)) { installer::SelfCleaningTempDir temp_dir; - if (!temp_dir.Initialize(installer_state.target_path().DirName(), - kInstallTempDir) || !temp_dir.Delete()) + if (!temp_dir.Initialize(target_path.DirName(), kInstallTempDir) || + !temp_dir.Delete()) { LOG(ERROR) << "Failed to delete temp dir " << temp_path.value(); + } } - VLOG(1) << "Deleting install path " << installer_state.target_path().value(); - if (!file_util::Delete(installer_state.target_path(), true)) { - LOG(ERROR) << "Failed to delete folder (1st try): " - << installer_state.target_path().value(); + VLOG(1) << "Deleting install path " << target_path.value(); + if (!file_util::Delete(target_path, true)) { + LOG(ERROR) << "Failed to delete folder (1st try): " << target_path.value(); if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { // We don't try killing Chrome processes for Chrome Frame builds since // that is unlikely to help. Instead, schedule files for deletion and // return a value that will trigger a reboot prompt. - ScheduleDirectoryForDeletion( - installer_state.target_path().value().c_str()); + ScheduleDirectoryForDeletion(target_path.value().c_str()); result = DELETE_REQUIRES_REBOOT; } else { // Try closing any running chrome processes and deleting files once again. CloseAllChromeProcesses(); - if (!file_util::Delete(installer_state.target_path(), true)) { + if (!file_util::Delete(target_path, true)) { LOG(ERROR) << "Failed to delete folder (2nd try): " - << installer_state.target_path().value(); + << target_path.value(); result = DELETE_FAILED; } } @@ -404,11 +416,11 @@ DeleteResult DeleteFilesAndFolders(const InstallerState& installer_state, // If we need a reboot to continue, schedule the parent directories for // deletion unconditionally. If they are not empty, the session manager // will not delete them on reboot. - ScheduleParentAndGrandparentForDeletion(installer_state.target_path()); + ScheduleParentAndGrandparentForDeletion(target_path); } else { // Now check and delete if the parent directories are empty // For example Google\Chrome or Chromium - DeleteEmptyParentDir(installer_state.target_path()); + DeleteEmptyParentDir(target_path); } return result; } @@ -682,6 +694,8 @@ InstallStatus UninstallProduct(const InstallationState& original_state, suffix, ret); } + ProcessQuickEnableWorkItems(installer_state, original_state); + // Get the state of the installed product (if any) const ProductState* product_state = original_state.GetProductState(installer_state.system_install(), diff --git a/chrome/installer/util/app_command.cc b/chrome/installer/util/app_command.cc new file mode 100644 index 0000000..588fe86 --- /dev/null +++ b/chrome/installer/util/app_command.cc @@ -0,0 +1,88 @@ +// 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. + +#include "chrome/installer/util/app_command.h" + +#include "base/logging.h" +#include "base/win/registry.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/work_item_list.h" + +namespace installer { + +AppCommand::AppCommand() + : sends_pings_(false), + is_web_accessible_(false) { +} + +AppCommand::AppCommand(const std::wstring& command_line, + bool sends_pings, + bool is_web_accessible) + : command_line_(command_line), + sends_pings_(sends_pings), + is_web_accessible_(is_web_accessible) { +} + +bool AppCommand::Initialize(const base::win::RegKey& key) { + if (!key.Valid()) { + LOG(DFATAL) << "Cannot initialize an AppCommand from an invalid key."; + return false; + } + + LONG result = ERROR_SUCCESS; + std::wstring cmd_line; + DWORD sends_pings = 0; + DWORD is_web_acc = 0; + + result = key.ReadValue(google_update::kRegCommandLineField, &cmd_line); + if (result != ERROR_SUCCESS) { + LOG(WARNING) << "Error reading " << google_update::kRegCommandLineField + << " value from registry: " << result; + return false; + } + + result = key.ReadValueDW(google_update::kRegSendsPingsField, &sends_pings); + if (result != ERROR_SUCCESS) { + LOG(WARNING) << "Error reading " << google_update::kRegSendsPingsField + << " value from registry: " << result; + return false; + } + + result = key.ReadValueDW(google_update::kRegWebAccessibleField, &is_web_acc); + if (result != ERROR_SUCCESS) { + LOG(WARNING) << "Error reading " << google_update::kRegWebAccessibleField + << " value from registry: " << result; + return false; + } + + command_line_.swap(cmd_line); + sends_pings_ = (sends_pings != 0); + is_web_accessible_ = (is_web_acc != 0); + + return true; +} + +void AppCommand::AddWorkItems(HKEY predefined_root, + const std::wstring& command_path, + WorkItemList* item_list) const { + const DWORD sends_pings = sends_pings_ ? 1U : 0U; + const DWORD is_web_accessible = is_web_accessible_ ? 1U : 0U; + + item_list->AddCreateRegKeyWorkItem(predefined_root, command_path) + ->set_log_message("creating quick-enable-cf command registry key"); + item_list->AddSetRegValueWorkItem(predefined_root, command_path, + google_update::kRegCommandLineField, + command_line_, true) + ->set_log_message("setting quick-enable-cf CommandLine registry value"); + item_list->AddSetRegValueWorkItem(predefined_root, command_path, + google_update::kRegSendsPingsField, + sends_pings, true) + ->set_log_message("setting quick-enable-cf SendsPings registry value"); + item_list->AddSetRegValueWorkItem(predefined_root, command_path, + google_update::kRegWebAccessibleField, + is_web_accessible, true) + ->set_log_message("setting quick-enable-cf WebAccessible registry value"); +} + +} // namespace installer diff --git a/chrome/installer/util/app_command.h b/chrome/installer/util/app_command.h new file mode 100644 index 0000000..88e4355 --- /dev/null +++ b/chrome/installer/util/app_command.h @@ -0,0 +1,66 @@ +// 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. + +#ifndef CHROME_INSTALLER_UTIL_APP_COMMAND_H_ +#define CHROME_INSTALLER_UTIL_APP_COMMAND_H_ +#pragma once + +#include <windows.h> + +#include <string> + +class WorkItemList; + +namespace base { +namespace win { +class RegKey; +} +} + +namespace installer { + +// A description of a command registered by setup.exe that can be invoked by +// Google Update. This class is CopyConstructible and Assignable for use in +// STL containers. +class AppCommand { + public: + AppCommand(); + AppCommand(const std::wstring& command_line, bool send_pings, + bool is_web_accessible); + // The implicit dtor, copy ctor and assignment operator are desired. + + // Initializes an instance from the command in |key|. + bool Initialize(const base::win::RegKey& key); + + // Adds to |item_list| work items to write this object to the key named + // |command_path| under |predefined_root|. + void AddWorkItems(HKEY predefined_root, + const std::wstring& command_path, + WorkItemList* item_list) const; + + // Returns the command-line for the app command as it is represented in the + // registry. Use CommandLine::FromString() on this value to check arguments + // or to launch the command. + const std::wstring& command_line() const { return command_line_; } + void set_command_line(const std::wstring& command_line) { + command_line_ = command_line; + } + + bool sends_pings() const { return sends_pings_; } + void set_sends_pings(bool sends_pings) { sends_pings_ = sends_pings; } + + bool is_web_accessible() const { return is_web_accessible_; } + void set_is_web_accessible(bool is_web_accessible) { + is_web_accessible_ = is_web_accessible; + } + + protected: + std::wstring command_line_; + bool sends_pings_; + bool is_web_accessible_; +}; + +} // namespace installer + +#endif // CHROME_INSTALLER_UTIL_APP_COMMAND_H_ diff --git a/chrome/installer/util/app_commands.cc b/chrome/installer/util/app_commands.cc new file mode 100644 index 0000000..de7dcc2 --- /dev/null +++ b/chrome/installer/util/app_commands.cc @@ -0,0 +1,91 @@ +// 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. + +#include "chrome/installer/util/app_commands.h" + +#include "base/logging.h" +#include "base/win/registry.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/work_item_list.h" + +using base::win::RegKey; + +namespace installer { + +AppCommands::AppCommands() { +} + +AppCommands::~AppCommands() { +} + +bool AppCommands::Initialize(const base::win::RegKey& key) { + if (!key.Valid()) { + LOG(DFATAL) << "Cannot initialize AppCommands from an invalid key."; + return false; + } + + using base::win::RegistryKeyIterator; + static const wchar_t kEmptyString[] = L""; + + commands_.clear(); + + RegKey cmd_key; + LONG result; + AppCommand command; + for (RegistryKeyIterator key_iterator(key.Handle(), kEmptyString); + key_iterator.Valid(); ++key_iterator) { + const wchar_t* name = key_iterator.Name(); + result = cmd_key.Open(key.Handle(), name, KEY_QUERY_VALUE); + if (result != ERROR_SUCCESS) { + LOG(ERROR) << "Failed to open key \"" << name + << "\" with last-error code " << result; + } else if (command.Initialize(cmd_key)) { + commands_[name] = command; + } else { + VLOG(1) << "Skipping over key \"" << name + << "\" as it does not appear to hold a product command."; + } + } + + return true; +} + +AppCommands& AppCommands::CopyFrom(const AppCommands& other) { + commands_ = other.commands_; + + return *this; +} + +void AppCommands::Clear() { + commands_.clear(); +} + +bool AppCommands::Get(const std::wstring& command_id, + AppCommand* command) const { + DCHECK(command); + CommandMap::const_iterator it(commands_.find(command_id)); + if (it == commands_.end()) + return false; + *command = it->second; + return true; +} + +bool AppCommands::Set(const std::wstring& command_id, + const AppCommand& command) { + std::pair<CommandMap::iterator, bool> result( + commands_.insert(std::make_pair(command_id, command))); + if (!result.second) + result.first->second = command; + return result.second; +} + +bool AppCommands::Remove(const std::wstring& command_id) { + return commands_.erase(command_id) != 0; +} + +AppCommands::CommandMapRange AppCommands::GetIterators() const { + return std::make_pair(commands_.begin(), commands_.end()); +} + +} // namespace installer diff --git a/chrome/installer/util/app_commands.h b/chrome/installer/util/app_commands.h new file mode 100644 index 0000000..cff16aa --- /dev/null +++ b/chrome/installer/util/app_commands.h @@ -0,0 +1,75 @@ +// 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. + +#ifndef CHROME_INSTALLER_UTIL_APP_COMMANDS_H_ +#define CHROME_INSTALLER_UTIL_APP_COMMANDS_H_ +#pragma once + +#include <windows.h> + +#include <map> +#include <string> +#include <utility> + +#include "base/basictypes.h" +#include "chrome/installer/util/app_command.h" + +class WorkItemList; + +namespace base { +namespace win { +class RegKey; +} +} + +namespace installer { + +// A collection of AppCommand objects. +class AppCommands { + public: + typedef std::map<std::wstring, AppCommand> CommandMap; + typedef std::pair<CommandMap::const_iterator, CommandMap::const_iterator> + CommandMapRange; + + AppCommands(); + ~AppCommands(); + + // Initialize an instance from the set of commands in a given registry key + // (typically the "Commands" subkey of a BrowserDistribution's "version key"). + // |key| must have been opened with at least + // KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE access rights. + bool Initialize(const base::win::RegKey& key); + + // Replaces the contents of this object with that of |other|. + AppCommands& CopyFrom(const AppCommands& other); + + // Clears this instance. + void Clear(); + + // Retrieves the command identified by |command_id| from the set, copying it + // into |command| and returning true if present. + bool Get(const std::wstring& command_id, AppCommand* command) const; + + // Sets a command in the collection, adding it if it doesn't already exist. + // Returns true if a new command is added; false if |command_id| was already + // present and has been replaced with |command|. + bool Set(const std::wstring& command_id, const AppCommand& command); + + // Removes a command from the collection. Returns false if |command_id| was + // not found. + bool Remove(const std::wstring& command_id); + + // Returns a pair of STL iterators defining the range of objects in the + // collection. + CommandMapRange GetIterators() const; + + protected: + CommandMap commands_; + + DISALLOW_COPY_AND_ASSIGN(AppCommands); +}; + +} // namespace installer + +#endif // CHROME_INSTALLER_UTIL_APP_COMMANDS_H_ diff --git a/chrome/installer/util/google_update_constants.cc b/chrome/installer/util/google_update_constants.cc index 1419128..d3ac0fe 100644 --- a/chrome/installer/util/google_update_constants.cc +++ b/chrome/installer/util/google_update_constants.cc @@ -14,27 +14,32 @@ const wchar_t kRegPathClientState[] = L"Software\\Google\\Update\\ClientState"; const wchar_t kRegPathClientStateMedium[] = L"Software\\Google\\Update\\ClientStateMedium"; +const wchar_t kRegCommandsKey[] = L"Commands"; + const wchar_t kRegApField[] = L"ap"; const wchar_t kRegBrowserField[] = L"browser"; +const wchar_t kRegCFEndTempOptOutCmdField[] = L"CFEndTempOptOutCmd"; +const wchar_t kRegCFOptInCmdField[] = L"CFOptInCmd"; +const wchar_t kRegCFOptOutCmdField[] = L"CFOptOutCmd"; +const wchar_t kRegCFTempOptOutCmdField[] = L"CFTempOptOutCmd"; const wchar_t kRegClientField[] = L"client"; +const wchar_t kRegCommandLineField[] = L"CommandLine"; const wchar_t kRegDidRunField[] = L"dr"; +const wchar_t kRegEULAAceptedField[] = L"eulaaccepted"; const wchar_t kRegLangField[] = L"lang"; const wchar_t kRegLastCheckedField[] = L"LastChecked"; +const wchar_t kRegLastRunTimeField[] = L"lastrun"; const wchar_t kRegMetricsId[] = L"metricsid"; const wchar_t kRegMSIField[] = L"msi"; const wchar_t kRegNameField[] = L"name"; -const wchar_t kRegOopcrashesField[] = L"oopcrashes"; const wchar_t kRegOldVersionField[] = L"opv"; -const wchar_t kRegRenameCmdField[] = L"cmd"; +const wchar_t kRegOopcrashesField[] = L"oopcrashes"; const wchar_t kRegRLZBrandField[] = L"brand"; +const wchar_t kRegReferralField[] = L"referral"; +const wchar_t kRegRenameCmdField[] = L"cmd"; +const wchar_t kRegSendsPingsField[] = L"SendsPings"; const wchar_t kRegUsageStatsField[] = L"usagestats"; const wchar_t kRegVersionField[] = L"pv"; -const wchar_t kRegReferralField[] = L"referral"; -const wchar_t kRegEULAAceptedField[] = L"eulaaccepted"; -const wchar_t kRegLastRunTimeField[] = L"lastrun"; -const wchar_t kRegCFTempOptOutCmdField[] = L"CFTempOptOutCmd"; -const wchar_t kRegCFEndTempOptOutCmdField[] = L"CFEndTempOptOutCmd"; -const wchar_t kRegCFOptOutCmdField[] = L"CFOptOutCmd"; -const wchar_t kRegCFOptInCmdField[] = L"CFOptInCmd"; +const wchar_t kRegWebAccessibleField[] = L"WebAccessible"; } // namespace google_update diff --git a/chrome/installer/util/google_update_constants.h b/chrome/installer/util/google_update_constants.h index e1125a7..825fcbd 100644 --- a/chrome/installer/util/google_update_constants.h +++ b/chrome/installer/util/google_update_constants.h @@ -25,27 +25,34 @@ extern const wchar_t kRegPathClients[]; extern const wchar_t kRegPathClientState[]; extern const wchar_t kRegPathClientStateMedium[]; +// The name of the "Commands" key that lives in an app's Clients key +// (a.k.a. "Version" key). +extern const wchar_t kRegCommandsKey[]; + extern const wchar_t kRegApField[]; extern const wchar_t kRegBrowserField[]; +extern const wchar_t kRegCFEndTempOptOutCmdField[]; +extern const wchar_t kRegCFOptInCmdField[]; +extern const wchar_t kRegCFOptOutCmdField[]; +extern const wchar_t kRegCFTempOptOutCmdField[]; extern const wchar_t kRegClientField[]; +extern const wchar_t kRegCommandLineField[]; extern const wchar_t kRegDidRunField[]; +extern const wchar_t kRegEULAAceptedField[]; extern const wchar_t kRegLangField[]; extern const wchar_t kRegLastCheckedField[]; extern const wchar_t kRegMetricsId[]; extern const wchar_t kRegMSIField[]; extern const wchar_t kRegNameField[]; -extern const wchar_t kRegOopcrashesField[]; extern const wchar_t kRegOldVersionField[]; -extern const wchar_t kRegRenameCmdField[]; +extern const wchar_t kRegOopcrashesField[]; extern const wchar_t kRegRLZBrandField[]; +extern const wchar_t kRegReferralField[]; +extern const wchar_t kRegRenameCmdField[]; +extern const wchar_t kRegSendsPingsField[]; extern const wchar_t kRegUsageStatsField[]; extern const wchar_t kRegVersionField[]; -extern const wchar_t kRegReferralField[]; -extern const wchar_t kRegEULAAceptedField[]; -extern const wchar_t kRegCFTempOptOutCmdField[]; -extern const wchar_t kRegCFEndTempOptOutCmdField[]; -extern const wchar_t kRegCFOptOutCmdField[]; -extern const wchar_t kRegCFOptInCmdField[]; +extern const wchar_t kRegWebAccessibleField[]; // last time that chrome ran in the Time internal format. extern const wchar_t kRegLastRunTimeField[]; diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc index 78642fc..4aec77b 100644 --- a/chrome/installer/util/install_util.cc +++ b/chrome/installer/util/install_util.cc @@ -203,8 +203,8 @@ bool InstallUtil::DeleteRegistryKey(RegKey& root_key, VLOG(1) << "Deleting registry key " << key_path; LONG result = root_key.DeleteKey(key_path.c_str()); if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) { - PLOG(ERROR) << "Failed to delete registry key: " << key_path - << " error: " << result; + LOG(ERROR) << "Failed to delete registry key: " << key_path + << " error: " << result; return false; } return true; diff --git a/chrome/installer/util/installation_state.cc b/chrome/installer/util/installation_state.cc index f766195..4d315ff 100644 --- a/chrome/installer/util/installation_state.cc +++ b/chrome/installer/util/installation_state.cc @@ -25,6 +25,20 @@ bool ProductState::Initialize(bool system_install, BrowserDistribution::GetSpecificDistribution(type)); } +// Initializes |commands| from the "Commands" subkey of |version_key|. +// Returns false if there is no "Commands" subkey or on error. +// static +bool ProductState::InitializeCommands(const base::win::RegKey& version_key, + AppCommands* commands) { + static const DWORD kAccess = KEY_ENUMERATE_SUB_KEYS | KEY_QUERY_VALUE; + base::win::RegKey commands_key; + + if (commands_key.Open(version_key.Handle(), google_update::kRegCommandsKey, + kAccess) == ERROR_SUCCESS) + return commands->Initialize(commands_key); + return false; +} + bool ProductState::Initialize(bool system_install, BrowserDistribution* distribution) { const std::wstring version_key(distribution->GetVersionKey()); @@ -47,6 +61,8 @@ bool ProductState::Initialize(bool system_install, if (key.ReadValue(google_update::kRegRenameCmdField, &rename_cmd_) != ERROR_SUCCESS) rename_cmd_.clear(); + if (!InitializeCommands(key, &commands_)) + commands_.Clear(); // Read from the ClientState key. channel_.set_value(std::wstring()); uninstall_command_ = CommandLine(CommandLine::NO_PROGRAM); @@ -99,6 +115,7 @@ ProductState& ProductState::CopyFrom(const ProductState& other) { other.old_version_.get() == NULL ? NULL : other.old_version_->Clone()); rename_cmd_ = other.rename_cmd_; uninstall_command_ = other.uninstall_command_; + commands_.CopyFrom(other.commands_); msi_ = other.msi_; multi_install_ = other.multi_install_; diff --git a/chrome/installer/util/installation_state.h b/chrome/installer/util/installation_state.h index 817e0af..a63627c 100644 --- a/chrome/installer/util/installation_state.h +++ b/chrome/installer/util/installation_state.h @@ -12,11 +12,18 @@ #include "base/command_line.h" #include "base/file_path.h" #include "base/scoped_ptr.h" +#include "chrome/installer/util/app_commands.h" #include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/channel_info.h" class Version; +namespace base { +namespace win { +class RegKey; +} +} + namespace installer { class InstallationState; @@ -64,15 +71,22 @@ class ProductState { // True if |uninstall_command| contains --multi-install. bool is_multi_install() const { return multi_install_; } + // Returns the set of Google Update commands. + const AppCommands& commands() const { return commands_; } + // Returns this object a la operator=(). ProductState& CopyFrom(const ProductState& other); protected: + static bool InitializeCommands(const base::win::RegKey& version_key, + AppCommands* commands); + ChannelInfo channel_; scoped_ptr<Version> version_; scoped_ptr<Version> old_version_; std::wstring rename_cmd_; CommandLine uninstall_command_; + AppCommands commands_; bool msi_; bool multi_install_; diff --git a/chrome/installer/util/installation_validator.cc b/chrome/installer/util/installation_validator.cc index 7115f2f..56840c5 100644 --- a/chrome/installer/util/installation_validator.cc +++ b/chrome/installer/util/installation_validator.cc @@ -7,6 +7,7 @@ #include "chrome/installer/util/installation_validator.h" #include <algorithm> +#include <set> #include "base/logging.h" #include "base/version.h" @@ -63,6 +64,19 @@ void InstallationValidator::ChromeFrameRules::AddProductSwitchExpectations( false)); } +BrowserDistribution::Type + InstallationValidator::ChromeBinariesRules::distribution_type() const { + return BrowserDistribution::CHROME_BINARIES; +} + +void InstallationValidator::ChromeBinariesRules::AddProductSwitchExpectations( + const InstallationState& machine_state, + bool system_install, + const ProductState& product_state, + SwitchExpectations* expectations) const { + NOTREACHED(); +} + // static const InstallationValidator::InstallationType InstallationValidator::kInstallationTypes[] = { @@ -77,6 +91,102 @@ const InstallationValidator::InstallationType CHROME_FRAME_READY_MODE_CHROME_MULTI }; +// Validates the "quick-enable-cf" Google Update product command. +void InstallationValidator::ValidateQuickEnableCfCommand( + const ProductContext& ctx, + const AppCommand& command, + bool* is_valid) { + DCHECK(is_valid); + + CommandLine the_command(CommandLine::FromString(command.command_line())); + + ValidateSetupPath(ctx, the_command.GetProgram(), "quick enable cf", is_valid); + + SwitchExpectations expected; + + expected.push_back( + std::make_pair(std::string(switches::kChromeFrameQuickEnable), true)); + expected.push_back(std::make_pair(std::string(switches::kSystemLevel), + ctx.system_install)); + expected.push_back(std::make_pair(std::string(switches::kMultiInstall), + ctx.state.is_multi_install())); + + ValidateCommandExpectations(ctx, the_command, expected, "quick enable cf", + is_valid); + + if (!command.sends_pings()) { + *is_valid = false; + LOG(ERROR) << "Quick-enable-cf command is not configured to send pings."; + } + + if (!command.is_web_accessible()) { + *is_valid = false; + LOG(ERROR) << "Quick-enable-cf command is not web accessible."; + } +} + +// Validates a product's set of Google Update product commands against a +// collection of expectations. +void InstallationValidator::ValidateAppCommandExpectations( + const ProductContext& ctx, + const CommandExpectations& expectations, + bool* is_valid) { + DCHECK(is_valid); + + CommandExpectations the_expectations(expectations); + + AppCommands::CommandMapRange cmd_iterators( + ctx.state.commands().GetIterators()); + CommandExpectations::iterator expectation; + for (; cmd_iterators.first != cmd_iterators.second; ++cmd_iterators.first) { + const std::wstring& cmd_id = cmd_iterators.first->first; + // Do we have an expectation for this command? + expectation = the_expectations.find(cmd_id); + if (expectation != the_expectations.end()) { + (expectation->second)(ctx, cmd_iterators.first->second, is_valid); + // Remove this command from the set of expectations since we found it. + the_expectations.erase(expectation); + } else { + *is_valid = false; + LOG(ERROR) << ctx.dist->GetAppShortCutName() + << " has an unexpected Google Update product command named \"" + << cmd_id << "\"."; + } + } + + // Report on any expected commands that weren't present. + CommandExpectations::const_iterator scan(the_expectations.begin()); + CommandExpectations::const_iterator end(the_expectations.end()); + for (; scan != end; ++scan) { + *is_valid = false; + LOG(ERROR) << ctx.dist->GetAppShortCutName() + << " is missing the Google Update product command named \"" + << scan->first << "\"."; + } +} + +// Validates the multi-install binaries' Google Update commands. +void InstallationValidator::ValidateBinariesCommands( + const ProductContext& ctx, + bool* is_valid) { + DCHECK(is_valid); + + // The quick-enable-cf command must be present if Chrome is installed either + // alone or with CF in ready-mode. + const ChannelInfo& channel = ctx.state.channel(); + const ProductState* chrome_state = ctx.machine_state.GetProductState( + ctx.system_install, BrowserDistribution::CHROME_BROWSER); + const ProductState* cf_state = ctx.machine_state.GetProductState( + ctx.system_install, BrowserDistribution::CHROME_FRAME); + + CommandExpectations expectations; + + if (chrome_state != NULL && (cf_state == NULL || channel.IsReadyMode())) + expectations[kCmdQuickEnableCf] = &ValidateQuickEnableCfCommand; + + ValidateAppCommandExpectations(ctx, expectations, is_valid); +} + // Validates the multi-install binaries at level |system_level|. void InstallationValidator::ValidateBinaries( const InstallationState& machine_state, @@ -159,6 +269,17 @@ void InstallationValidator::ValidateBinaries( LOG(ERROR) << "Chrome Binaries are present without Chrome yet Chrome Frame " "is not multi-install."; } + + ChromeBinariesRules binaries_rules; + ProductContext ctx = { + machine_state, + system_install, + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES), + binaries_state, + binaries_rules + }; + ValidateBinariesCommands(ctx, is_valid); } // Validates the path to |setup_exe| for the product described by |ctx|. @@ -183,7 +304,7 @@ void InstallationValidator::ValidateSetupPath(const ProductContext& ctx, if (!FilePath::CompareEqualIgnoreCase(expected_path.value(), setup_exe.value())) { *is_valid = false; - LOG(ERROR) << ctx.dist->GetApplicationName() << " path to " << purpose + LOG(ERROR) << ctx.dist->GetAppShortCutName() << " path to " << purpose << " is not " << expected_path.value() << ": " << setup_exe.value(); } @@ -201,7 +322,7 @@ void InstallationValidator::ValidateCommandExpectations( const SwitchExpectations::value_type& expectation = expected[i]; if (command.HasSwitch(expectation.first) != expectation.second) { *is_valid = false; - LOG(ERROR) << ctx.dist->GetApplicationName() << " " << source + LOG(ERROR) << ctx.dist->GetAppShortCutName() << " " << source << (expectation.second ? " is missing" : " has") << " \"" << expectation.first << "\"" << (expectation.second ? "" : " but shouldn't") << ": " @@ -271,14 +392,14 @@ void InstallationValidator::ValidateOldVersionValues( if (ctx.state.old_version() == NULL) { if (!ctx.state.rename_cmd().empty()) { *is_valid = false; - LOG(ERROR) << ctx.dist->GetApplicationName() + LOG(ERROR) << ctx.dist->GetAppShortCutName() << " has a rename command but no opv: " << ctx.state.rename_cmd(); } } else { if (ctx.state.rename_cmd().empty()) { *is_valid = false; - LOG(ERROR) << ctx.dist->GetApplicationName() + LOG(ERROR) << ctx.dist->GetAppShortCutName() << " has an opv but no rename command: " << ctx.state.old_version()->GetString(); } else { @@ -301,7 +422,7 @@ void InstallationValidator::ValidateMultiInstallProduct( // Version must match that of binaries. if (ctx.state.version().CompareTo(binaries->version()) != 0) { *is_valid = false; - LOG(ERROR) << "Version of " << ctx.dist->GetApplicationName() + LOG(ERROR) << "Version of " << ctx.dist->GetAppShortCutName() << " (" << ctx.state.version().GetString() << ") does not " "match that of Chrome Binaries (" << binaries->version().GetString() << ")."; @@ -310,13 +431,23 @@ void InstallationValidator::ValidateMultiInstallProduct( // Channel value must match that of binaries. if (!ctx.state.channel().Equals(binaries->channel())) { *is_valid = false; - LOG(ERROR) << "Channel name of " << ctx.dist->GetApplicationName() + LOG(ERROR) << "Channel name of " << ctx.dist->GetAppShortCutName() << " (" << ctx.state.channel().value() << ") does not match that of Chrome Binaries (" << binaries->channel().value() << ")."; } } +// Validates the Google Update commands for the product described in |ctx|. +void InstallationValidator::ValidateAppCommands( + const ProductContext& ctx, + bool* is_valid) { + DCHECK(is_valid); + + // Products are not expected to have any commands. + ValidateAppCommandExpectations(ctx, CommandExpectations(), is_valid); +} + // Validates the product described in |product_state| according to |rules|. void InstallationValidator::ValidateProduct( const InstallationState& machine_state, @@ -340,6 +471,8 @@ void InstallationValidator::ValidateProduct( if (product_state.is_multi_install()) ValidateMultiInstallProduct(ctx, is_valid); + + ValidateAppCommands(ctx, is_valid); } // static diff --git a/chrome/installer/util/installation_validator.h b/chrome/installer/util/installation_validator.h index 710faf6..450c714 100644 --- a/chrome/installer/util/installation_validator.h +++ b/chrome/installer/util/installation_validator.h @@ -6,6 +6,7 @@ #define CHROME_INSTALLER_UTIL_INSTALLATION_VALIDATOR_H_ #pragma once +#include <map> #include <string> #include <utility> #include <vector> @@ -23,6 +24,7 @@ class Version; namespace installer { class InstallationState; +class AppCommand; class ProductState; // A class that validates the state of an installation. Violations are logged @@ -76,7 +78,12 @@ class InstallationValidator { InstallationType* type); protected: + struct ProductContext; typedef std::vector<std::pair<std::string, bool> > SwitchExpectations; + typedef void (*CommandValidatorFn)(const ProductContext& ctx, + const AppCommand& command, + bool* is_valid); + typedef std::map<std::wstring, CommandValidatorFn> CommandExpectations; // An interface to product-specific validation rules. class ProductRules { @@ -112,6 +119,17 @@ class InstallationValidator { SwitchExpectations* expectations) const OVERRIDE; }; + // Validation rules for the multi-install Chrome binaries. + class ChromeBinariesRules : public ProductRules { + public: + virtual BrowserDistribution::Type distribution_type() const OVERRIDE; + virtual void AddProductSwitchExpectations( + const InstallationState& machine_state, + bool system_install, + const ProductState& product_state, + SwitchExpectations* expectations) const OVERRIDE; + }; + struct ProductContext { const InstallationState& machine_state; bool system_install; @@ -120,6 +138,15 @@ class InstallationValidator { const ProductRules& rules; }; + static void ValidateQuickEnableCfCommand(const ProductContext& ctx, + const AppCommand& command, + bool* is_valid); + static void ValidateAppCommandExpectations( + const ProductContext& ctx, + const CommandExpectations& expectations, + bool* is_valid); + static void ValidateBinariesCommands(const ProductContext& ctx, + bool* is_valid); static void ValidateBinaries(const InstallationState& machine_state, bool system_install, const ProductState& binaries_state, @@ -143,6 +170,8 @@ class InstallationValidator { bool* is_valid); static void ValidateMultiInstallProduct(const ProductContext& ctx, bool* is_valid); + static void ValidateAppCommands(const ProductContext& ctx, + bool* is_valid); static void ValidateProduct(const InstallationState& machine_state, bool system_install, const ProductState& product_state, diff --git a/chrome/installer/util/installation_validator_unittest.cc b/chrome/installer/util/installation_validator_unittest.cc index 2bd5459..4992dc76a 100644 --- a/chrome/installer/util/installation_validator_unittest.cc +++ b/chrome/installer/util/installation_validator_unittest.cc @@ -2,6 +2,8 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <map> + #include "base/command_line.h" #include "base/file_path.h" #include "base/logging.h" @@ -18,9 +20,11 @@ using installer::ChannelInfo; using installer::InstallationValidator; using installer::InstallationState; +using installer::AppCommand; using installer::ProductState; using testing::_; using testing::StrictMock; +using testing::Values; namespace { @@ -75,9 +79,15 @@ class FakeProductState : public ProductState { const char* version, int channel_modifiers, Vehicle vehicle); + void AddQuickEnableCfCommand(BrowserDistribution::Type dist_type, + Level install_level, + const char* version, + int channel_modifiers); + void RemoveQuickEnableCfCommand(BrowserDistribution::Type dist_type); void set_multi_install(bool is_multi_install) { multi_install_ = is_multi_install; } + installer::AppCommands& commands() { return commands_; } protected: struct ChannelMethodForModifier { @@ -85,6 +95,12 @@ class FakeProductState : public ProductState { bool (ChannelInfo::*method)(bool value); }; + static FilePath GetSetupExePath( + BrowserDistribution::Type dist_type, + Level install_level, + const char* version, + int channel_modifiers); + static const ChannelMethodForModifier kChannelMethods[]; }; @@ -112,12 +128,29 @@ const FakeProductState::ChannelMethodForModifier { CM_FULL, &ChannelInfo::SetFullSuffix } }; +// static +FilePath FakeProductState::GetSetupExePath(BrowserDistribution::Type dist_type, + Level install_level, + const char* version, + int channel_modifiers) { + const bool is_multi_install = (channel_modifiers & CM_MULTI) != 0; + FilePath setup_path = installer::GetChromeInstallPath( + install_level == SYSTEM_LEVEL, + BrowserDistribution::GetSpecificDistribution(is_multi_install ? + BrowserDistribution::CHROME_BINARIES : dist_type)); + return setup_path + .AppendASCII(version) + .Append(installer::kInstallerDir) + .Append(installer::kSetupExe); +} + void FakeProductState::Clear() { channel_.set_value(std::wstring()); version_.reset(); old_version_.reset(); rename_cmd_.clear(); uninstall_command_ = CommandLine(CommandLine::NO_PROGRAM); + commands_.Clear(); msi_ = false; multi_install_ = false; } @@ -146,15 +179,8 @@ void FakeProductState::SetUninstallCommand(BrowserDistribution::Type dist_type, DCHECK(version); const bool is_multi_install = (channel_modifiers & CM_MULTI) != 0; - FilePath setup_path = installer::GetChromeInstallPath( - install_level == SYSTEM_LEVEL, - BrowserDistribution::GetSpecificDistribution(is_multi_install ? - BrowserDistribution::CHROME_BINARIES : dist_type)); - setup_path = setup_path - .AppendASCII(version) - .Append(installer::kInstallerDir) - .Append(installer::kSetupExe); - uninstall_command_ = CommandLine(setup_path); + uninstall_command_ = CommandLine(GetSetupExePath(dist_type, install_level, + version, channel_modifiers)); uninstall_command_.AppendSwitch(installer::switches::kUninstall); if (install_level == SYSTEM_LEVEL) uninstall_command_.AppendSwitch(installer::switches::kSystemLevel); @@ -167,6 +193,12 @@ void FakeProductState::SetUninstallCommand(BrowserDistribution::Type dist_type, uninstall_command_.AppendSwitch( installer::switches::kChromeFrameReadyMode); } + } else if (dist_type == BrowserDistribution::CHROME_FRAME) { + uninstall_command_.AppendSwitch(installer::switches::kChromeFrame); + if ((channel_modifiers & CM_READY_MODE) != 0) { + uninstall_command_.AppendSwitch( + installer::switches::kChromeFrameReadyMode); + } } } else if (dist_type == BrowserDistribution::CHROME_FRAME) { uninstall_command_.AppendSwitch(installer::switches::kChromeFrame); @@ -175,22 +207,31 @@ void FakeProductState::SetUninstallCommand(BrowserDistribution::Type dist_type, uninstall_command_.AppendSwitch(installer::switches::kMsi); } -// Populates |chrome_state| with the state of a valid Chrome browser -// installation. |channel_modifiers|, a field of bits defined by enum -// ChannelModifier, dictate properties of the installation (multi-install, -// ready-mode, etc). -void MakeChromeState(Level install_level, - Channel channel, - int channel_modifiers, - Vehicle vehicle, - FakeProductState* chrome_state) { - chrome_state->Clear(); - chrome_state->SetChannel(kChromeChannels[channel], channel_modifiers); - chrome_state->SetVersion(chrome::kChromeVersion); - chrome_state->SetUninstallCommand(BrowserDistribution::CHROME_BROWSER, - install_level, chrome::kChromeVersion, - channel_modifiers, vehicle); - chrome_state->set_multi_install((channel_modifiers & CM_MULTI) != 0); +// Adds the "quick-enable-cf" Google Update product command. +void FakeProductState::AddQuickEnableCfCommand( + BrowserDistribution::Type dist_type, + Level install_level, + const char* version, + int channel_modifiers) { + DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BINARIES); + DCHECK_NE(channel_modifiers & CM_MULTI, 0); + + CommandLine cmd_line(GetSetupExePath(dist_type, install_level, version, + channel_modifiers)); + cmd_line.AppendSwitch(installer::switches::kMultiInstall); + if (install_level == SYSTEM_LEVEL) + cmd_line.AppendSwitch(installer::switches::kSystemLevel); + cmd_line.AppendSwitch(installer::switches::kChromeFrameQuickEnable); + commands_.Set(installer::kCmdQuickEnableCf, + AppCommand(cmd_line.command_line_string(), true, true)); +} + +// Removes the "quick-enable-cf" Google Update product command. +void FakeProductState::RemoveQuickEnableCfCommand( + BrowserDistribution::Type dist_type) { + DCHECK_EQ(dist_type, BrowserDistribution::CHROME_BINARIES); + + commands_.Remove(installer::kCmdQuickEnableCf); } } // namespace @@ -198,8 +239,22 @@ void MakeChromeState(Level install_level, // Fixture for testing the InstallationValidator. Errors logged by the // validator are sent to an optional mock recipient (see // set_validation_error_recipient) upon which expectations can be placed. -class InstallationValidatorTest : public testing::Test { +class InstallationValidatorTest + : public testing::TestWithParam<InstallationValidator::InstallationType> { + public: + + // These shouldn't need to be public, but there seems to be some interaction + // with parameterized tests that requires it. + static void SetUpTestCase(); + static void TearDownTestCase(); + + // Returns the multi channel modifiers for a given installation type. + static int GetChannelModifiers(InstallationValidator::InstallationType type); + protected: + typedef std::map<InstallationValidator::InstallationType, int> + InstallationTypeToModifiers; + class ValidationErrorRecipient { public: virtual ~ValidationErrorRecipient() { } @@ -214,8 +269,7 @@ class InstallationValidatorTest : public testing::Test { const char* message)); }; - static void SetUpTestCase(); - static void TearDownTestCase(); + protected: static bool HandleLogMessage(int severity, const char* file, int line, @@ -223,11 +277,24 @@ class InstallationValidatorTest : public testing::Test { const std::string& str); static void set_validation_error_recipient( ValidationErrorRecipient* recipient); - + static void MakeProductState( + BrowserDistribution::Type prod_type, + InstallationValidator::InstallationType inst_type, + Level install_level, + Channel channel, + Vehicle vehicle, + FakeProductState* state); + static void MakeMachineState( + InstallationValidator::InstallationType inst_type, + Level install_level, + Channel channel, + Vehicle vehicle, + FakeInstallationState* state); virtual void TearDown(); static logging::LogMessageHandlerFunction old_log_message_handler_; static ValidationErrorRecipient* validation_error_recipient_; + static InstallationTypeToModifiers* type_to_modifiers_; }; // static @@ -239,15 +306,47 @@ InstallationValidatorTest::ValidationErrorRecipient* InstallationValidatorTest::validation_error_recipient_ = NULL; // static +InstallationValidatorTest::InstallationTypeToModifiers* + InstallationValidatorTest::type_to_modifiers_ = NULL; + +// static +int InstallationValidatorTest::GetChannelModifiers( + InstallationValidator::InstallationType type) { + DCHECK(type_to_modifiers_); + DCHECK(type_to_modifiers_->find(type) != type_to_modifiers_->end()); + + return (*type_to_modifiers_)[type]; +} + +// static void InstallationValidatorTest::SetUpTestCase() { + DCHECK(type_to_modifiers_ == NULL); old_log_message_handler_ = logging::GetLogMessageHandler(); logging::SetLogMessageHandler(&HandleLogMessage); + + type_to_modifiers_ = new InstallationTypeToModifiers(); + InstallationTypeToModifiers& ttm = *type_to_modifiers_; + ttm[InstallationValidator::NO_PRODUCTS] = 0; + ttm[InstallationValidator::CHROME_SINGLE] = 0; + ttm[InstallationValidator::CHROME_MULTI] = CM_MULTI | CM_CHROME; + ttm[InstallationValidator::CHROME_FRAME_SINGLE] = 0; + ttm[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE] = 0; + ttm[InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI] = + CM_MULTI | CM_CHROME; + ttm[InstallationValidator::CHROME_FRAME_MULTI] = CM_MULTI | CM_CHROME_FRAME; + ttm[InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI] = + CM_MULTI | CM_CHROME_FRAME | CM_CHROME; + ttm[InstallationValidator::CHROME_FRAME_READY_MODE_CHROME_MULTI] = + CM_MULTI | CM_CHROME_FRAME | CM_CHROME | CM_READY_MODE; } // static void InstallationValidatorTest::TearDownTestCase() { logging::SetLogMessageHandler(old_log_message_handler_); old_log_message_handler_ = NULL; + + delete type_to_modifiers_; + type_to_modifiers_ = NULL; } // static @@ -257,13 +356,18 @@ bool InstallationValidatorTest::HandleLogMessage(int severity, size_t message_start, const std::string& str) { // All validation failures result in LOG(ERROR) - if (severity == logging::LOG_ERROR) { + if (severity == logging::LOG_ERROR && !str.empty()) { + // Remove the trailing newline, if present. + std::streamsize message_length = str.size() - message_start; + if (*str.rbegin() == '\n') + --message_length; if (validation_error_recipient_ != NULL) { validation_error_recipient_->ReceiveValidationError( - file, line, str.c_str() + message_start); + file, line, str.substr(message_start, message_length).c_str()); } else { // Fail the test if an error wasn't handled. - ADD_FAILURE_AT(file, line) << (str.c_str() + message_start); + ADD_FAILURE_AT(file, line) + << base::StringPiece(str.c_str() + message_start, message_length); } return true; } @@ -280,36 +384,125 @@ void InstallationValidatorTest::set_validation_error_recipient( validation_error_recipient_ = recipient; } -void InstallationValidatorTest::TearDown() { - validation_error_recipient_ = NULL; +// static +// Populates |state| with the state of a valid installation of product +// |prod_type|. |inst_type| dictates properties of the installation +// (multi-install, ready-mode, etc). +void InstallationValidatorTest::MakeProductState( + BrowserDistribution::Type prod_type, + InstallationValidator::InstallationType inst_type, + Level install_level, + Channel channel, + Vehicle vehicle, + FakeProductState* state) { + DCHECK(state); + + const bool is_multi_install = + prod_type == BrowserDistribution::CHROME_BINARIES || + (prod_type == BrowserDistribution::CHROME_BROWSER && + (inst_type & InstallationValidator::ProductBits::CHROME_MULTI) != 0) || + (prod_type == BrowserDistribution::CHROME_FRAME && + (inst_type & + (InstallationValidator::ProductBits::CHROME_FRAME_MULTI | + InstallationValidator::ProductBits::CHROME_FRAME_READY_MODE)) != 0); + + const wchar_t* const* channels = &kChromeChannels[0]; + if (prod_type == BrowserDistribution::CHROME_FRAME && !is_multi_install) + channels = &kChromeFrameChannels[0]; // SxS GCF has its own channel names. + const int channel_modifiers = + is_multi_install ? GetChannelModifiers(inst_type) : 0; + + state->Clear(); + state->SetChannel(channels[channel], channel_modifiers); + state->SetVersion(chrome::kChromeVersion); + state->SetUninstallCommand(prod_type, install_level, chrome::kChromeVersion, + channel_modifiers, vehicle); + state->set_multi_install(is_multi_install); + if (prod_type == BrowserDistribution::CHROME_BINARIES && + (inst_type == InstallationValidator::CHROME_MULTI || + inst_type == + InstallationValidator::CHROME_FRAME_READY_MODE_CHROME_MULTI)) { + state->AddQuickEnableCfCommand(prod_type, install_level, + chrome::kChromeVersion, channel_modifiers); + } } -// Test that NO_PRODUCTS is returned. -TEST_F(InstallationValidatorTest, NoProducts) { - InstallationState empty_state; - InstallationValidator::InstallationType type = - static_cast<InstallationValidator::InstallationType>(-1); - StrictMock<MockValidationErrorRecipient> recipient; - set_validation_error_recipient(&recipient); +// static +// Populates |state| with the state of a valid installation of |inst_type|. +void InstallationValidatorTest::MakeMachineState( + InstallationValidator::InstallationType inst_type, + Level install_level, + Channel channel, + Vehicle vehicle, + FakeInstallationState* state) { + DCHECK(state); + + static const int kChromeMask = + (InstallationValidator::ProductBits::CHROME_SINGLE | + InstallationValidator::ProductBits::CHROME_MULTI); + static const int kChromeFrameMask = + (InstallationValidator::ProductBits::CHROME_FRAME_SINGLE | + InstallationValidator::ProductBits::CHROME_FRAME_MULTI | + InstallationValidator::ProductBits::CHROME_FRAME_READY_MODE); + static const int kBinariesMask = + (InstallationValidator::ProductBits::CHROME_MULTI | + InstallationValidator::ProductBits::CHROME_FRAME_MULTI | + InstallationValidator::ProductBits::CHROME_FRAME_READY_MODE); + + FakeProductState prod_state; + + if ((inst_type & kChromeMask) != 0) { + MakeProductState(BrowserDistribution::CHROME_BROWSER, inst_type, + install_level, channel, vehicle, &prod_state); + state->SetProductState(BrowserDistribution::CHROME_BROWSER, install_level, + prod_state); + } - EXPECT_TRUE(InstallationValidator::ValidateInstallationTypeForState( - empty_state, true, &type)); - EXPECT_EQ(InstallationValidator::NO_PRODUCTS, type); + if ((inst_type & kChromeFrameMask) != 0) { + MakeProductState(BrowserDistribution::CHROME_FRAME, inst_type, + install_level, channel, vehicle, &prod_state); + state->SetProductState(BrowserDistribution::CHROME_FRAME, install_level, + prod_state); + } + + if ((inst_type & kBinariesMask) != 0) { + MakeProductState(BrowserDistribution::CHROME_BINARIES, inst_type, + install_level, channel, vehicle, &prod_state); + state->SetProductState(BrowserDistribution::CHROME_BINARIES, install_level, + prod_state); + } +} + +void InstallationValidatorTest::TearDown() { + validation_error_recipient_ = NULL; } -// Test valid single Chrome. -TEST_F(InstallationValidatorTest, ChromeVersion) { - FakeProductState chrome_state; +// Builds a proper machine state for a given InstallationType, then validates +// it. +TEST_P(InstallationValidatorTest, TestValidInstallation) { + const InstallationValidator::InstallationType inst_type = GetParam(); FakeInstallationState machine_state; InstallationValidator::InstallationType type; StrictMock<MockValidationErrorRecipient> recipient; set_validation_error_recipient(&recipient); - MakeChromeState(SYSTEM_LEVEL, STABLE_CHANNEL, 0, GOOGLE_UPDATE, - &chrome_state); - machine_state.SetProductState(BrowserDistribution::CHROME_BROWSER, - SYSTEM_LEVEL, chrome_state); + MakeMachineState(inst_type, SYSTEM_LEVEL, STABLE_CHANNEL, GOOGLE_UPDATE, + &machine_state); EXPECT_TRUE(InstallationValidator::ValidateInstallationTypeForState( machine_state, true, &type)); - EXPECT_EQ(InstallationValidator::CHROME_SINGLE, type); + EXPECT_EQ(inst_type, type); } + +// Run the test for all installation types. +INSTANTIATE_TEST_CASE_P( + AllValidInstallations, + InstallationValidatorTest, + Values(InstallationValidator::NO_PRODUCTS, + InstallationValidator::CHROME_SINGLE, + InstallationValidator::CHROME_MULTI, + InstallationValidator::CHROME_FRAME_SINGLE, + InstallationValidator::CHROME_FRAME_SINGLE_CHROME_SINGLE, + InstallationValidator::CHROME_FRAME_SINGLE_CHROME_MULTI, + InstallationValidator::CHROME_FRAME_MULTI, + InstallationValidator::CHROME_FRAME_MULTI_CHROME_MULTI, + InstallationValidator::CHROME_FRAME_READY_MODE_CHROME_MULTI)); diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc index 6c4bbd7..20b4a0c 100644 --- a/chrome/installer/util/util_constants.cc +++ b/chrome/installer/util/util_constants.cc @@ -162,6 +162,7 @@ const wchar_t kChromeLauncherExe[] = L"chrome_launcher.exe"; const wchar_t kChromeNaCl64Dll[] = L"nacl64.dll"; const wchar_t kChromeNewExe[] = L"new_chrome.exe"; const wchar_t kChromeOldExe[] = L"old_chrome.exe"; +const wchar_t kCmdQuickEnableCf[] = L"quick-enable-cf"; const wchar_t kGoogleChromeInstallSubDir1[] = L"Google"; const wchar_t kGoogleChromeInstallSubDir2[] = L"Chrome"; const wchar_t kInstallBinaryDir[] = L"Application"; diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h index a44275e..5c83f30 100644 --- a/chrome/installer/util/util_constants.h +++ b/chrome/installer/util/util_constants.h @@ -135,6 +135,7 @@ extern const wchar_t kChromeLauncherExe[]; extern const wchar_t kChromeNaCl64Dll[]; extern const wchar_t kChromeOldExe[]; extern const wchar_t kChromeNewExe[]; +extern const wchar_t kCmdQuickEnableCf[]; extern const wchar_t kGoogleChromeInstallSubDir1[]; extern const wchar_t kGoogleChromeInstallSubDir2[]; extern const wchar_t kInstallBinaryDir[]; |