Chromium Code Reviews| Index: chrome/installer/util/installer_state.cc |
| =================================================================== |
| --- chrome/installer/util/installer_state.cc (revision 71802) |
| +++ chrome/installer/util/installer_state.cc (working copy) |
| @@ -4,11 +4,23 @@ |
| #include "chrome/installer/util/installer_state.h" |
| +#include <algorithm> |
| +#include <functional> |
| +#include <utility> |
| + |
| +#include "base/command_line.h" |
| +#include "base/file_util.h" |
| #include "base/logging.h" |
| +#include "base/scoped_ptr.h" |
| +#include "base/string_util.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "chrome/installer/util/delete_tree_work_item.h" |
| +#include "chrome/installer/util/helper.h" |
| #include "chrome/installer/util/installation_state.h" |
| #include "chrome/installer/util/master_preferences.h" |
| #include "chrome/installer/util/master_preferences_constants.h" |
| -#include "chrome/installer/util/package_properties.h" |
| +#include "chrome/installer/util/product.h" |
| +#include "chrome/installer/util/work_item.h" |
| namespace installer { |
| @@ -16,7 +28,8 @@ |
| const InstallationState& machine_state) { |
| // First, is the package present? |
| const ProductState* package = |
| - machine_state.GetMultiPackageState(system_install_); |
| + machine_state.GetProductState(level_ == SYSTEM_LEVEL, |
| + BrowserDistribution::CHROME_BINARIES); |
| if (package == NULL) { |
| // The multi-install package has not been installed, so it certainly isn't |
| // being updated. |
| @@ -33,7 +46,7 @@ |
| for (const BrowserDistribution::Type* scan = &types[0], |
| *end = &types[num_types]; scan != end; ++scan) { |
| const ProductState* product = |
| - machine_state.GetProductState(system_install_, *scan); |
| + machine_state.GetProductState(level_ == SYSTEM_LEVEL, *scan); |
| if (product == NULL) { |
| VLOG(2) << "It seems that distribution type " << *scan |
| << " is being installed for the first time."; |
| @@ -51,42 +64,289 @@ |
| return true; |
| } |
| -InstallerState::InstallerState() : operation_(UNINITIALIZED) { |
| +InstallerState::InstallerState() |
| + : operation_(UNINITIALIZED), |
| + multi_package_distribution_(NULL), |
| + level_(UNKNOWN_LEVEL), |
| + package_type_(UNKNOWN_PACKAGE_TYPE), |
| + root_key_(NULL), |
| + msi_(false), |
| + verbose_logging_(false) { |
| } |
| -void InstallerState::Initialize(const MasterPreferences& prefs, |
| +InstallerState::InstallerState(Level level) |
| + : operation_(UNINITIALIZED), |
| + multi_package_distribution_(NULL), |
| + level_(level), |
| + package_type_(UNKNOWN_PACKAGE_TYPE), |
| + root_key_(NULL), |
| + msi_(false), |
| + verbose_logging_(false) { |
| +} |
| + |
| + |
| +void InstallerState::Initialize(const CommandLine& command_line, |
| + const MasterPreferences& prefs, |
| const InstallationState& machine_state) { |
| - if (!prefs.GetBool(installer::master_preferences::kSystemLevel, |
| - &system_install_)) |
| - system_install_ = false; |
| + bool pref_bool; |
| + if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool)) |
| + pref_bool = false; |
| + level_ = (pref_bool ? SYSTEM_LEVEL : USER_LEVEL); |
| + root_key_ = (level_ == SYSTEM_LEVEL ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER); |
| + |
| + if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_)) |
|
tommi (sloooow) - chröme
2011/01/21 21:45:17
in order to get rid of this pattern we should add
grt (UTC plus 2)
2011/01/24 16:07:02
<nods head>
|
| + verbose_logging_ = false; |
| + |
| + if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool)) |
| + pref_bool = false; |
| + package_type_ = (pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE); |
| + |
| + if (!prefs.GetBool(master_preferences::kMsi, &msi_)) |
| + msi_ = false; |
| + |
| + const bool is_uninstall = command_line.HasSwitch(switches::kUninstall); |
| + |
| + if (prefs.install_chrome()) { |
| + const Product& p = |
| + AddProductFromPreferences(BrowserDistribution::CHROME_BROWSER, prefs, |
| + machine_state); |
| + VLOG(1) << (is_uninstall ? "Uninstall" : "Install") |
| + << " distribution: " << p.distribution()->GetApplicationName(); |
| + } |
| + if (prefs.install_chrome_frame()) { |
| + const Product& p = |
| + AddProductFromPreferences(BrowserDistribution::CHROME_FRAME, prefs, |
| + machine_state); |
| + VLOG(1) << (is_uninstall ? "Uninstall" : "Install") |
| + << " distribution: " << p.distribution()->GetApplicationName(); |
| + } |
| + |
| + // Operate in the Chrome Frame directory iff single install and installing |
| + // CF. Otherwise, install in Chrome binaries directory. |
| + if (package_type_ == SINGLE_PACKAGE && prefs.install_chrome_frame()) { |
| + target_path_ = GetChromeInstallPath( |
| + level_ == SYSTEM_LEVEL, |
| + BrowserDistribution::GetSpecificDistribution( |
| + BrowserDistribution::CHROME_FRAME)); |
| + } else { |
| + target_path_ = GetChromeInstallPath( |
| + level_ == SYSTEM_LEVEL, |
| + BrowserDistribution::GetSpecificDistribution( |
| + BrowserDistribution::CHROME_BINARIES)); |
| + } |
| + |
| + if (package_type_ == MULTI_PACKAGE) { |
| + multi_package_distribution_ = |
| + BrowserDistribution::GetSpecificDistribution( |
| + BrowserDistribution::CHROME_BINARIES); |
| + } |
| + |
| BrowserDistribution* operand = NULL; |
| - if (!prefs.is_multi_install()) { |
| + if (is_uninstall) { |
| + operation_ = UNINSTALL; |
| + } else if (!prefs.is_multi_install()) { |
| // For a single-install, the current browser dist is the operand. |
| operand = BrowserDistribution::GetDistribution(); |
| operation_ = SINGLE_INSTALL_OR_UPDATE; |
| } else if (IsMultiInstallUpdate(prefs, machine_state)) { |
| // Updates driven by Google Update take place under the multi-installer's |
| // app guid. |
| - installer::ActivePackageProperties package_properties; |
| + operand = multi_package_distribution_; |
| operation_ = MULTI_UPDATE; |
| - state_key_ = package_properties.GetStateKey(); |
| } else { |
| // Initial and over installs will always take place under one of the |
| // product app guids. Chrome Frame's will be used if only Chrome Frame |
| // is being installed. In all other cases, Chrome's is used. |
| + operation_ = MULTI_INSTALL; |
| + } |
| + |
| + if (operand == NULL) { |
| operand = BrowserDistribution::GetSpecificDistribution( |
| prefs.install_chrome() ? |
| BrowserDistribution::CHROME_BROWSER : |
| - BrowserDistribution::CHROME_FRAME, |
| - prefs); |
| - operation_ = MULTI_INSTALL; |
| + BrowserDistribution::CHROME_FRAME); |
| } |
| - if (operand != NULL) { |
| - state_key_ = operand->GetStateKey(); |
| + state_key_ = operand->GetStateKey(); |
| +} |
| + |
| +void InstallerState::ResetProducts() { |
| + operation_ = UNINITIALIZED; |
| + target_path_.clear(); |
| + products_.reset(); |
| + multi_package_distribution_ = NULL; |
| + package_type_ = UNKNOWN_PACKAGE_TYPE; |
| + msi_ = false; |
| +} |
| + |
| +const Product& InstallerState::AddProductFromPreferences( |
| + BrowserDistribution::Type distribution_type, |
| + const MasterPreferences& prefs, |
| + const InstallationState& machine_state) { |
| + scoped_ptr<Product> product( |
| + new Product(BrowserDistribution::GetSpecificDistribution( |
| + distribution_type))); |
| + if (!msi_) { |
| + const ProductState* product_state = machine_state.GetProductState( |
| + level_ == SYSTEM_LEVEL, distribution_type); |
| + if (product_state != NULL) |
| + msi_ = product_state->msi(); |
| } |
| + product->InitializeFromPreferences(prefs); |
| + products_.push_back(product.release()); |
| + return *products_[products_.size() - 1]; |
| } |
| +Product* InstallerState::AddProductFromState( |
| + BrowserDistribution::Type type, |
| + const ProductState& state) { |
|
robertshield
2011/01/21 16:58:56
This method should return NULL when package_type_
grt (UTC plus 2)
2011/01/24 16:07:02
Done. Also if the target_path would conflict.
|
| + DCHECK_NE(SINGLE_PACKAGE, package_type_) |
| + << "Cannot process more than one single-install product at a time."; |
| + scoped_ptr<Product> product( |
| + new Product(BrowserDistribution::GetSpecificDistribution(type))); |
| + product->InitializeFromUninstallCommand(state.uninstall_command()); |
| + products_.push_back(product.release()); |
| + if (package_type_ == UNKNOWN_PACKAGE_TYPE) { |
| + package_type_ = state.multi_install() ? MULTI_PACKAGE : SINGLE_PACKAGE; |
| + if (package_type_ == MULTI_PACKAGE) |
| + multi_package_distribution_ = |
| + BrowserDistribution::GetSpecificDistribution( |
| + BrowserDistribution::CHROME_BINARIES); |
| + // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory(). |
| + target_path_ = state.GetSetupPath().DirName().DirName().DirName(); |
| + // Use the ClientState key of the first product added. |
| + state_key_ = products_[products_.size() - 1]->distribution()->GetStateKey(); |
| + } else { |
| + DCHECK_EQ(MULTI_PACKAGE, package_type_); |
| + DCHECK(state.multi_install()); |
| + DCHECK_EQ(target_path_.value(), |
| + state.GetSetupPath().DirName().DirName().DirName().value()); |
| + } |
| + msi_ |= state.msi(); |
| + return products_[products_.size() - 1]; |
| +} |
| + |
| +bool InstallerState::RemoveProduct(const Product* product) { |
| + ScopedVector<Product>::iterator it = |
| + std::find(products_.begin(), products_.end(), product); |
|
tommi (sloooow) - chröme
2011/01/21 21:45:17
indent
grt (UTC plus 2)
2011/01/24 16:07:02
Done.
|
| + if (it != products_.end()) { |
| + products_->erase(it); |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| +const Product* InstallerState::FindProduct( |
| + BrowserDistribution::Type distribution_type) const { |
| + const Products::const_iterator it = |
| + std::find_if(products_.begin(), products_.end(), |
| + std::bind2nd(std::mem_fun(&Product::is_type), |
| + distribution_type)); |
|
tommi (sloooow) - chröme
2011/01/21 21:45:17
this feels less readable to me than a simple loop
grt (UTC plus 2)
2011/01/24 16:07:02
Okay.
|
| + return it == products_.end() ? NULL : *it; |
| +} |
| + |
| +Version* InstallerState::GetCurrentVersion( |
| + const InstallationState& machine_state) const { |
| + DCHECK(!products_.empty()); |
| + scoped_ptr<Version> current_version; |
| + const BrowserDistribution::Type prod_type = (package_type_ == MULTI_PACKAGE) ? |
| + BrowserDistribution::CHROME_BINARIES : |
| + products_[0]->distribution()->GetType(); |
| + const ProductState* product_state = |
| + machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type); |
| + |
| + if (product_state != NULL) { |
| + const Version* version = NULL; |
| + |
| + // Be aware that there might be a pending "new_chrome.exe" already in the |
| + // installation path. If so, we use old_version, which holds the version of |
| + // "chrome.exe" itself. |
| + if (file_util::PathExists(target_path().Append(kChromeNewExe))) |
| + version = product_state->old_version(); |
| + |
| + if (version == NULL) |
| + version = &product_state->version(); |
| + |
| + current_version.reset(version->Clone()); |
| + } |
| + |
| + return current_version.release(); |
| +} |
| + |
| +FilePath InstallerState::GetInstallerDirectory(const Version& version) const { |
| + return target_path().Append(ASCIIToWide(version.GetString())) |
| + .Append(kInstallerDir); |
| +} |
| + |
| +void InstallerState::RemoveOldVersionDirectories( |
| + const Version& latest_version) const { |
| + file_util::FileEnumerator version_enum(target_path(), false, |
| + file_util::FileEnumerator::DIRECTORIES); |
| + scoped_ptr<Version> version; |
| + std::vector<FilePath> key_files; |
| + |
| + // We try to delete all directories whose versions are lower than |
| + // latest_version. |
| + FilePath next_version = version_enum.Next(); |
| + while (!next_version.empty()) { |
| + file_util::FileEnumerator::FindInfo find_data = {0}; |
| + version_enum.GetFindInfo(&find_data); |
| + VLOG(1) << "directory found: " << find_data.cFileName; |
| + version.reset(Version::GetVersionFromString( |
| + WideToASCII(find_data.cFileName))); |
| + if (version.get() && (latest_version.CompareTo(*version) > 0)) { |
|
tommi (sloooow) - chröme
2011/01/21 21:45:17
no need for () around latest_version check.
grt (UTC plus 2)
2011/01/24 16:07:02
Done.
|
| + key_files.clear(); |
| + std::for_each(products_.begin(), products_.end(), |
| + std::bind2nd(std::mem_fun(&Product::AddKeyFiles), |
| + &key_files)); |
| + const std::vector<FilePath>::iterator end = key_files.end(); |
| + for (std::vector<FilePath>::iterator scan = key_files.begin(); |
| + scan != end; ++scan) { |
| + *scan = next_version.Append(*scan); |
| + } |
| + |
| + VLOG(1) << "Deleting directory: " << next_version.value(); |
| + |
| + scoped_ptr<WorkItem> item( |
| + WorkItem::CreateDeleteTreeWorkItem(next_version, key_files)); |
| + if (!item->Do()) |
| + item->Rollback(); |
| + } |
| + |
| + next_version = version_enum.Next(); |
| + } |
| +} |
| + |
| +void InstallerState::AddComDllList(std::vector<FilePath>* com_dll_list) const { |
| + std::for_each(products_.begin(), products_.end(), |
| + std::bind2nd(std::mem_fun(&Product::AddComDllList), |
| + com_dll_list)); |
| +} |
| + |
| +bool InstallerState::SetChannelFlags(bool set, |
| + ChannelInfo* channel_info) const { |
| + bool modified = false; |
| + |
| + // We don't have access to lambdas yet, but we can sure fake it!. |
| + struct SetChannelFlagsFunc |
| + : public std::unary_function<const Product*, void> { |
| + public: |
| + SetChannelFlagsFunc(bool set, ChannelInfo* channel_info, bool* modified) |
| + : channel_info_(channel_info), modified_(modified), set_(set) {} |
| + result_type operator()(argument_type product) const { |
| + *modified_ |= product->SetChannelFlags(set_, channel_info_); |
| + } |
| + private: |
| + ChannelInfo* channel_info_; |
| + bool* modified_; |
| + bool set_; |
| + }; |
| + |
| + std::for_each(products_.begin(), products_.end(), |
| + SetChannelFlagsFunc(set, channel_info, &modified)); |
| + return modified; |
|
robertshield
2011/01/21 19:37:47
this is neat, but the following (or a non-broken v
tommi (sloooow) - chröme
2011/01/21 21:45:17
+1
grt (UTC plus 2)
2011/01/24 16:07:02
Okay. I can't wait for lambdas...
|
| +} |
| + |
| } // namespace installer |