summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-04 19:27:59 +0000
committerrobertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-01-04 19:27:59 +0000
commitba4f2a1ec1d2318847b6ee65ae1e8ee52ed234da (patch)
treefaff2a33f5d263d9c4c51e1c705b136645176357
parentd8858e37c0631e16185794d180269dce5d7e8b70 (diff)
downloadchromium_src-ba4f2a1ec1d2318847b6ee65ae1e8ee52ed234da.zip
chromium_src-ba4f2a1ec1d2318847b6ee65ae1e8ee52ed234da.tar.gz
chromium_src-ba4f2a1ec1d2318847b6ee65ae1e8ee52ed234da.tar.bz2
Refactor the install flow in the Chrome installer to use a single WorkItemList for all operations that mutate the system state. This is a prerequisite for meaningful unittests and significantly simplifies the install flow.
BUG=61609 TEST=None (yet) Review URL: http://codereview.chromium.org/6078007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70426 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome/chrome_installer_util.gypi4
-rw-r--r--chrome/installer/setup/install.cc227
-rw-r--r--chrome/installer/setup/install.h26
-rw-r--r--chrome/installer/setup/uninstall.cc14
-rw-r--r--chrome/installer/util/conditional_work_item_list.cc41
-rw-r--r--chrome/installer/util/conditional_work_item_list.h61
-rw-r--r--chrome/installer/util/delete_reg_key_work_item.cc44
-rw-r--r--chrome/installer/util/delete_reg_key_work_item.h44
-rw-r--r--chrome/installer/util/self_reg_work_item.cc9
-rw-r--r--chrome/installer/util/self_reg_work_item.h5
-rw-r--r--chrome/installer/util/set_reg_value_work_item.cc160
-rw-r--r--chrome/installer/util/work_item.cc13
-rw-r--r--chrome/installer/util/work_item.h39
-rw-r--r--chrome/installer/util/work_item_list.cc183
-rw-r--r--chrome/installer/util/work_item_list.h72
-rw-r--r--chrome/installer/util/work_item_list_unittest.cc125
16 files changed, 754 insertions, 313 deletions
diff --git a/chrome/chrome_installer_util.gypi b/chrome/chrome_installer_util.gypi
index 3e8cedd..b581c41 100644
--- a/chrome/chrome_installer_util.gypi
+++ b/chrome/chrome_installer_util.gypi
@@ -17,12 +17,16 @@
'installer/util/channel_info.h',
'installer/util/chrome_frame_distribution.cc',
'installer/util/chrome_frame_distribution.h',
+ 'installer/util/conditional_work_item_list.cc',
+ 'installer/util/conditional_work_item_list.h',
'installer/util/copy_tree_work_item.cc',
'installer/util/copy_tree_work_item.h',
'installer/util/create_dir_work_item.cc',
'installer/util/create_dir_work_item.h',
'installer/util/create_reg_key_work_item.cc',
'installer/util/create_reg_key_work_item.h',
+ 'installer/util/delete_reg_key_work_item.cc',
+ 'installer/util/delete_reg_key_work_item.h',
'installer/util/delete_reg_value_work_item.cc',
'installer/util/delete_reg_value_work_item.h',
'installer/util/delete_tree_work_item.cc',
diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc
index 1f99df4..0403257e 100644
--- a/chrome/installer/setup/install.cc
+++ b/chrome/installer/setup/install.cc
@@ -21,6 +21,7 @@
#include "chrome/installer/util/browser_distribution.h"
#include "chrome/installer/util/channel_info.h"
#include "chrome/installer/util/chrome_frame_distribution.h"
+#include "chrome/installer/util/conditional_work_item_list.h"
#include "chrome/installer/util/create_reg_key_work_item.h"
#include "chrome/installer/util/delete_after_reboot_helper.h"
#include "chrome/installer/util/google_update_constants.h"
@@ -223,7 +224,8 @@ void AddUninstallShortcutWorkItems(const FilePath& setup_path,
// If so, try and remove any existing uninstallation shortcuts, as we want the
// uninstall to be managed entirely by the MSI machinery (accessible via the
// Add/Remove programs dialog).
-void DeleteUninstallShortcutsForMSI(const Product& product) {
+void AddDeleteUninstallShortcutsForMSIWorkItems(const Product& product,
+ WorkItemList* work_item_list) {
DCHECK(product.IsMsi()) << "This must only be called for MSI installations!";
// First attempt to delete the old installation's ARP dialog entry.
@@ -231,7 +233,10 @@ void DeleteUninstallShortcutsForMSI(const Product& product) {
HKEY_CURRENT_USER;
base::win::RegKey root_key(reg_root, L"", KEY_ALL_ACCESS);
std::wstring uninstall_reg(product.distribution()->GetUninstallRegPath());
- InstallUtil::DeleteRegistryKey(root_key, uninstall_reg);
+
+ WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem(
+ reg_root, uninstall_reg);
+ delete_reg_key->set_ignore_failure(true);
// Then attempt to delete the old installation's start menu shortcut.
FilePath uninstall_link;
@@ -250,8 +255,11 @@ void DeleteUninstallShortcutsForMSI(const Product& product) {
product.distribution()->GetUninstallLinkName() + L".lnk");
VLOG(1) << "Deleting old uninstall shortcut (if present): "
<< uninstall_link.value();
- if (!file_util::Delete(uninstall_link, true))
- VLOG(1) << "Failed to delete old uninstall shortcut.";
+ WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem(
+ uninstall_link);
+ delete_link->set_ignore_failure(true);
+ delete_link->set_log_message(
+ "Failed to delete old uninstall shortcut.");
}
}
@@ -402,11 +410,12 @@ bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path,
return ret;
}
-// Local helper to call RegisterComDllList for all DLLs in a set of products
-// managed by a given package.
-bool RegisterComDlls(const Package& package,
- const Version* old_version,
- const Version& new_version) {
+// Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of
+// products managed by a given package.
+void AddRegisterComDllWorkItemsForPackage(const Package& package,
+ const Version* old_version,
+ const Version& new_version,
+ WorkItemList* work_item_list) {
// First collect the list of DLLs to be registered from each product.
const Products& products = package.products();
Products::const_iterator product_iter(products.begin());
@@ -414,8 +423,8 @@ bool RegisterComDlls(const Package& package,
for (; product_iter != products.end(); ++product_iter) {
BrowserDistribution* dist = product_iter->get()->distribution();
std::vector<FilePath> dist_dll_list(dist->GetComDllList());
- std::copy(dist_dll_list.begin(), dist_dll_list.end(),
- std::back_inserter(com_dll_list));
+ com_dll_list.insert(com_dll_list.end(), dist_dll_list.begin(),
+ dist_dll_list.end());
}
// Then, if we got some, attempt to unregister the DLLs from the old
@@ -426,29 +435,28 @@ bool RegisterComDlls(const Package& package,
// TODO(robertshield): If we ever remove a DLL from a product, this will
// not unregister it on update. We should build the unregistration list from
// saved state instead of assuming it is the same as the registration list.
- bool success = true;
if (!com_dll_list.empty()) {
if (old_version) {
FilePath old_dll_path(
package.path().Append(UTF8ToWide(old_version->GetString())));
- installer::RegisterComDllList(old_dll_path,
- com_dll_list,
- package.system_level(),
- false, // Unregister
- false); // Ignore failures
+ installer::AddRegisterComDllWorkItems(old_dll_path,
+ com_dll_list,
+ package.system_level(),
+ false, // Unregister
+ true, // May fail
+ work_item_list);
}
FilePath dll_path(
package.path().Append(UTF8ToWide(new_version.GetString())));
- success = installer::RegisterComDllList(dll_path,
- com_dll_list,
- package.system_level(),
- true, // Register
- true); // Rollback on failure.
+ installer::AddRegisterComDllWorkItems(dll_path,
+ com_dll_list,
+ package.system_level(),
+ true, // Register
+ false, // Must succeed.
+ work_item_list);
}
-
- return success;
}
// After a successful copying of all the files, this function is called to
@@ -460,26 +468,28 @@ bool RegisterComDlls(const Package& package,
// it if not.
// If these operations are successful, the function returns true, otherwise
// false.
-bool DoPostInstallTasks(bool multi_install,
- const FilePath& setup_path,
- const FilePath& new_chrome_exe,
- const Version* current_version,
- const Version& new_version,
- const Package& package) {
+bool AppendPostInstallTasks(bool multi_install,
+ const FilePath& setup_path,
+ const FilePath& new_chrome_exe,
+ const Version* current_version,
+ const Version& new_version,
+ const Package& package,
+ WorkItemList* post_install_task_list) {
+ DCHECK(post_install_task_list);
HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE :
HKEY_CURRENT_USER;
const Products& products = package.products();
- if (file_util::PathExists(new_chrome_exe)) {
- // Looks like this was in use update. So make sure we update the 'opv' key
- // with the current version that is active and 'cmd' key with the rename
- // command to run.
- if (!current_version) {
- LOG(ERROR) << "New chrome.exe exists but current version is NULL!";
- return false;
- }
- scoped_ptr<WorkItemList> inuse_list(WorkItem::CreateWorkItemList());
+ // Append work items that will only be executed if this was an update.
+ // We update the 'opv' key with the current version that is active and 'cmd'
+ // key with the rename command to run.
+ {
+ scoped_ptr<WorkItemList> in_use_update_work_items(
+ WorkItem::CreateConditionalWorkItemList(
+ new ConditionRunIfFileExists(new_chrome_exe)));
+ in_use_update_work_items->set_log_message("InUseUpdateWorkItemList");
+
FilePath installer_path(package.GetInstallerDirectory(new_version)
.Append(setup_path.BaseName()));
@@ -498,75 +508,83 @@ bool DoPostInstallTasks(bool multi_install,
for (size_t i = 0; i < products.size(); ++i) {
BrowserDistribution* dist = products[i]->distribution();
version_key = dist->GetVersionKey();
- inuse_list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegOldVersionField,
- UTF8ToWide(current_version->GetString()), true);
+
+ if (current_version != NULL) {
+ in_use_update_work_items->AddSetRegValueWorkItem(root, version_key,
+ google_update::kRegOldVersionField,
+ UTF8ToWide(current_version->GetString()), true);
+ }
// Adding this registry entry for all products is overkill.
// However, as it stands, we don't have a way to know which distribution
// will check the key and run the command, so we add it for all.
// After the first run, the subsequent runs should just be noops.
// (see Upgrade::SwapNewChromeExeIfPresent).
- inuse_list->AddSetRegValueWorkItem(root, version_key,
- google_update::kRegRenameCmdField,
- rename.command_line_string(), true);
+ in_use_update_work_items->AddSetRegValueWorkItem(
+ root,
+ version_key,
+ google_update::kRegRenameCmdField,
+ rename.command_line_string(),
+ true);
}
if (multi_install) {
PackageProperties* props = package.properties();
- if (props->ReceivesUpdates()) {
- inuse_list->AddSetRegValueWorkItem(root, props->GetVersionKey(),
+ if (props->ReceivesUpdates() && current_version != NULL) {
+ in_use_update_work_items->AddSetRegValueWorkItem(
+ root,
+ props->GetVersionKey(),
google_update::kRegOldVersionField,
- UTF8ToWide(current_version->GetString()), true);
- // TODO(tommi): We should move the rename command here. We also need to
+ UTF8ToWide(current_version->GetString()),
+ true);
+ // TODO(tommi): We should move the rename command here. We also need to
// update Upgrade::SwapNewChromeExeIfPresent.
}
}
- if (!inuse_list->Do()) {
- LOG(ERROR) << "Couldn't write opv/cmd values to registry.";
- inuse_list->Rollback();
- return false;
- }
- } else {
+ post_install_task_list->AddWorkItem(in_use_update_work_items.release());
+ }
+
+
+ // Append work items that will be executed if this was NOT an in-use update.
+ {
+ scoped_ptr<WorkItemList> regular_update_work_items(
+ WorkItem::CreateConditionalWorkItemList(
+ new Not(new ConditionRunIfFileExists(new_chrome_exe))));
+ regular_update_work_items->set_log_message(
+ "RegularUpdateWorkItemList");
+
// Since this was not an in-use-update, delete 'opv' and 'cmd' keys.
- scoped_ptr<WorkItemList> inuse_list(WorkItem::CreateWorkItemList());
for (size_t i = 0; i < products.size(); ++i) {
BrowserDistribution* dist = products[i]->distribution();
std::wstring version_key(dist->GetVersionKey());
- inuse_list->AddDeleteRegValueWorkItem(root, version_key,
+ regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
google_update::kRegOldVersionField,
true);
- inuse_list->AddDeleteRegValueWorkItem(root, version_key,
+ regular_update_work_items->AddDeleteRegValueWorkItem(root, version_key,
google_update::kRegRenameCmdField,
true);
}
- if (!inuse_list->Do()) {
- LOG(ERROR) << "Couldn't delete opv/cmd values from registry.";
- inuse_list->Rollback();
- return false;
- }
+ post_install_task_list->AddWorkItem(regular_update_work_items.release());
}
- if (!RegisterComDlls(package, current_version, new_version)) {
- LOG(ERROR) << "RegisterComDlls failed. Aborting.";
- return false;
- }
+ AddRegisterComDllWorkItemsForPackage(package, current_version, new_version,
+ post_install_task_list);
for (size_t i = 0; i < products.size(); ++i) {
const Product* product = products[i];
// If we're told that we're an MSI install, make sure to set the marker
// in the client state key so that future updates do the right thing.
if (product->IsMsi()) {
- if (!product->SetMsiMarker(true))
- return false;
+ AddSetMsiMarkerWorkItem(*product, true, post_install_task_list);
// We want MSI installs to take over the Add/Remove Programs shortcut.
// Make a best-effort attempt to delete any shortcuts left over from
// previous non-MSI installations for the same type of install (system or
// per user).
- DeleteUninstallShortcutsForMSI(*product);
+ AddDeleteUninstallShortcutsForMSIWorkItems(*product,
+ post_install_task_list);
}
}
@@ -781,10 +799,16 @@ installer::InstallStatus InstallNewVersion(
// each product.
AddProductSpecificWorkItems(true, setup_path, new_version, package,
install_list.get());
-
- if (!install_list->Do() ||
- !DoPostInstallTasks(multi_install, setup_path, new_chrome_exe,
- current_version->get(), new_version, package)) {
+ // Append the tasks that run after the installation.
+ AppendPostInstallTasks(multi_install,
+ setup_path,
+ new_chrome_exe,
+ current_version->get(),
+ new_version,
+ package,
+ install_list.get());
+
+ if (!install_list->Do()) {
installer::InstallStatus result =
file_util::PathExists(new_chrome_exe) && current_version->get() &&
new_version.Equals(**current_version) ?
@@ -892,36 +916,41 @@ installer::InstallStatus InstallOrUpdateProduct(
return result;
}
-bool RegisterComDllList(const FilePath& dll_folder,
- const std::vector<FilePath>& dll_list,
- bool system_level,
- bool do_register,
- bool rollback_on_failure) {
+void AddRegisterComDllWorkItems(const FilePath& dll_folder,
+ const std::vector<FilePath>& dll_list,
+ bool system_level,
+ bool do_register,
+ bool ignore_failures,
+ WorkItemList* work_item_list) {
+ DCHECK(work_item_list);
if (dll_list.empty()) {
VLOG(1) << "No COM DLLs to register";
- return true;
- }
-
- scoped_ptr<WorkItemList> work_item_list;
- if (rollback_on_failure) {
- work_item_list.reset(WorkItem::CreateWorkItemList());
} else {
- work_item_list.reset(WorkItem::CreateNoRollbackWorkItemList());
- }
-
- std::vector<FilePath>::const_iterator dll_iter(dll_list.begin());
- for (; dll_iter != dll_list.end(); ++dll_iter) {
- FilePath dll_path = dll_folder.Append(*dll_iter);
- work_item_list->AddSelfRegWorkItem(dll_path.value(),
- do_register,
- !system_level);
+ std::vector<FilePath>::const_iterator dll_iter(dll_list.begin());
+ for (; dll_iter != dll_list.end(); ++dll_iter) {
+ FilePath dll_path = dll_folder.Append(*dll_iter);
+ WorkItem* work_item = work_item_list->AddSelfRegWorkItem(
+ dll_path.value(), do_register, !system_level);
+ DCHECK(work_item);
+ work_item->set_ignore_failure(ignore_failures);
+ }
}
+}
- bool success = work_item_list->Do();
- if (!success && rollback_on_failure) {
- work_item_list->Rollback();
- }
- return success;
+void AddSetMsiMarkerWorkItem(const Product& product,
+ bool set,
+ WorkItemList* work_item_list) {
+ DCHECK(work_item_list);
+ BrowserDistribution* dist = product.distribution();
+ HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE :
+ HKEY_CURRENT_USER;
+ DWORD msi_value = set ? 1 : 0;
+ WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem(
+ reg_root, dist->GetStateKey(), google_update::kRegMSIField,
+ msi_value, true);
+ DCHECK(set_msi_work_item);
+ set_msi_work_item->set_ignore_failure(true);
+ set_msi_work_item->set_log_message("Could not write MSI marker!");
}
void AddChromeFrameWorkItems(bool install,
diff --git a/chrome/installer/setup/install.h b/chrome/installer/setup/install.h
index b1ba24c..7d21a24 100644
--- a/chrome/installer/setup/install.h
+++ b/chrome/installer/setup/install.h
@@ -47,19 +47,25 @@ installer::InstallStatus InstallOrUpdateProduct(
const installer::MasterPreferences& prefs, const Version& new_version,
const Package& package);
-// Registers or unregisters the COM DLLs whose file names are given in
-// |dll_files| and who reside in the path |dll_folder|.
+// Appends registration or unregistration work items to |work_item_list| for the
+// COM DLLs whose file names are given in |dll_files| and which reside in the
+// path |dll_folder|.
// |system_level| specifies whether to call the system or user level DLL
// registration entry points.
// |do_register| says whether to register or unregister.
-// |rollback_on_failure| says whether we should try to revert the registration
-// or unregistration by calling the opposite entry point on the DLLs if
-// something goes wrong.
-bool RegisterComDllList(const FilePath& dll_folder,
- const std::vector<FilePath>& dll_files,
- bool system_level,
- bool do_register,
- bool rollback_on_failure);
+// |may_fail| states whether this is best effort or not. If |may_fail| is true
+// then |work_item_list| will still succeed if the registration fails and
+// no registration rollback will be performed.
+void AddRegisterComDllWorkItems(const FilePath& dll_folder,
+ const std::vector<FilePath>& dll_files,
+ bool system_level,
+ bool do_register,
+ bool ignore_failures,
+ WorkItemList* work_item_list);
+
+void AddSetMsiMarkerWorkItem(const Product& product,
+ bool set,
+ WorkItemList* work_item_list);
// Called for either installation or uninstallation. This method updates the
// registry according to Chrome Frame specific options for the current
diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
index 1a623d6..0b1aa79 100644
--- a/chrome/installer/setup/uninstall.cc
+++ b/chrome/installer/setup/uninstall.cc
@@ -605,8 +605,18 @@ InstallStatus UninstallProduct(const FilePath& setup_path,
std::vector<FilePath> com_dll_list(browser_dist->GetComDllList());
FilePath dll_folder = product.package().path().Append(
UTF8ToWide(installed_version->GetString()));
- RegisterComDllList(dll_folder, com_dll_list, product.system_level(),
- false, false);
+
+ scoped_ptr<WorkItemList> unreg_work_item_list(
+ WorkItem::CreateWorkItemList());
+
+
+ AddRegisterComDllWorkItems(dll_folder,
+ com_dll_list,
+ product.system_level(),
+ false, // Unregister
+ true, // May fail
+ unreg_work_item_list.get());
+ unreg_work_item_list->Do();
}
}
diff --git a/chrome/installer/util/conditional_work_item_list.cc b/chrome/installer/util/conditional_work_item_list.cc
new file mode 100644
index 0000000..9e8d5bf
--- /dev/null
+++ b/chrome/installer/util/conditional_work_item_list.cc
@@ -0,0 +1,41 @@
+// Copyright (c) 2010 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/conditional_work_item_list.h"
+
+#include "base/file_util.h"
+#include "base/logging.h"
+
+ConditionalWorkItemList::ConditionalWorkItemList(Condition* condition)
+ : condition_(condition) {
+}
+
+ConditionalWorkItemList::~ConditionalWorkItemList() {}
+
+bool ConditionalWorkItemList::Do() {
+ VLOG(1) << "Evaluating " << log_message_ << " condition...";
+ if (condition_.get() && condition_->ShouldRun()) {
+ VLOG(1) << "Beginning conditional work item list";
+ return WorkItemList::Do();
+ }
+ VLOG(1) << "No work to do in condition work item list "
+ << log_message_;
+ return true;
+}
+
+void ConditionalWorkItemList::Rollback() {
+ VLOG(1) << "Rolling back conditional list " << log_message_;
+ WorkItemList::Rollback();
+}
+
+// Pre-defined conditions:
+//------------------------------------------------------------------------------
+bool ConditionRunIfFileExists::ShouldRun() const {
+ return file_util::PathExists(key_path_);
+}
+
+bool Not::ShouldRun() const {
+ return !original_condition_->ShouldRun();
+}
+
diff --git a/chrome/installer/util/conditional_work_item_list.h b/chrome/installer/util/conditional_work_item_list.h
new file mode 100644
index 0000000..f926f76
--- /dev/null
+++ b/chrome/installer/util/conditional_work_item_list.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2010 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_CONDITIONAL_WORK_ITEM_LIST_H_
+#define CHROME_INSTALLER_UTIL_CONDITIONAL_WORK_ITEM_LIST_H_
+#pragma once
+
+#include "chrome/installer/util/work_item_list.h"
+
+#include "base/file_path.h"
+#include "base/scoped_ptr.h"
+
+// A WorkItemList subclass that permits conditionally executing a set of
+// WorkItems.
+class ConditionalWorkItemList : public WorkItemList {
+ public:
+ explicit ConditionalWorkItemList(Condition* condition);
+ virtual ~ConditionalWorkItemList();
+
+ // If condition_->ShouldRun() returns true, then execute the items in this
+ // list and return true iff they all succeed. If condition_->ShouldRun()
+ // returns false, does nothing and returns true.
+ virtual bool Do();
+
+ // Does a rollback of the items (if any) that were run in Do.
+ virtual void Rollback();
+
+ protected:
+ // Pointer to a Condition that is used to determine whether to run this
+ // WorkItemList.
+ scoped_ptr<Condition> condition_;
+};
+
+
+// Pre-defined conditions:
+//------------------------------------------------------------------------------
+class ConditionRunIfFileExists : public WorkItem::Condition {
+ public:
+ explicit ConditionRunIfFileExists(const FilePath& key_path)
+ : key_path_(key_path) {}
+ bool ShouldRun() const;
+
+ private:
+ FilePath key_path_;
+};
+
+// Condition class that inverts the ShouldRun result of another Condition.
+// This class assumes ownership of original_condition.
+class Not : public WorkItem::Condition {
+ public:
+ explicit Not(WorkItem::Condition* original_condition)
+ : original_condition_(original_condition) {}
+ bool ShouldRun() const;
+
+ private:
+ scoped_ptr<WorkItem::Condition> original_condition_;
+};
+
+
+#endif // CHROME_INSTALLER_UTIL_CONDITIONAL_WORK_ITEM_LIST_H_
diff --git a/chrome/installer/util/delete_reg_key_work_item.cc b/chrome/installer/util/delete_reg_key_work_item.cc
new file mode 100644
index 0000000..6c3096d
--- /dev/null
+++ b/chrome/installer/util/delete_reg_key_work_item.cc
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 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 <shlwapi.h>
+
+#include "chrome/installer/util/delete_reg_key_work_item.h"
+
+#include "base/logging.h"
+#include "chrome/installer/util/logging_installer.h"
+
+
+DeleteRegKeyWorkItem::~DeleteRegKeyWorkItem() {
+}
+
+DeleteRegKeyWorkItem::DeleteRegKeyWorkItem(HKEY predefined_root,
+ const std::wstring& path)
+ : predefined_root_(predefined_root),
+ path_(path) {
+}
+
+bool DeleteRegKeyWorkItem::Do() {
+ // TODO(robertshield): Implement rollback for this work item. Consider using
+ // RegSaveKey or RegCopyKey.
+ if (!ignore_failure_) {
+ NOTREACHED();
+ LOG(ERROR) << "Unexpected configuration in DeleteRegKeyWorkItem.";
+ return false;
+ }
+
+ LSTATUS result = SHDeleteKey(predefined_root_, path_.c_str());
+ if (result != ERROR_SUCCESS) {
+ LOG(ERROR) << "Failed to delete key at " << path_ << ", result: " << result;
+ }
+
+ return true;
+}
+
+void DeleteRegKeyWorkItem::Rollback() {
+ if (ignore_failure_)
+ return;
+ NOTREACHED();
+}
+
diff --git a/chrome/installer/util/delete_reg_key_work_item.h b/chrome/installer/util/delete_reg_key_work_item.h
new file mode 100644
index 0000000..3d42bf2
--- /dev/null
+++ b/chrome/installer/util/delete_reg_key_work_item.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2010 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_DELETE_REG_KEY_WORK_ITEM_H_
+#define CHROME_INSTALLER_UTIL_DELETE_REG_KEY_WORK_ITEM_H_
+#pragma once
+
+#include <windows.h>
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "chrome/installer/util/work_item.h"
+
+// A WorkItem subclass that deletes a registry key at the given path.
+class DeleteRegKeyWorkItem : public WorkItem {
+ public:
+ virtual ~DeleteRegKeyWorkItem();
+
+ virtual bool Do();
+
+ virtual void Rollback();
+
+ private:
+ friend class WorkItem;
+
+ DeleteRegKeyWorkItem(HKEY predefined_root, const std::wstring& path);
+
+ // Root key from which we delete the key. The root key can only be
+ // one of the predefined keys on Windows.
+ HKEY predefined_root_;
+
+ // Path of the key to be deleted.
+ std::wstring path_;
+
+ // Path in the registry that the key is backed up to.
+ std::wstring backup_path_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(DeleteRegKeyWorkItem);
+};
+
+#endif // CHROME_INSTALLER_UTIL_DELETE_REG_KEY_WORK_ITEM_H_
diff --git a/chrome/installer/util/self_reg_work_item.cc b/chrome/installer/util/self_reg_work_item.cc
index 831d2cd..6b7c4f3 100644
--- a/chrome/installer/util/self_reg_work_item.cc
+++ b/chrome/installer/util/self_reg_work_item.cc
@@ -66,9 +66,14 @@ bool SelfRegWorkItem::RegisterDll(bool do_register) {
}
bool SelfRegWorkItem::Do() {
- return RegisterDll(do_register_);
+ bool success = RegisterDll(do_register_);
+ if (ignore_failure_)
+ success = true;
+ return success;
}
void SelfRegWorkItem::Rollback() {
- RegisterDll(!do_register_);
+ if (!ignore_failure_) {
+ RegisterDll(!do_register_);
+ }
}
diff --git a/chrome/installer/util/self_reg_work_item.h b/chrome/installer/util/self_reg_work_item.h
index cc12ca9..fa4adde 100644
--- a/chrome/installer/util/self_reg_work_item.h
+++ b/chrome/installer/util/self_reg_work_item.h
@@ -49,6 +49,11 @@ class SelfRegWorkItem : public WorkItem {
// Whether to use alternate export names on the DLL that will perform
// user level registration.
bool user_level_registration_;
+
+ // Specifies whether this work item my fail to complete and yet still
+ // return true from Do(). Use this when making a best effort at unregistering
+ // old Dlls.
+ bool ignore_failure_;
};
#endif // CHROME_INSTALLER_UTIL_SELF_REG_WORK_ITEM_H__
diff --git a/chrome/installer/util/set_reg_value_work_item.cc b/chrome/installer/util/set_reg_value_work_item.cc
index c808b86..4a76040 100644
--- a/chrome/installer/util/set_reg_value_work_item.cc
+++ b/chrome/installer/util/set_reg_value_work_item.cc
@@ -43,114 +43,116 @@ SetRegValueWorkItem::SetRegValueWorkItem(HKEY predefined_root,
}
bool SetRegValueWorkItem::Do() {
+ bool success = true;
+
if (status_ != SET_VALUE) {
// we already did something.
LOG(ERROR) << "multiple calls to Do()";
- return false;
+ success = false;
}
base::win::RegKey key;
- if (!key.Open(predefined_root_, key_path_.c_str(),
- KEY_READ | KEY_SET_VALUE)) {
+ if (success && !key.Open(predefined_root_, key_path_.c_str(),
+ KEY_READ | KEY_SET_VALUE)) {
LOG(ERROR) << "can not open " << key_path_;
status_ = VALUE_UNCHANGED;
- return false;
+ success = false;
}
- bool result = false;
- if (key.ValueExists(value_name_.c_str())) {
- if (overwrite_) {
- bool success = true;
- // Read previous value for rollback and write new value
- if (is_str_type_) {
- std::wstring data;
- if (key.ReadValue(value_name_.c_str(), &data)) {
- previous_value_str_.assign(data);
+ if (success) {
+ if (key.ValueExists(value_name_.c_str())) {
+ if (overwrite_) {
+ // Read previous value for rollback and write new value
+ if (is_str_type_) {
+ std::wstring data;
+ if (key.ReadValue(value_name_.c_str(), &data)) {
+ previous_value_str_.assign(data);
+ }
+ success = key.WriteValue(value_name_.c_str(),
+ value_data_str_.c_str());
+ } else {
+ DWORD data;
+ if (key.ReadValueDW(value_name_.c_str(), &data)) {
+ previous_value_dword_ = data;
+ }
+ success = key.WriteValue(value_name_.c_str(), value_data_dword_);
}
+ if (success) {
+ VLOG(1) << "overwritten value for " << value_name_;
+ status_ = VALUE_OVERWRITTEN;
+ } else {
+ LOG(ERROR) << "failed to overwrite value for " << value_name_;
+ status_ = VALUE_UNCHANGED;
+ }
+ } else {
+ VLOG(1) << value_name_ << " exists. not changed ";
+ status_ = VALUE_UNCHANGED;
+ }
+ } else {
+ if (is_str_type_) {
success = key.WriteValue(value_name_.c_str(), value_data_str_.c_str());
} else {
- DWORD data;
- if (key.ReadValueDW(value_name_.c_str(), &data)) {
- previous_value_dword_ = data;
- }
success = key.WriteValue(value_name_.c_str(), value_data_dword_);
}
if (success) {
- VLOG(1) << "overwritten value for " << value_name_;
- status_ = VALUE_OVERWRITTEN;
- result = true;
+ VLOG(1) << "created value for " << value_name_;
+ status_ = NEW_VALUE_CREATED;
} else {
- LOG(ERROR) << "failed to overwrite value for " << value_name_;
+ LOG(ERROR) << "failed to create value for " << value_name_;
status_ = VALUE_UNCHANGED;
- result = false;
}
- } else {
- VLOG(1) << value_name_ << " exists. not changed ";
- status_ = VALUE_UNCHANGED;
- result = true;
- }
- } else {
- bool success = true;
- if (is_str_type_) {
- success = key.WriteValue(value_name_.c_str(), value_data_str_.c_str());
- } else {
- success = key.WriteValue(value_name_.c_str(), value_data_dword_);
- }
- if (success) {
- VLOG(1) << "created value for " << value_name_;
- status_ = NEW_VALUE_CREATED;
- result = true;
- } else {
- LOG(ERROR) << "failed to create value for " << value_name_;
- status_ = VALUE_UNCHANGED;
- result = false;
}
}
- key.Close();
- return result;
+ LOG_IF(ERROR, !success && !log_message_.empty()) << log_message_;
+
+ if (ignore_failure_) {
+ success = true;
+ }
+
+ return success;
}
void SetRegValueWorkItem::Rollback() {
- if (status_ == SET_VALUE || status_ == VALUE_ROLL_BACK)
- return;
+ if (!ignore_failure_) {
+ if (status_ == SET_VALUE || status_ == VALUE_ROLL_BACK)
+ return;
- if (status_ == VALUE_UNCHANGED) {
- status_ = VALUE_ROLL_BACK;
- VLOG(1) << "rollback: setting unchanged, nothing to do";
- return;
- }
+ if (status_ == VALUE_UNCHANGED) {
+ status_ = VALUE_ROLL_BACK;
+ VLOG(1) << "rollback: setting unchanged, nothing to do";
+ return;
+ }
- base::win::RegKey key;
- if (!key.Open(predefined_root_, key_path_.c_str(),
- KEY_READ | KEY_SET_VALUE)) {
- status_ = VALUE_ROLL_BACK;
- VLOG(1) << "rollback: can not open " << key_path_;
- return;
- }
+ base::win::RegKey key;
+ if (!key.Open(predefined_root_, key_path_.c_str(), KEY_SET_VALUE)) {
+ status_ = VALUE_ROLL_BACK;
+ VLOG(1) << "rollback: can not open " << key_path_;
+ return;
+ }
- std::wstring result_str(L" failed");
- if (status_ == NEW_VALUE_CREATED) {
- if (key.DeleteValue(value_name_.c_str()))
- result_str.assign(L" succeeded");
- VLOG(1) << "rollback: deleting " << value_name_ << result_str;
- } else if (status_ == VALUE_OVERWRITTEN) {
- // try restore the previous value
- bool success = true;
- if (is_str_type_) {
- success = key.WriteValue(value_name_.c_str(),
- previous_value_str_.c_str());
+ std::wstring result_str(L" failed");
+ if (status_ == NEW_VALUE_CREATED) {
+ if (key.DeleteValue(value_name_.c_str()))
+ result_str.assign(L" succeeded");
+ VLOG(1) << "rollback: deleting " << value_name_ << result_str;
+ } else if (status_ == VALUE_OVERWRITTEN) {
+ // try restore the previous value
+ bool success = true;
+ if (is_str_type_) {
+ success = key.WriteValue(value_name_.c_str(),
+ previous_value_str_.c_str());
+ } else {
+ success = key.WriteValue(value_name_.c_str(), previous_value_dword_);
+ }
+ if (success)
+ result_str.assign(L" succeeded");
+ VLOG(1) << "rollback: restoring " << value_name_ << result_str;
} else {
- success = key.WriteValue(value_name_.c_str(), previous_value_dword_);
+ NOTREACHED();
}
- if (success)
- result_str.assign(L" succeeded");
- VLOG(1) << "rollback: restoring " << value_name_ << result_str;
- } else {
- // Not reached.
- }
- status_ = VALUE_ROLL_BACK;
- key.Close();
- return;
+ status_ = VALUE_ROLL_BACK;
+ return;
+ }
}
diff --git a/chrome/installer/util/work_item.cc b/chrome/installer/util/work_item.cc
index 57313f6..d646051 100644
--- a/chrome/installer/util/work_item.cc
+++ b/chrome/installer/util/work_item.cc
@@ -4,17 +4,19 @@
#include "chrome/installer/util/work_item.h"
+#include "chrome/installer/util/conditional_work_item_list.h"
#include "chrome/installer/util/copy_tree_work_item.h"
#include "chrome/installer/util/create_dir_work_item.h"
#include "chrome/installer/util/create_reg_key_work_item.h"
#include "chrome/installer/util/delete_tree_work_item.h"
+#include "chrome/installer/util/delete_reg_key_work_item.h"
#include "chrome/installer/util/delete_reg_value_work_item.h"
#include "chrome/installer/util/move_tree_work_item.h"
#include "chrome/installer/util/self_reg_work_item.h"
#include "chrome/installer/util/set_reg_value_work_item.h"
#include "chrome/installer/util/work_item_list.h"
-WorkItem::WorkItem() {
+WorkItem::WorkItem() : ignore_failure_(false) {
}
WorkItem::~WorkItem() {
@@ -39,6 +41,11 @@ CreateRegKeyWorkItem* WorkItem::CreateCreateRegKeyWorkItem(
return new CreateRegKeyWorkItem(predefined_root, path);
}
+DeleteRegKeyWorkItem* WorkItem::CreateDeleteRegKeyWorkItem(
+ HKEY predefined_root, const std::wstring& path) {
+ return new DeleteRegKeyWorkItem(predefined_root, path);
+}
+
DeleteRegValueWorkItem* WorkItem::CreateDeleteRegValueWorkItem(
HKEY predefined_root,
const std::wstring& key_path,
@@ -93,3 +100,7 @@ WorkItemList* WorkItem::CreateWorkItemList() {
WorkItemList* WorkItem::CreateNoRollbackWorkItemList() {
return new NoRollbackWorkItemList();
}
+
+WorkItemList* WorkItem::CreateConditionalWorkItemList(Condition* condition) {
+ return new ConditionalWorkItemList(condition);
+}
diff --git a/chrome/installer/util/work_item.h b/chrome/installer/util/work_item.h
index b449cfa..a03f5f46 100644
--- a/chrome/installer/util/work_item.h
+++ b/chrome/installer/util/work_item.h
@@ -19,6 +19,7 @@ class CopyTreeWorkItem;
class CreateDirWorkItem;
class CreateRegKeyWorkItem;
class DeleteTreeWorkItem;
+class DeleteRegKeyWorkItem;
class DeleteRegValueWorkItem;
class FilePath;
class MoveTreeWorkItem;
@@ -39,6 +40,14 @@ class WorkItem {
NEW_NAME_IF_IN_USE // Copy to a new path if dest is in use(only files).
};
+ // Abstract base class for the conditions used by ConditionWorkItemList.
+ // TODO(robertshield): Move this out of WorkItem.
+ class Condition {
+ public:
+ virtual ~Condition() {}
+ virtual bool ShouldRun() const = 0;
+ };
+
virtual ~WorkItem();
// Create a CopyTreeWorkItem that recursively copies a file system hierarchy
@@ -62,6 +71,11 @@ class WorkItem {
static CreateRegKeyWorkItem* CreateCreateRegKeyWorkItem(
HKEY predefined_root, const std::wstring& path);
+ // Create a DeleteRegKeyWorkItem that deletes a registry key at the given
+ // path.
+ static DeleteRegKeyWorkItem* CreateDeleteRegKeyWorkItem(
+ HKEY predefined_root, const std::wstring& path);
+
// Create a DeleteRegValueWorkItem that deletes a registry value
static DeleteRegValueWorkItem* CreateDeleteRegValueWorkItem(
HKEY predefined_root,
@@ -114,6 +128,11 @@ class WorkItem {
// not abort execution if an item in the list fails.
static WorkItemList* CreateNoRollbackWorkItemList();
+ // Create a conditional work item list that will execute only if
+ // condition->ShouldRun() returns true. The WorkItemList instance
+ // assumes ownership of condition.
+ static WorkItemList* CreateConditionalWorkItemList(Condition* condition);
+
// Perform the actions of WorkItem. Returns true if success, returns false
// otherwise.
// If the WorkItem is transactional, then Do() is done as a transaction.
@@ -126,12 +145,26 @@ class WorkItem {
// best effort.
virtual void Rollback() = 0;
- // Return true if the WorkItem is transactional, return false if
- // non-transactional.
- virtual bool IsTransactional() { return false; }
+ // If called with true, this WorkItem may return true from its Do() method
+ // even on failure and Rollback will have no effect.
+ void set_ignore_failure(bool ignore_failure) {
+ ignore_failure_ = ignore_failure;
+ }
+
+ // Sets an optional log message that a work item may use to print additional
+ // instance-specific information.
+ void set_log_message(const std::string& log_message) {
+ log_message_ = log_message;
+ }
+
+ // Retrieves the optional log message. The retrieved string may be empty.
+ std::string log_message() { return log_message_; }
protected:
WorkItem();
+
+ bool ignore_failure_;
+ std::string log_message_;
};
#endif // CHROME_INSTALLER_UTIL_WORK_ITEM_H_
diff --git a/chrome/installer/util/work_item_list.cc b/chrome/installer/util/work_item_list.cc
index 37f08bd..706ca10 100644
--- a/chrome/installer/util/work_item_list.cc
+++ b/chrome/installer/util/work_item_list.cc
@@ -7,6 +7,15 @@
#include "base/logging.h"
#include "base/file_path.h"
#include "chrome/installer/util/logging_installer.h"
+#include "chrome/installer/util/copy_tree_work_item.h"
+#include "chrome/installer/util/create_dir_work_item.h"
+#include "chrome/installer/util/create_reg_key_work_item.h"
+#include "chrome/installer/util/delete_tree_work_item.h"
+#include "chrome/installer/util/delete_reg_key_work_item.h"
+#include "chrome/installer/util/delete_reg_value_work_item.h"
+#include "chrome/installer/util/move_tree_work_item.h"
+#include "chrome/installer/util/self_reg_work_item.h"
+#include "chrome/installer/util/set_reg_value_work_item.h"
WorkItemList::~WorkItemList() {
for (WorkItemIterator itr = list_.begin(); itr != list_.end(); ++itr) {
@@ -32,7 +41,7 @@ bool WorkItemList::Do() {
list_.pop_front();
executed_list_.push_front(work_item);
if (!work_item->Do()) {
- LOG(ERROR) << "item execution failed";
+ LOG(ERROR) << "item execution failed " << work_item->log_message();
result = false;
break;
}
@@ -58,108 +67,123 @@ void WorkItemList::Rollback() {
return;
}
-bool WorkItemList::AddWorkItem(WorkItem* work_item) {
- if (status_ != ADD_ITEM)
- return false;
-
+void WorkItemList::AddWorkItem(WorkItem* work_item) {
+ DCHECK(status_ == ADD_ITEM);
list_.push_back(work_item);
- return true;
}
-bool WorkItemList::AddCopyTreeWorkItem(const std::wstring& source_path,
- const std::wstring& dest_path,
- const std::wstring& temp_dir,
- CopyOverWriteOption overwrite_option,
- const std::wstring& alternative_path) {
- WorkItem* item = reinterpret_cast<WorkItem*>(
- WorkItem::CreateCopyTreeWorkItem(source_path, dest_path, temp_dir,
- overwrite_option, alternative_path));
- return AddWorkItem(item);
+WorkItem* WorkItemList::AddCopyTreeWorkItem(
+ const std::wstring& source_path,
+ const std::wstring& dest_path,
+ const std::wstring& temp_dir,
+ CopyOverWriteOption overwrite_option,
+ const std::wstring& alternative_path) {
+ WorkItem* item = WorkItem::CreateCopyTreeWorkItem(source_path,
+ dest_path,
+ temp_dir,
+ overwrite_option,
+ alternative_path);
+ AddWorkItem(item);
+ return item;
}
-bool WorkItemList::AddCreateDirWorkItem(const FilePath& path) {
- WorkItem* item = reinterpret_cast<WorkItem*>(
- WorkItem::CreateCreateDirWorkItem(path));
- return AddWorkItem(item);
+WorkItem* WorkItemList::AddCreateDirWorkItem(const FilePath& path) {
+ WorkItem* item = WorkItem::CreateCreateDirWorkItem(path);
+ AddWorkItem(item);
+ return item;
}
-bool WorkItemList::AddCreateRegKeyWorkItem(HKEY predefined_root,
+WorkItem* WorkItemList::AddCreateRegKeyWorkItem(HKEY predefined_root,
const std::wstring& path) {
- WorkItem* item = reinterpret_cast<WorkItem*>(
- WorkItem::CreateCreateRegKeyWorkItem(predefined_root, path));
- return AddWorkItem(item);
+ WorkItem* item = WorkItem::CreateCreateRegKeyWorkItem(predefined_root, path);
+ AddWorkItem(item);
+ return item;
+}
+
+WorkItem* WorkItemList::AddDeleteRegKeyWorkItem(HKEY predefined_root,
+ const std::wstring& path) {
+ WorkItem* item = WorkItem::CreateDeleteRegKeyWorkItem(predefined_root, path);
+ AddWorkItem(item);
+ return item;
}
-bool WorkItemList::AddDeleteRegValueWorkItem(HKEY predefined_root,
- const std::wstring& key_path,
- const std::wstring& value_name,
- bool is_str_type) {
- WorkItem* item = reinterpret_cast<WorkItem*>(
- WorkItem::CreateDeleteRegValueWorkItem(predefined_root, key_path,
- value_name, is_str_type));
- return AddWorkItem(item);
+WorkItem* WorkItemList::AddDeleteRegValueWorkItem(
+ HKEY predefined_root,
+ const std::wstring& key_path,
+ const std::wstring& value_name,
+ bool is_str_type) {
+ WorkItem* item = WorkItem::CreateDeleteRegValueWorkItem(predefined_root,
+ key_path,
+ value_name,
+ is_str_type);
+ AddWorkItem(item);
+ return item;
}
-bool WorkItemList::AddDeleteTreeWorkItem(
+WorkItem* WorkItemList::AddDeleteTreeWorkItem(
const FilePath& root_path,
const std::vector<FilePath>& key_paths) {
- WorkItem* item = reinterpret_cast<WorkItem*>(
- WorkItem::CreateDeleteTreeWorkItem(root_path, key_paths));
- return AddWorkItem(item);
+ WorkItem* item = WorkItem::CreateDeleteTreeWorkItem(root_path, key_paths);
+ AddWorkItem(item);
+ return item;
}
-bool WorkItemList::AddDeleteTreeWorkItem(const FilePath& root_path) {
+WorkItem* WorkItemList::AddDeleteTreeWorkItem(const FilePath& root_path) {
std::vector<FilePath> no_key_files;
return AddDeleteTreeWorkItem(root_path, no_key_files);
}
-bool WorkItemList::AddMoveTreeWorkItem(const std::wstring& source_path,
- const std::wstring& dest_path,
- const std::wstring& temp_dir) {
- WorkItem* item = reinterpret_cast<WorkItem*>(
- WorkItem::CreateMoveTreeWorkItem(source_path, dest_path, temp_dir));
- return AddWorkItem(item);
-}
-
-bool WorkItemList::AddSetRegValueWorkItem(HKEY predefined_root,
- const std::wstring& key_path,
- const std::wstring& value_name,
- const std::wstring& value_data,
- bool overwrite) {
- WorkItem* item = reinterpret_cast<WorkItem*>(
- WorkItem::CreateSetRegValueWorkItem(predefined_root, key_path, value_name,
- value_data, overwrite));
- return AddWorkItem(item);
-}
-
-bool WorkItemList::AddSetRegValueWorkItem(HKEY predefined_root,
- const std::wstring& key_path,
- const std::wstring& value_name,
- DWORD value_data,
- bool overwrite) {
- WorkItem* item = reinterpret_cast<WorkItem*>(
- WorkItem::CreateSetRegValueWorkItem(predefined_root, key_path, value_name,
- value_data, overwrite));
- return AddWorkItem(item);
-}
-
-bool WorkItemList::AddSelfRegWorkItem(const std::wstring& dll_path,
- bool do_register,
- bool user_level_registration) {
- WorkItem* item = reinterpret_cast<WorkItem*>(
- WorkItem::CreateSelfRegWorkItem(dll_path, do_register,
- user_level_registration));
- return AddWorkItem(item);
+WorkItem* WorkItemList::AddMoveTreeWorkItem(const std::wstring& source_path,
+ const std::wstring& dest_path,
+ const std::wstring& temp_dir) {
+ WorkItem* item = WorkItem::CreateMoveTreeWorkItem(source_path, dest_path,
+ temp_dir);
+ AddWorkItem(item);
+ return item;
+}
+
+WorkItem* WorkItemList::AddSetRegValueWorkItem(
+ HKEY predefined_root,
+ const std::wstring& key_path,
+ const std::wstring& value_name,
+ const std::wstring& value_data,
+ bool overwrite) {
+ WorkItem* item = WorkItem::CreateSetRegValueWorkItem(predefined_root,
+ key_path,
+ value_name,
+ value_data,
+ overwrite);
+ AddWorkItem(item);
+ return item;
+}
+
+WorkItem* WorkItemList::AddSetRegValueWorkItem(HKEY predefined_root,
+ const std::wstring& key_path,
+ const std::wstring& value_name,
+ DWORD value_data,
+ bool overwrite) {
+ WorkItem* item = WorkItem::CreateSetRegValueWorkItem(predefined_root,
+ key_path,
+ value_name,
+ value_data,
+ overwrite);
+ AddWorkItem(item);
+ return item;
+}
+
+WorkItem* WorkItemList::AddSelfRegWorkItem(const std::wstring& dll_path,
+ bool do_register,
+ bool user_level_registration) {
+ WorkItem* item = WorkItem::CreateSelfRegWorkItem(dll_path, do_register,
+ user_level_registration);
+ AddWorkItem(item);
+ return item;
}
////////////////////////////////////////////////////////////////////////////////
NoRollbackWorkItemList::~NoRollbackWorkItemList() {
}
-void NoRollbackWorkItemList::Rollback() {
- NOTREACHED() << "Can't rollback a NoRollbackWorkItemList";
-}
-
bool NoRollbackWorkItemList::Do() {
if (status_ != ADD_ITEM)
return false;
@@ -181,3 +205,8 @@ bool NoRollbackWorkItemList::Do() {
status_ = LIST_EXECUTED;
return result;
}
+
+void NoRollbackWorkItemList::Rollback() {
+ NOTREACHED() << "Can't rollback a NoRollbackWorkItemList";
+}
+
diff --git a/chrome/installer/util/work_item_list.h b/chrome/installer/util/work_item_list.h
index c43041f..30d332a 100644
--- a/chrome/installer/util/work_item_list.h
+++ b/chrome/installer/util/work_item_list.h
@@ -12,6 +12,7 @@
#include <string>
#include <vector>
+#include "base/scoped_ptr.h"
#include "chrome/installer/util/work_item.h"
class FilePath;
@@ -31,69 +32,74 @@ class WorkItemList : public WorkItem {
// Rollback the WorkItems in the reverse order as they are executed.
virtual void Rollback();
- // Add a WorkItem to the list. Return true if the WorkItem is successfully
- // added. Return false otherwise.
+ // Add a WorkItem to the list.
// A WorkItem can only be added to the list before the list's DO() is called.
// Once a WorkItem is added to the list. The list owns the WorkItem.
- bool AddWorkItem(WorkItem* work_item);
+ void AddWorkItem(WorkItem* work_item);
// Add a CopyTreeWorkItem to the list of work items.
- bool AddCopyTreeWorkItem(const std::wstring& source_path,
- const std::wstring& dest_path,
- const std::wstring& temp_dir,
- CopyOverWriteOption overwrite_option,
- const std::wstring& alternative_path = L"");
+ WorkItem* AddCopyTreeWorkItem(const std::wstring& source_path,
+ const std::wstring& dest_path,
+ const std::wstring& temp_dir,
+ CopyOverWriteOption overwrite_option,
+ const std::wstring& alternative_path = L"");
// Add a CreateDirWorkItem that creates a directory at the given path.
- bool AddCreateDirWorkItem(const FilePath& path);
+ WorkItem* AddCreateDirWorkItem(const FilePath& path);
// Add a CreateRegKeyWorkItem that creates a registry key at the given
// path.
- bool AddCreateRegKeyWorkItem(HKEY predefined_root, const std::wstring& path);
+ WorkItem* AddCreateRegKeyWorkItem(HKEY predefined_root,
+ const std::wstring& path);
+
+ // Add a DeleteRegKeyWorkItem that deletes a registry key from the given
+ // path.
+ WorkItem* AddDeleteRegKeyWorkItem(HKEY predefined_root,
+ const std::wstring& path);
// Add a DeleteRegValueWorkItem that deletes registry value of type REG_SZ
// or REG_DWORD.
- bool AddDeleteRegValueWorkItem(HKEY predefined_root,
- const std::wstring& key_path,
- const std::wstring& value_name,
- bool is_str_type);
+ WorkItem* AddDeleteRegValueWorkItem(HKEY predefined_root,
+ const std::wstring& key_path,
+ const std::wstring& value_name,
+ bool is_str_type);
// Add a DeleteTreeWorkItem that recursively deletes a file system
// hierarchy at the given root path. A key file can be optionally specified
// by key_path.
- bool AddDeleteTreeWorkItem(const FilePath& root_path,
- const std::vector<FilePath>& key_paths);
+ WorkItem* AddDeleteTreeWorkItem(const FilePath& root_path,
+ const std::vector<FilePath>& key_paths);
// Same as above but without support for key files.
- bool AddDeleteTreeWorkItem(const FilePath& root_path);
+ WorkItem* AddDeleteTreeWorkItem(const FilePath& root_path);
// Add a MoveTreeWorkItem to the list of work items.
- bool AddMoveTreeWorkItem(const std::wstring& source_path,
- const std::wstring& dest_path,
- const std::wstring& temp_dir);
+ WorkItem* AddMoveTreeWorkItem(const std::wstring& source_path,
+ const std::wstring& dest_path,
+ const std::wstring& temp_dir);
// Add a SetRegValueWorkItem that sets a registry value with REG_SZ type
// at the key with specified path.
- bool AddSetRegValueWorkItem(HKEY predefined_root,
- const std::wstring& key_path,
- const std::wstring& value_name,
- const std::wstring& value_data,
- bool overwrite);
+ WorkItem* AddSetRegValueWorkItem(HKEY predefined_root,
+ const std::wstring& key_path,
+ const std::wstring& value_name,
+ const std::wstring& value_data,
+ bool overwrite);
// Add a SetRegValueWorkItem that sets a registry value with REG_DWORD type
// at the key with specified path.
- bool AddSetRegValueWorkItem(HKEY predefined_root,
- const std::wstring& key_path,
- const std::wstring& value_name,
- DWORD value_data,
- bool overwrite);
+ WorkItem* AddSetRegValueWorkItem(HKEY predefined_root,
+ const std::wstring& key_path,
+ const std::wstring& value_name,
+ DWORD value_data,
+ bool overwrite);
// Add a SelfRegWorkItem that registers or unregisters a DLL at the
// specified path. If user_level_registration is true, then alternate
// registration and unregistration entry point names will be used.
- bool AddSelfRegWorkItem(const std::wstring& dll_path,
- bool do_register,
- bool user_level_registration);
+ WorkItem* AddSelfRegWorkItem(const std::wstring& dll_path,
+ bool do_register,
+ bool user_level_registration);
protected:
friend class WorkItem;
diff --git a/chrome/installer/util/work_item_list_unittest.cc b/chrome/installer/util/work_item_list_unittest.cc
index 1ba6e52..97a6927 100644
--- a/chrome/installer/util/work_item_list_unittest.cc
+++ b/chrome/installer/util/work_item_list_unittest.cc
@@ -10,6 +10,7 @@
#include "base/scoped_ptr.h"
#include "base/string_util.h"
#include "base/win/registry.h"
+#include "chrome/installer/util/conditional_work_item_list.h"
#include "chrome/installer/util/work_item.h"
#include "chrome/installer/util/work_item_list.h"
#include "testing/gtest/include/gtest/gtest.h"
@@ -67,21 +68,21 @@ TEST_F(WorkItemListTest, ExecutionSuccess) {
work_item.reset(reinterpret_cast<WorkItem*>(
WorkItem::CreateCreateDirWorkItem(dir_to_create)));
- EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+ work_item_list->AddWorkItem(work_item.release());
std::wstring key_to_create(test_root);
file_util::AppendToPath(&key_to_create, L"ExecutionSuccess");
work_item.reset(reinterpret_cast<WorkItem*>(
WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create)));
- EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+ work_item_list->AddWorkItem(work_item.release());
std::wstring name(L"name");
std::wstring data(data_str);
work_item.reset(reinterpret_cast<WorkItem*>(
WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, key_to_create,
name, data, false)));
- EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+ work_item_list->AddWorkItem(work_item.release());
EXPECT_TRUE(work_item_list->Do());
@@ -116,14 +117,14 @@ TEST_F(WorkItemListTest, ExecutionFailAndRollback) {
work_item.reset(reinterpret_cast<WorkItem*>(
WorkItem::CreateCreateDirWorkItem(dir_to_create)));
- EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+ work_item_list->AddWorkItem(work_item.release());
std::wstring key_to_create(test_root);
file_util::AppendToPath(&key_to_create, L"ExecutionFail");
work_item.reset(reinterpret_cast<WorkItem*>(
WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create)));
- EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+ work_item_list->AddWorkItem(work_item.release());
std::wstring not_created_key(test_root);
file_util::AppendToPath(&not_created_key, L"NotCreated");
@@ -132,13 +133,13 @@ TEST_F(WorkItemListTest, ExecutionFailAndRollback) {
work_item.reset(reinterpret_cast<WorkItem*>(
WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, not_created_key,
name, data, false)));
- EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+ work_item_list->AddWorkItem(work_item.release());
// This one will not be executed because we will fail early.
work_item.reset(reinterpret_cast<WorkItem*>(
WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER,
not_created_key)));
- EXPECT_TRUE(work_item_list->AddWorkItem(work_item.release()));
+ work_item_list->AddWorkItem(work_item.release());
EXPECT_FALSE(work_item_list->Do());
@@ -157,3 +158,113 @@ TEST_F(WorkItemListTest, ExecutionFailAndRollback) {
EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
EXPECT_FALSE(file_util::PathExists(top_dir_to_create));
}
+
+TEST_F(WorkItemListTest, ConditionalExecutionSuccess) {
+ scoped_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList());
+ scoped_ptr<WorkItem> work_item;
+
+ FilePath top_dir_to_create(test_dir_);
+ top_dir_to_create = top_dir_to_create.AppendASCII("a");
+ FilePath dir_to_create(top_dir_to_create);
+ dir_to_create = dir_to_create.AppendASCII("b");
+ ASSERT_FALSE(file_util::PathExists(dir_to_create));
+
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateDirWorkItem(dir_to_create)));
+ work_item_list->AddWorkItem(work_item.release());
+
+ scoped_ptr<WorkItemList> conditional_work_item_list(
+ WorkItem::CreateConditionalWorkItemList(
+ new ConditionRunIfFileExists(dir_to_create)));
+
+ std::wstring key_to_create(test_root);
+ file_util::AppendToPath(&key_to_create, L"ExecutionSuccess");
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create)));
+ conditional_work_item_list->AddWorkItem(work_item.release());
+
+ std::wstring name(L"name");
+ std::wstring data(data_str);
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, key_to_create,
+ name, data, false)));
+ conditional_work_item_list->AddWorkItem(work_item.release());
+
+ work_item_list->AddWorkItem(conditional_work_item_list.release());
+
+ EXPECT_TRUE(work_item_list->Do());
+
+ // Verify all WorkItems have been executed.
+ RegKey key;
+ EXPECT_TRUE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+ std::wstring read_out;
+ EXPECT_TRUE(key.ReadValue(name.c_str(), &read_out));
+ EXPECT_EQ(0, read_out.compare(data_str));
+ key.Close();
+ EXPECT_TRUE(file_util::PathExists(dir_to_create));
+
+ work_item_list->Rollback();
+
+ // Verify everything is rolled back.
+ // The value must have been deleted first in roll back otherwise the key
+ // can not be deleted.
+ EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+ EXPECT_FALSE(file_util::PathExists(top_dir_to_create));
+}
+
+TEST_F(WorkItemListTest, ConditionalExecutionConditionFailure) {
+ scoped_ptr<WorkItemList> work_item_list(WorkItem::CreateWorkItemList());
+ scoped_ptr<WorkItem> work_item;
+
+ FilePath top_dir_to_create(test_dir_);
+ top_dir_to_create = top_dir_to_create.AppendASCII("a");
+ FilePath dir_to_create(top_dir_to_create);
+ dir_to_create = dir_to_create.AppendASCII("b");
+ ASSERT_FALSE(file_util::PathExists(dir_to_create));
+
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateDirWorkItem(dir_to_create)));
+ work_item_list->AddWorkItem(work_item.release());
+
+ scoped_ptr<WorkItemList> conditional_work_item_list(
+ WorkItem::CreateConditionalWorkItemList(
+ new ConditionRunIfFileExists(dir_to_create.AppendASCII("c"))));
+
+ std::wstring key_to_create(test_root);
+ file_util::AppendToPath(&key_to_create, L"ExecutionSuccess");
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateCreateRegKeyWorkItem(HKEY_CURRENT_USER, key_to_create)));
+ conditional_work_item_list->AddWorkItem(work_item.release());
+
+ std::wstring name(L"name");
+ std::wstring data(data_str);
+ work_item.reset(reinterpret_cast<WorkItem*>(
+ WorkItem::CreateSetRegValueWorkItem(HKEY_CURRENT_USER, key_to_create,
+ name, data, false)));
+ conditional_work_item_list->AddWorkItem(work_item.release());
+
+ work_item_list->AddWorkItem(conditional_work_item_list.release());
+
+ EXPECT_TRUE(work_item_list->Do());
+
+ // Verify that the WorkItems added as part of the conditional list have NOT
+ // been executed.
+ RegKey key;
+ EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+ std::wstring read_out;
+ EXPECT_FALSE(key.ReadValue(name.c_str(), &read_out));
+ key.Close();
+
+ // Verify that the other work item was executed.
+ EXPECT_TRUE(file_util::PathExists(dir_to_create));
+
+ work_item_list->Rollback();
+
+ // Verify everything is rolled back.
+ // The value must have been deleted first in roll back otherwise the key
+ // can not be deleted.
+ EXPECT_FALSE(key.Open(HKEY_CURRENT_USER, key_to_create.c_str(), KEY_READ));
+ EXPECT_FALSE(file_util::PathExists(top_dir_to_create));
+}
+
+