summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorgrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-03 20:05:51 +0000
committergrt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-03 20:05:51 +0000
commit995708fd774716311b6feeaa08d5855c302ecf18 (patch)
tree54aa413dab26c5e4df97f6026d3f99d3f39e5400 /chrome
parentf130ef429844fc2f99089fe3ffc09af0b187577b (diff)
downloadchromium_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')
-rw-r--r--chrome/chrome_installer_util.gypi4
-rw-r--r--chrome/installer/setup/chrome_frame_quick_enable.cc12
-rw-r--r--chrome/installer/setup/install_worker.cc138
-rw-r--r--chrome/installer/setup/install_worker.h13
-rw-r--r--chrome/installer/setup/install_worker_unittest.cc382
-rw-r--r--chrome/installer/setup/setup_main.cc3
-rw-r--r--chrome/installer/setup/uninstall.cc48
-rw-r--r--chrome/installer/util/app_command.cc88
-rw-r--r--chrome/installer/util/app_command.h66
-rw-r--r--chrome/installer/util/app_commands.cc91
-rw-r--r--chrome/installer/util/app_commands.h75
-rw-r--r--chrome/installer/util/google_update_constants.cc23
-rw-r--r--chrome/installer/util/google_update_constants.h23
-rw-r--r--chrome/installer/util/install_util.cc4
-rw-r--r--chrome/installer/util/installation_state.cc17
-rw-r--r--chrome/installer/util/installation_state.h14
-rw-r--r--chrome/installer/util/installation_validator.cc145
-rw-r--r--chrome/installer/util/installation_validator.h29
-rw-r--r--chrome/installer/util/installation_validator_unittest.cc297
-rw-r--r--chrome/installer/util/util_constants.cc1
-rw-r--r--chrome/installer/util/util_constants.h1
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[];