Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(7305)

Unified Diff: chrome/installer/util/installer_state.cc

Issue 6288009: More installer refactoring in the interest of fixing some bugs and cleaning t... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
Index: chrome/installer/util/installer_state.cc
===================================================================
--- chrome/installer/util/installer_state.cc (revision 72328)
+++ 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,372 @@
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_(UNKNOWN_LEVEL),
+ package_type_(UNKNOWN_PACKAGE_TYPE),
+ root_key_(NULL),
+ msi_(false),
+ verbose_logging_(false) {
+ // Use set_level() so that root_key_ is updated properly.
+ set_level(level);
+}
+
+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;
+ set_level(pref_bool ? SYSTEM_LEVEL : USER_LEVEL);
+ if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_))
+ verbose_logging_ = false;
+
+ if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool))
+ pref_bool = false;
+ set_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()) {
+ Product* p =
+ AddProductFromPreferences(BrowserDistribution::CHROME_BROWSER, prefs,
+ machine_state);
+ VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
+ << " distribution: " << p->distribution()->GetApplicationName();
+ }
+ if (prefs.install_chrome_frame()) {
+ Product* p =
+ AddProductFromPreferences(BrowserDistribution::CHROME_FRAME, prefs,
+ machine_state);
+ VLOG(1) << (is_uninstall ? "Uninstall" : "Install")
+ << " distribution: " << p->distribution()->GetApplicationName();
+ }
+
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::set_level(Level level) {
+ level_ = level;
+ switch (level) {
+ case USER_LEVEL:
+ root_key_ = HKEY_CURRENT_USER;
+ break;
+ case SYSTEM_LEVEL:
+ root_key_ = HKEY_LOCAL_MACHINE;
+ break;
+ default:
+ DCHECK(level == UNKNOWN_LEVEL);
+ level_ = UNKNOWN_LEVEL;
+ root_key_ = NULL;
+ break;
}
}
+void InstallerState::set_package_type(PackageType type) {
+ package_type_ = type;
+ switch (type) {
+ case SINGLE_PACKAGE:
+ multi_package_distribution_ = NULL;
+ break;
+ case MULTI_PACKAGE:
+ multi_package_distribution_ =
+ BrowserDistribution::GetSpecificDistribution(
+ BrowserDistribution::CHROME_BINARIES);
+ break;
+ default:
+ DCHECK(type == UNKNOWN_PACKAGE_TYPE);
+ package_type_ = UNKNOWN_PACKAGE_TYPE;
+ multi_package_distribution_ = NULL;
+ break;
+ }
+}
+
+// Returns the Chrome binaries directory for multi-install or |dist|'s directory
+// otherwise.
+FilePath InstallerState::GetDefaultProductInstallPath(
+ BrowserDistribution* dist) const {
+ DCHECK(dist);
+ DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE);
+
+ if (package_type_ == SINGLE_PACKAGE) {
+ return GetChromeInstallPath(system_install(), dist);
+ } else {
+ return GetChromeInstallPath(system_install(),
+ BrowserDistribution::GetSpecificDistribution(
+ BrowserDistribution::CHROME_BINARIES));
+ }
+}
+
+// Evaluates a product's eligibility for participation in this operation.
+// We never expect these checks to fail, hence they all terminate the process in
+// debug builds. See the log messages for details.
+bool InstallerState::CanAddProduct(const Product& product,
+ const FilePath* product_dir) const {
+ switch (package_type_) {
+ case SINGLE_PACKAGE:
+ if (!products_.empty()) {
+ LOG(DFATAL) << "Cannot process more than one single-install product.";
+ return false;
+ }
+ break;
+ case MULTI_PACKAGE:
+ if (!product.HasOption(kOptionMultiInstall)) {
+ LOG(DFATAL) << "Cannot process a single-install product with a "
+ "multi-install state.";
+ return false;
+ }
+ if (FindProduct(product.distribution()->GetType()) != NULL) {
+ LOG(DFATAL) << "Cannot process more than one product of the same type.";
+ return false;
+ }
+ if (!target_path_.empty()) {
+ FilePath default_dir;
+ if (product_dir == NULL)
+ default_dir = GetDefaultProductInstallPath(product.distribution());
+ if (!FilePath::CompareEqualIgnoreCase(
+ (product_dir == NULL ? default_dir : *product_dir).value(),
+ target_path_.value())) {
+ LOG(DFATAL) << "Cannot process products in different directories.";
+ return false;
+ }
+ }
+ break;
+ default:
+ DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_);
+ break;
+ }
+ return true;
+}
+
+// Adds |product|, installed in |product_dir| to this object's collection. If
+// |product_dir| is NULL, the product's default install location is used.
+// Returns NULL if |product| is incompatible with this object. Otherwise,
+// returns a pointer to the product (ownership is held by this object).
+Product* InstallerState::AddProductInDirectory(const FilePath* product_dir,
+ scoped_ptr<Product>* product) {
+ DCHECK(product != NULL);
+ DCHECK(product->get() != NULL);
+ const Product& the_product = *product->get();
+
+ if (!CanAddProduct(the_product, product_dir))
+ return NULL;
+
+ if (package_type_ == UNKNOWN_PACKAGE_TYPE) {
+ set_package_type(the_product.HasOption(kOptionMultiInstall) ?
+ MULTI_PACKAGE : SINGLE_PACKAGE);
+ }
+
+ if (target_path_.empty()) {
+ if (product_dir == NULL)
+ target_path_ = GetDefaultProductInstallPath(the_product.distribution());
+ else
+ target_path_ = *product_dir;
+ }
+
+ if (state_key_.empty())
+ state_key_ = the_product.distribution()->GetStateKey();
+
+ products_.push_back(product->release());
+ return products_[products_->size() - 1];
+}
+
+Product* InstallerState::AddProduct(scoped_ptr<Product>* product) {
+ return AddProductInDirectory(NULL, product);
+}
+
+// Adds a product of type |distribution_type| constructed on the basis of
+// |prefs|, setting this object's msi flag if the product is represented in
+// |machine_state| and is msi-installed. Returns the product that was added,
+// or NULL if |state| is incompatible with this object. Ownership is not passed
+// to the caller.
+Product* InstallerState::AddProductFromPreferences(
+ BrowserDistribution::Type distribution_type,
+ const MasterPreferences& prefs,
+ const InstallationState& machine_state) {
+ scoped_ptr<Product> product_ptr(
+ new Product(BrowserDistribution::GetSpecificDistribution(
+ distribution_type)));
+ product_ptr->InitializeFromPreferences(prefs);
+
+ Product* product = AddProductInDirectory(NULL, &product_ptr);
+
+ if (product != NULL && !msi_) {
+ const ProductState* product_state = machine_state.GetProductState(
+ system_install(), distribution_type);
+ if (product_state != NULL)
+ msi_ = product_state->is_msi();
+ }
+
+ return product;
+}
+
+Product* InstallerState::AddProductFromState(
+ BrowserDistribution::Type type,
+ const ProductState& state) {
+ scoped_ptr<Product> product_ptr(
+ new Product(BrowserDistribution::GetSpecificDistribution(type)));
+ product_ptr->InitializeFromUninstallCommand(state.uninstall_command());
+
+ // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory().
+ FilePath product_dir = state.GetSetupPath().DirName().DirName().DirName();
+
+ Product* product = AddProductInDirectory(&product_dir, &product_ptr);
+
+ if (product != NULL)
+ msi_ |= state.is_msi();
+
+ return product;
+}
+
+bool InstallerState::RemoveProduct(const Product* product) {
+ ScopedVector<Product>::iterator it =
+ std::find(products_.begin(), products_.end(), product);
+ if (it != products_.end()) {
+ products_->erase(it);
+ return true;
+ }
+ return false;
+}
+
+const Product* InstallerState::FindProduct(
+ BrowserDistribution::Type distribution_type) const {
+ for (Products::const_iterator scan = products_.begin(), end = products_.end();
+ scan != end; ++scan) {
+ if ((*scan)->is_type(distribution_type))
+ return *scan;
+ }
+ return NULL;
+}
+
+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) {
+ 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;
+ for (Products::const_iterator scan = products_.begin(), end = products_.end();
+ scan != end; ++scan) {
+ modified |= (*scan)->SetChannelFlags(set, channel_info);
+ }
+ return modified;
+}
+
} // namespace installer

Powered by Google App Engine
This is Rietveld 408576698