diff options
author | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-04 19:27:59 +0000 |
---|---|---|
committer | robertshield@chromium.org <robertshield@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-04 19:27:59 +0000 |
commit | ba4f2a1ec1d2318847b6ee65ae1e8ee52ed234da (patch) | |
tree | faff2a33f5d263d9c4c51e1c705b136645176357 | |
parent | d8858e37c0631e16185794d180269dce5d7e8b70 (diff) | |
download | chromium_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.gypi | 4 | ||||
-rw-r--r-- | chrome/installer/setup/install.cc | 227 | ||||
-rw-r--r-- | chrome/installer/setup/install.h | 26 | ||||
-rw-r--r-- | chrome/installer/setup/uninstall.cc | 14 | ||||
-rw-r--r-- | chrome/installer/util/conditional_work_item_list.cc | 41 | ||||
-rw-r--r-- | chrome/installer/util/conditional_work_item_list.h | 61 | ||||
-rw-r--r-- | chrome/installer/util/delete_reg_key_work_item.cc | 44 | ||||
-rw-r--r-- | chrome/installer/util/delete_reg_key_work_item.h | 44 | ||||
-rw-r--r-- | chrome/installer/util/self_reg_work_item.cc | 9 | ||||
-rw-r--r-- | chrome/installer/util/self_reg_work_item.h | 5 | ||||
-rw-r--r-- | chrome/installer/util/set_reg_value_work_item.cc | 160 | ||||
-rw-r--r-- | chrome/installer/util/work_item.cc | 13 | ||||
-rw-r--r-- | chrome/installer/util/work_item.h | 39 | ||||
-rw-r--r-- | chrome/installer/util/work_item_list.cc | 183 | ||||
-rw-r--r-- | chrome/installer/util/work_item_list.h | 72 | ||||
-rw-r--r-- | chrome/installer/util/work_item_list_unittest.cc | 125 |
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(¬_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)); +} + + |