Chromium Code Reviews| Index: chrome/installer/setup/setup_main.cc |
| diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc |
| index 7925d7911ba25224bbc19e056af110c962708bce..e85ae5486f59ada5e173854037b29318368a1ffe 100644 |
| --- a/chrome/installer/setup/setup_main.cc |
| +++ b/chrome/installer/setup/setup_main.cc |
| @@ -148,8 +148,10 @@ DWORD UnPackArchive(const FilePath& archive, |
| void AddExistingMultiInstalls(const InstallationState& original_state, |
| InstallerState* installer_state) { |
| if (installer_state->is_multi_install()) { |
| - for (size_t i = 0; i < BrowserDistribution::kNumProductTypes; ++i) { |
| - BrowserDistribution::Type type = BrowserDistribution::kProductTypes[i]; |
| + for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { |
| + BrowserDistribution::Type type = |
| + static_cast<BrowserDistribution::Type>(i); |
| + |
| if (!installer_state->FindProduct(type)) { |
| const ProductState* state = |
| original_state.GetProductState(installer_state->system_install(), |
| @@ -333,18 +335,20 @@ bool CheckGroupPolicySettings(const InstallationState& original_state, |
| #endif // defined(GOOGLE_CHROME_BUILD) |
| } |
| -// The supported multi-install modes are: |
| -// --multi-install --chrome --chrome-frame --ready-mode |
| +// If Chrome Frame is being installed by itself in multi-mode, non-ready-mode: |
| +// - If a non-multi Chrome Frame installation is present, fail. |
| +// If Chrome Frame is being installed by itself in multi-mode, ready-mode: |
| +// - If no Chrome installation is present, fail. |
| +// - If a Chrome installation is present, add it to the set of products to |
| +// install. |
| +// If Chrome Frame is being installed with Chrome in multi-mode, ready-mode: |
| // - If a non-multi Chrome Frame installation is present, Chrome Frame is |
| // removed from |installer_state|'s list of products (thereby preserving |
| // the existing SxS install). |
| // - If a multi Chrome Frame installation is present, its options are |
| // preserved (i.e., the --ready-mode command-line option is ignored). |
| -// --multi-install --chrome-frame |
| -// - If a non-multi Chrome Frame installation is present, fail. |
| -// - If --ready-mode and no Chrome installation is present, fail. |
| -// - If a Chrome installation is present, add it to the set of products to |
| -// install. |
| +// If any product is being installed in single-mode that already exists in |
| +// multi-mode, fail. |
| bool CheckMultiInstallConditions(const InstallationState& original_state, |
| InstallerState* installer_state, |
| installer::InstallStatus* status) { |
| @@ -356,13 +360,42 @@ bool CheckMultiInstallConditions(const InstallationState& original_state, |
| if (installer_state->is_multi_install()) { |
| const Product* chrome = |
| installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); |
| + const Product* app_host = |
| + installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST); |
| + const Product* binaries = |
| + installer_state->FindProduct(BrowserDistribution::CHROME_BINARIES); |
| const Product* chrome_frame = |
| installer_state->FindProduct(BrowserDistribution::CHROME_FRAME); |
| const ProductState* cf_state = |
| original_state.GetProductState(system_level, |
| BrowserDistribution::CHROME_FRAME); |
| - if (chrome != NULL) { |
| - if (chrome_frame != NULL && |
| + const ProductState* chrome_state = |
| + original_state.GetProductState(system_level, |
| + BrowserDistribution::CHROME_BROWSER); |
| + |
| + if (!binaries) { |
| + if (app_host && !chrome && !chrome_frame && !cf_state && !chrome_state) { |
| + DCHECK(!system_level); |
| + // App Host may use Chrome/Chrome binaries at system-level. |
| + if (original_state.GetProductState( |
| + true, // system |
| + BrowserDistribution::CHROME_BROWSER) || |
| + original_state.GetProductState( |
| + true, // system |
| + BrowserDistribution::CHROME_BINARIES)) { |
| + return true; |
| + } else { |
| + return false; |
| + } |
| + } else { |
| + // Every other scenario requires the binaries to be installed/updated |
| + // simultaneous to the main product. |
| + return false; |
| + } |
| + } |
| + |
| + if (chrome) { |
| + if (chrome_frame && |
| chrome_frame->HasOption(installer::kOptionReadyMode)) { |
| // We're being asked to install Chrome with Chrome Frame in ready-mode. |
| // This is an optimistic operation: if a SxS install of Chrome Frame |
| @@ -384,51 +417,83 @@ bool CheckMultiInstallConditions(const InstallationState& original_state, |
| VLOG(1) << "Performing initial install of Chrome Frame ready-mode."; |
| } |
| } |
| - } else if (chrome_frame != NULL) { |
| - // We're being asked to install or update Chrome Frame alone. |
| - const ProductState* chrome_state = |
| - original_state.GetProductState(system_level, |
| - BrowserDistribution::CHROME_BROWSER); |
| - if (chrome_state != NULL) { |
| - // Add Chrome to the set of products (making it multi-install in the |
| - // process) so that it is updated, too. |
| - scoped_ptr<Product> multi_chrome(new Product( |
| - BrowserDistribution::GetSpecificDistribution( |
| - BrowserDistribution::CHROME_BROWSER))); |
| - multi_chrome->SetOption(installer::kOptionMultiInstall, true); |
| - chrome = installer_state->AddProduct(&multi_chrome); |
| - VLOG(1) << "Upgrading existing multi-install Chrome browser along with " |
| - << chrome_frame->distribution()->GetAppShortCutName(); |
| - } else if (chrome_frame->HasOption(installer::kOptionReadyMode)) { |
| - // Chrome Frame with ready-mode is to be installed, yet Chrome is |
| - // neither installed nor being installed. Fail. |
| - LOG(ERROR) << "Cannot install Chrome Frame in ready mode without " |
| - "Chrome."; |
| - *status = installer::READY_MODE_REQUIRES_CHROME; |
| - installer_state->WriteInstallerResult(*status, |
| - IDS_INSTALL_READY_MODE_REQUIRES_CHROME_BASE, NULL); |
| - return false; |
| - } |
| + } else if (chrome_state != NULL) { |
| + // Chrome Frame is being installed in multi-install mode, and Chrome is |
| + // already present. Add Chrome to the set of products (making it |
| + // multi-install in the process) so that it is updated, too. |
| + scoped_ptr<Product> multi_chrome(new Product( |
| + BrowserDistribution::GetSpecificDistribution( |
| + BrowserDistribution::CHROME_BROWSER))); |
| + multi_chrome->SetOption(installer::kOptionMultiInstall, true); |
| + chrome = installer_state->AddProduct(&multi_chrome); |
| + VLOG(1) << "Upgrading existing Chrome browser in multi-install mode."; |
| + } else if (chrome_frame && |
| + chrome_frame->HasOption(installer::kOptionReadyMode)) { |
| + // Chrome Frame with ready-mode is to be installed, yet Chrome is |
| + // neither installed nor being installed. Fail. |
| + LOG(ERROR) << "Cannot install Chrome Frame in ready mode without Chrome."; |
| + *status = installer::READY_MODE_REQUIRES_CHROME; |
| + installer_state->WriteInstallerResult( |
| + *status, IDS_INSTALL_READY_MODE_REQUIRES_CHROME_BASE, NULL); |
| + return false; |
| } |
| // Fail if we're installing Chrome Frame when a single-install of it is |
| // already installed. |
| // TODO(grt): Add support for migration of Chrome Frame from single- to |
| // multi-install. |
| - if (chrome_frame != NULL && |
| - cf_state != NULL && !cf_state->is_multi_install()) { |
| + if (chrome_frame && cf_state && !cf_state->is_multi_install()) { |
| LOG(ERROR) << "Cannot migrate existing Chrome Frame installation to " |
| - "multi-install."; |
| + << "multi-install."; |
|
grt (UTC plus 2)
2012/07/23 11:31:12
nit: i prefer the previous code here since there's
|
| *status = installer::NON_MULTI_INSTALLATION_EXISTS; |
| installer_state->WriteInstallerResult(*status, |
| IDS_INSTALL_NON_MULTI_INSTALLATION_EXISTS_BASE, NULL); |
| return false; |
| } |
| - } else if (DCHECK_IS_ON()) { |
| + } else { |
| + // This is a non-multi installation. |
| + |
| // It isn't possible to stuff two products into a single-install |
| // InstallerState. Abort the process here in debug builds just in case |
| // someone finds a way. |
| DCHECK_EQ(1U, products.size()); |
| + if (products.size() != 1) |
| + return false; |
| + |
| + // Check for an existing installation of the product. |
| + const ProductState* product_state = original_state.GetProductState( |
| + system_level, products[0]->distribution()->GetType()); |
| + if (product_state != NULL) { |
| + // Block downgrades from multi-install to single-install. |
| + if (product_state->is_multi_install()) { |
| + LOG(ERROR) << "Multi-install " |
| + << products[0]->distribution()->GetAppShortCutName() |
| + << " exists; aborting single install."; |
| + *status = installer::MULTI_INSTALLATION_EXISTS; |
| + installer_state->WriteInstallerResult(*status, |
| + IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE, NULL); |
| + return false; |
| + } |
| + } |
| + |
| + } |
| + |
| + return true; |
| +} |
| + |
| +bool CheckAppHostPreconditions(const InstallationState& original_state, |
| + InstallerState* installer_state) { |
| + if (!installer_state->FindProduct(BrowserDistribution::CHROME_APP_HOST)) |
| + return true; |
| + |
| + if (!installer_state->is_multi_install()) { |
| + VLOG(1) << "Application Host may only be installed in multi-install mode."; |
| + return false; |
| + } |
| + |
| + if (installer_state->system_install()) { |
| + VLOG(1) << "Application Host may only be installed at user-level."; |
| + return false; |
| } |
| return true; |
| @@ -446,9 +511,11 @@ bool CheckMultiInstallConditions(const InstallationState& original_state, |
| bool CheckPreInstallConditions(const InstallationState& original_state, |
| InstallerState* installer_state, |
| installer::InstallStatus* status) { |
| - // See what products are already installed in multi mode. When we do multi |
| - // installs, we must upgrade all installations since they share the binaries. |
| - AddExistingMultiInstalls(original_state, installer_state); |
| + if (!CheckAppHostPreconditions(original_state, installer_state)) |
| + return false; |
| + |
| + if (!CheckMultiInstallConditions(original_state, installer_state, status)) |
| + return false; |
| const Products& products = installer_state->products(); |
| if (products.empty()) { |
| @@ -461,57 +528,47 @@ bool CheckPreInstallConditions(const InstallationState& original_state, |
| return false; |
| } |
| - if (!CheckMultiInstallConditions(original_state, installer_state, status)) |
| - return false; |
| + // See what products are already installed in multi mode. When we do multi |
| + // installs, we must upgrade all installations since they share the binaries. |
| + AddExistingMultiInstalls(original_state, installer_state); |
| - bool is_first_install = true; |
| - const bool system_level = installer_state->system_install(); |
| + if (!installer_state->system_install()) { |
| + // This is a user-level installation. Make sure that we are not installing |
| + // on top of an existing system-level installation. |
| + for (size_t i = 0; i < products.size(); ++i) { |
| + const Product* product = products[i]; |
| + BrowserDistribution* browser_dist = product->distribution(); |
| + const ProductState* user_level_product_state = |
| + original_state.GetProductState(false, browser_dist->GetType()); |
| + const ProductState* system_level_product_state = |
| + original_state.GetProductState(true, browser_dist->GetType()); |
| - for (size_t i = 0; i < products.size(); ++i) { |
| - const Product* product = products[i]; |
| - BrowserDistribution* browser_dist = product->distribution(); |
| + // Allow upgrades to proceed so that out-of-date versions are not left |
| + // around. |
| + if (user_level_product_state) |
| + continue; |
| - // Check for an existing installation of the product. |
| - const ProductState* product_state = |
| - original_state.GetProductState(system_level, browser_dist->GetType()); |
| - if (product_state != NULL) { |
| - is_first_install = false; |
| - // Block downgrades from multi-install to single-install. |
| - if (!installer_state->is_multi_install() && |
| - product_state->is_multi_install()) { |
| - LOG(ERROR) << "Multi-install " << browser_dist->GetAppShortCutName() |
| - << " exists; aborting single install."; |
| - *status = installer::MULTI_INSTALLATION_EXISTS; |
| - installer_state->WriteInstallerResult(*status, |
| - IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE, NULL); |
| - return false; |
| - } |
| - } |
| + // This is a new user-level install... |
| - // Check to avoid attempting to lay down a user-level installation on top |
| - // of a system-level one. |
| - const ProductState* other_state = |
| - original_state.GetProductState(!system_level, browser_dist->GetType()); |
| - if (other_state != NULL && !system_level) { |
| - if (is_first_install) { |
| - // This is a user-level install and there is a system-level install of |
| - // the product. |
| + if (system_level_product_state) { |
| + // ... and the product already exists at system-level. |
| LOG(ERROR) << "Already installed version " |
| - << other_state->version().GetString() |
| + << system_level_product_state->version().GetString() |
| << " at system-level conflicts with this one at user-level."; |
| if (product->is_chrome()) { |
| // Instruct Google Update to launch the existing system-level Chrome. |
| // There should be no error dialog. |
| - FilePath chrome_exe(installer::GetChromeInstallPath(!system_level, |
| - browser_dist)); |
| - if (chrome_exe.empty()) { |
| - // If we failed to construct install path. Give up. |
| + FilePath install_path(installer::GetChromeInstallPath(true, // system |
| + browser_dist)); |
| + if (install_path.empty()) { |
| + // Give up if we failed to construct the install path. |
| *status = installer::OS_ERROR; |
| installer_state->WriteInstallerResult(*status, |
| - IDS_INSTALL_OS_ERROR_BASE, NULL); |
| + IDS_INSTALL_OS_ERROR_BASE, |
| + NULL); |
| } else { |
| *status = installer::EXISTING_VERSION_LAUNCHED; |
| - chrome_exe = chrome_exe.Append(installer::kChromeExe); |
| + FilePath chrome_exe = install_path.Append(installer::kChromeExe); |
| CommandLine cmd(chrome_exe); |
| cmd.AppendSwitch(switches::kFirstRun); |
| installer_state->WriteInstallerResult(*status, 0, NULL); |
| @@ -519,31 +576,13 @@ bool CheckPreInstallConditions(const InstallationState& original_state, |
| base::LaunchProcess(cmd, base::LaunchOptions(), NULL); |
| } |
| } else { |
| - // Display an error message for Chrome Frame. |
| + // Display an error message for other products. |
| *status = installer::SYSTEM_LEVEL_INSTALL_EXISTS; |
| - installer_state->WriteInstallerResult(*status, |
| - IDS_INSTALL_SYSTEM_LEVEL_EXISTS_BASE, NULL); |
| + installer_state->WriteInstallerResult( |
| + *status, IDS_INSTALL_SYSTEM_LEVEL_EXISTS_BASE, NULL); |
| } |
| return false; |
| } |
| - // This is an update, not a new install. Allow it to take place so that |
| - // out-of-date versions are not left around. |
| - } |
| - } |
| - |
| - // If no previous installation of Chrome, make sure installation directory |
| - // either does not exist or can be deleted (i.e. is not locked by some other |
| - // process). |
| - if (is_first_install) { |
| - if (file_util::PathExists(installer_state->target_path()) && |
| - !file_util::Delete(installer_state->target_path(), true)) { |
| - LOG(ERROR) << "Installation directory " |
| - << installer_state->target_path().value() |
| - << " exists and can not be deleted."; |
| - *status = installer::INSTALL_DIR_IN_USE; |
| - int str_id = IDS_INSTALL_DIR_IN_USE_BASE; |
| - installer_state->WriteInstallerResult(*status, str_id, NULL); |
| - return false; |
| } |
| } |
| @@ -570,7 +609,7 @@ installer::InstallStatus InstallProductsHelper( |
| archive = cmd_line.GetSwitchValuePath( |
| installer::switches::kInstallArchive); |
| } |
| - VLOG(1) << "Archive found to install Chrome " << archive.value(); |
| + |
| const Products& products = installer_state.products(); |
| // Create a temp folder where we will unpack Chrome archive. If it fails, |
| @@ -586,13 +625,48 @@ installer::InstallStatus InstallProductsHelper( |
| VLOG(1) << "created path " << temp_path.path().value(); |
| FilePath unpack_path(temp_path.path().Append(installer::kInstallSourceDir)); |
| - if (UnPackArchive(archive, installer_state, temp_path.path(), unpack_path, |
| - archive_type)) { |
| - install_status = (*archive_type) == installer::INCREMENTAL_ARCHIVE_TYPE ? |
| - installer::APPLY_DIFF_PATCH_FAILED : installer::UNCOMPRESSION_FAILED; |
| - installer_state.WriteInstallerResult(install_status, |
| - IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, NULL); |
| + |
| + bool unpacked = false; |
| + |
| + // We want to keep uncompressed archive (chrome.7z) that we get after |
| + // uncompressing and binary patching. Get the location for this file. |
| + FilePath archive_to_copy; |
| + if (file_util::PathExists(archive)) { |
| + VLOG(1) << "Archive found to install Chrome " << archive.value(); |
| + if (UnPackArchive(archive, installer_state, temp_path.path(), unpack_path, |
| + archive_type)) { |
| + install_status = (*archive_type) == installer::INCREMENTAL_ARCHIVE_TYPE ? |
| + installer::APPLY_DIFF_PATCH_FAILED : installer::UNCOMPRESSION_FAILED; |
| + installer_state.WriteInstallerResult( |
| + install_status, |
| + IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, |
| + NULL); |
| + } else { |
| + unpacked = true; |
| + archive_to_copy = temp_path.path().Append(installer::kChromeArchive); |
| + } |
| } else { |
| + FilePath uncompressed_archive(cmd_line.GetProgram().DirName().Append( |
| + installer::kChromeArchive)); |
| + |
| + if (file_util::PathExists(uncompressed_archive)) { |
| + VLOG(1) << "Uncompressed archive found to install Chrome " |
| + << uncompressed_archive.value(); |
| + *archive_type = installer::FULL_ARCHIVE_TYPE; |
| + string16 unpacked_file; |
| + if (LzmaUtil::UnPackArchive(uncompressed_archive.value(), |
| + unpack_path.value(), &unpacked_file)) { |
| + installer_state.WriteInstallerResult( |
| + installer::UNCOMPRESSION_FAILED, |
| + IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, |
| + NULL); |
| + } else { |
| + unpacked = true; |
| + archive_to_copy = uncompressed_archive; |
| + } |
| + } |
| + } |
| + if (unpacked) { |
| VLOG(1) << "unpacked to " << unpack_path.value(); |
| FilePath src_path(unpack_path.Append(installer::kInstallSourceChromeDir)); |
| scoped_ptr<Version> |
| @@ -624,22 +698,28 @@ installer::InstallStatus InstallProductsHelper( |
| } |
| if (higher_products != 0) { |
| - COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 3, |
| + COMPILE_ASSERT(BrowserDistribution::NUM_TYPES == 4, |
| add_support_for_new_products_here_); |
| const uint32 kBrowserBit = 1 << BrowserDistribution::CHROME_BROWSER; |
| const uint32 kGCFBit = 1 << BrowserDistribution::CHROME_FRAME; |
| + const uint32 kAppHostBit = 1 << BrowserDistribution::CHROME_APP_HOST; |
| int message_id = 0; |
| proceed_with_installation = false; |
| install_status = installer::HIGHER_VERSION_EXISTS; |
| - if ((higher_products & kBrowserBit) != 0) { |
| - if ((higher_products & kGCFBit) != 0) |
| - message_id = IDS_INSTALL_HIGHER_VERSION_CB_CF_BASE; |
| - else |
| + switch (higher_products) { |
| + case kBrowserBit: |
| message_id = IDS_INSTALL_HIGHER_VERSION_BASE; |
| - } else { |
| - DCHECK(higher_products == kGCFBit); |
| - message_id = IDS_INSTALL_HIGHER_VERSION_CF_BASE; |
| + break; |
| + case kGCFBit: |
| + message_id = IDS_INSTALL_HIGHER_VERSION_CF_BASE; |
| + break; |
| + case kGCFBit | kBrowserBit: |
| + message_id = IDS_INSTALL_HIGHER_VERSION_CB_CF_BASE; |
| + break; |
| + default: |
| + message_id = IDS_INSTALL_HIGHER_VERSION_APP_HOST_BASE; |
| + break; |
| } |
| installer_state.WriteInstallerResult(install_status, message_id, NULL); |
| @@ -651,10 +731,6 @@ installer::InstallStatus InstallProductsHelper( |
| *installer_version, &install_status); |
| if (proceed_with_installation) { |
| - // We want to keep uncompressed archive (chrome.7z) that we get after |
| - // uncompressing and binary patching. Get the location for this file. |
| - FilePath archive_to_copy( |
| - temp_path.path().Append(installer::kChromeArchive)); |
| FilePath prefs_source_path(cmd_line.GetSwitchValueNative( |
| installer::switches::kInstallerData)); |
| install_status = installer::InstallOrUpdateProduct(original_state, |
| @@ -879,11 +955,20 @@ installer::InstallStatus UninstallProducts( |
| const InstallerState& installer_state, |
| const CommandLine& cmd_line) { |
| const Products& products = installer_state.products(); |
| - // InstallerState::Initialize always puts Chrome first, and we rely on that |
| - // here for this reason: if Chrome is in-use, the user will be prompted to |
| - // confirm uninstallation. Upon cancel, we should not continue with the |
| - // other products. |
| - DCHECK(products.size() < 2 || products[0]->is_chrome()); |
| + |
| + if (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER)) { |
| + // InstallerState::Initialize always puts Chrome first, and we rely on that |
| + // here for this reason: if Chrome is in-use, the user will be prompted to |
| + // confirm uninstallation. Upon cancel, we should not continue with the |
| + // other products. |
| + DCHECK(products[0]->is_chrome()); |
| + } |
| + if (installer_state.FindProduct(BrowserDistribution::CHROME_BINARIES)) { |
| + // Chrome Binaries should be last; if something else is cancelled, they |
| + // should stay. |
| + DCHECK(products[products.size() - 1]->is_chrome_binaries()); |
| + } |
| + |
| installer::InstallStatus install_status = installer::UNINSTALL_SUCCESSFUL; |
| installer::InstallStatus prod_status = installer::UNKNOWN_STATUS; |
| const bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall); |