From f86aa4f31692f064b1690a114058a0b86d4aa737 Mon Sep 17 00:00:00 2001 From: "grt@chromium.org" Date: Sat, 21 Sep 2013 16:44:25 +0000 Subject: [PATCH] Invoke the CF migration path when applying a CF multi update. Moves MigrateChromeFrame (without changing its logic) from setup_main.cc into cf_migration.cc. Contributed by robertshield@chromium.org (originally from https://codereview.chromium.org/22632009/). BUG=265952 TEST=A multi Chrome Frame install receiving an update will change to a SxS Chrome Frame install. And will still work. Review URL: https://chromiumcodereview.appspot.com/24287006 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@224600 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/chrome_installer.gypi | 2 + chrome/installer/setup/cf_migration.cc | 131 +++++++++++++ chrome/installer/setup/cf_migration.h | 45 +++++ chrome/installer/setup/setup_main.cc | 185 ++++++++---------- chrome/installer/setup/setup_main.h | 23 ++- chrome/installer/util/channel_info.cc | 13 +- chrome/installer/util/channel_info.h | 7 + .../installer/util/channel_info_unittest.cc | 34 +++- 8 files changed, 328 insertions(+), 112 deletions(-) create mode 100644 chrome/installer/setup/cf_migration.cc create mode 100644 chrome/installer/setup/cf_migration.h diff --git a/chrome/chrome_installer.gypi b/chrome/chrome_installer.gypi index fd405ce9924529..4f1309415103e3 100644 --- a/chrome/chrome_installer.gypi +++ b/chrome/chrome_installer.gypi @@ -270,6 +270,8 @@ 'installer/mini_installer/chrome.release', 'installer/setup/archive_patch_helper.cc', 'installer/setup/archive_patch_helper.h', + 'installer/setup/cf_migration.cc', + 'installer/setup/cf_migration.h', 'installer/setup/chrome_frame_quick_enable.cc', 'installer/setup/chrome_frame_quick_enable.h', 'installer/setup/chrome_frame_ready_mode.cc', diff --git a/chrome/installer/setup/cf_migration.cc b/chrome/installer/setup/cf_migration.cc new file mode 100644 index 00000000000000..313f2ce31778ff --- /dev/null +++ b/chrome/installer/setup/cf_migration.cc @@ -0,0 +1,131 @@ +// Copyright 2013 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/setup/cf_migration.h" + +#include "base/command_line.h" +#include "base/files/file_path.h" +#include "base/logging.h" +#include "base/process/launch.h" +#include "base/win/registry.h" +#include "chrome/installer/setup/setup_constants.h" +#include "chrome/installer/setup/setup_main.h" +#include "chrome/installer/setup/setup_util.h" +#include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/installation_state.h" +#include "chrome/installer/util/installer_state.h" +#include "chrome/installer/util/master_preferences.h" +#include "chrome/installer/util/util_constants.h" + +namespace installer { + +bool LaunchChromeFrameMigrationProcess( + const ProductState& chrome_frame_product, + const CommandLine& command_line, + const base::FilePath& installer_directory, + bool system_level) { + // Before running the migration, mutate the CF ap value to include a + // "-migrate" beacon. This beacon value will be cleaned up by the "ap" + // cleanup in MigrateGoogleUpdateStateMultiToSingle that calls + // ChannelInfo::RemoveAllModifiersAndSuffixes(). + if (chrome_frame_product.is_multi_install()) { + const HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + base::win::RegKey state_key; + installer::ChannelInfo channel_info; + BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_FRAME); + + LONG result = state_key.Open(root, dist->GetStateKey().c_str(), + KEY_QUERY_VALUE | KEY_SET_VALUE); + if (result != ERROR_SUCCESS || !channel_info.Initialize(state_key)) { + LOG(ERROR) << "Failed to read CF channel to store beacon."; + } else if (!channel_info.SetMigratingSuffix(true)) { + LOG(WARNING) << "CF already has migration beacon in channel."; + } else { + VLOG(1) << "Writing CF migration beacon to channel: " + << channel_info.value(); + channel_info.Write(&state_key); + } + } + + // Call the installed setup.exe using the current installer command line and + // adding the migration flags. This seems like it could be unsafe, here's why + // it's safe today: + // 1) MigrateChromeFrameInChildProcess is called only during a multi update. + // 2) Multi update processing occurs after HandleNonInstallCmdLineOptions is + // called. + // 3) Setup exits if there were any non-install command line options handled. + // 4) Thus, the command line being copied will have no non-install command + // line options at time of copying. + // 5) kMigrateChromeFrame is a non-install command line option. + // 6) Thus, it will be handled (and the child setup process will exit) before + // the child setup process acts on any other flags on the command line. + // 7) Furthermore, --uncompressed-archive takes precedence over + // --install-archive, so it is safe to add the former to the command line + // without removing the latter. + CommandLine setup_cmd(command_line); + setup_cmd.SetProgram(installer_directory.Append(installer::kSetupExe)); + setup_cmd.AppendSwitchPath( + switches::kUncompressedArchive, + installer_directory.Append(installer::kChromeArchive)); + setup_cmd.AppendSwitch(switches::kMigrateChromeFrame); + + VLOG(1) << "Running Chrome Frame migration process with command line: " + << setup_cmd.GetCommandLineString(); + + base::LaunchOptions options; + options.force_breakaway_from_job_ = true; + if (!base::LaunchProcess(setup_cmd, options, NULL)) { + PLOG(ERROR) << "Launching Chrome Frame migration process failed. " + << "(Command line: " << setup_cmd.GetCommandLineString() << ")"; + return false; + } + + return true; +} + +InstallStatus MigrateChromeFrame(const InstallationState& original_state, + InstallerState* installer_state) { + const bool system_level = installer_state->system_install(); + + // Nothing to do if multi-install Chrome Frame is not installed. + const ProductState* multi_chrome_frame = original_state.GetProductState( + system_level, BrowserDistribution::CHROME_FRAME); + if (!multi_chrome_frame || !multi_chrome_frame->is_multi_install()) + return INVALID_STATE_FOR_OPTION; + + // Install SxS Chrome Frame. + InstallerState install_gcf(installer_state->level()); + { + scoped_ptr chrome_frame( + new Product(BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_FRAME))); + install_gcf.AddProduct(&chrome_frame); + } + DCHECK(!install_gcf.is_multi_install()); + + ArchiveType archive_type = UNKNOWN_ARCHIVE_TYPE; + bool delegated_to_existing = false; + InstallStatus install_status = InstallProductsHelper( + original_state, *CommandLine::ForCurrentProcess(), + MasterPreferences::ForCurrentProcess(), install_gcf, + NULL, &archive_type, &delegated_to_existing); + + if (!InstallUtil::GetInstallReturnCode(install_status)) { + // Migration was successful. There's no turning back now. The multi-install + // npchrome_frame.dll and/or chrome.exe may still be in use at this point, + // although the user-level helper will not be. It is not safe to delete the + // multi-install binaries until npchrome_frame.dll and chrome.exe are no + // longer in use. The remaining tasks here are best-effort. Failure does not + // do any harm. + MigrateGoogleUpdateStateMultiToSingle(system_level, + BrowserDistribution::CHROME_FRAME, + original_state); + } + + return install_status; +} + +} // namespace installer diff --git a/chrome/installer/setup/cf_migration.h b/chrome/installer/setup/cf_migration.h new file mode 100644 index 00000000000000..976e54464a260c --- /dev/null +++ b/chrome/installer/setup/cf_migration.h @@ -0,0 +1,45 @@ +// Copyright 2013 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_SETUP_CF_MIGRATION_H_ +#define CHROME_INSTALLER_SETUP_CF_MIGRATION_H_ + +#include "chrome/installer/util/util_constants.h" + +class CommandLine; + +namespace base { +class FilePath; +} + +namespace installer { + +class InstallationState; +class InstallerState; +class ProductState; + +// Invokes a child helper instance of the setup.exe in |installer_directory| to +// run MigrateChromeFrame (see comments below) using the archive in +// |installer_directory| for the given installation level. Returns true if the +// process is launched. +bool LaunchChromeFrameMigrationProcess( + const ProductState& chrome_frame_product, + const CommandLine& command_line, + const base::FilePath& installer_directory, + bool system_level); + +// Migrates multi-install Chrome Frame to single-install at the current +// level. Does not remove the multi-install binaries if no other products are +// using them. --uncompressed-archive=chrome.7z is expected to be given on the +// command line to point this setup.exe at the (possibly patched) archive from +// the calling instance. +// Note about process model: this is called in a child setup.exe that is +// invoked from the setup.exe instance run as part of an update to a +// multi-install Chrome Frame. +InstallStatus MigrateChromeFrame(const InstallationState& original_state, + InstallerState* installer_state); + +} // namespace installer + +#endif // CHROME_INSTALLER_SETUP_CF_MIGRATION_H_ diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index 171ac7c7c481a6..a5ebb52b72e774 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc @@ -36,6 +36,7 @@ #include "chrome/common/chrome_constants.h" #include "chrome/common/chrome_switches.h" #include "chrome/installer/setup/archive_patch_helper.h" +#include "chrome/installer/setup/cf_migration.h" #include "chrome/installer/setup/chrome_frame_quick_enable.h" #include "chrome/installer/setup/chrome_frame_ready_mode.h" #include "chrome/installer/setup/install.h" @@ -71,10 +72,10 @@ using installer::InstallerState; using installer::InstallationState; using installer::InstallationValidator; +using installer::MasterPreferences; using installer::Product; using installer::ProductState; using installer::Products; -using installer::MasterPreferences; const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; const wchar_t kGoogleUpdatePipeName[] = L"\\\\.\\pipe\\GoogleCrashServices\\"; @@ -691,7 +692,8 @@ installer::InstallStatus InstallProducts( const InstallationState& original_state, const CommandLine& cmd_line, const MasterPreferences& prefs, - InstallerState* installer_state) { + InstallerState* installer_state, + base::FilePath* installer_directory) { DCHECK(installer_state); const bool system_install = installer_state->system_install(); installer::InstallStatus install_status = installer::UNKNOWN_STATUS; @@ -706,8 +708,8 @@ installer::InstallStatus InstallProducts( &install_status)) { VLOG(1) << "Installing to " << installer_state->target_path().value(); install_status = InstallProductsHelper( - original_state, cmd_line, prefs, *installer_state, &archive_type, - &delegated_to_existing); + original_state, cmd_line, prefs, *installer_state, + installer_directory, &archive_type, &delegated_to_existing); } else { // CheckPreInstallConditions must set the status on failure. DCHECK_NE(install_status, installer::UNKNOWN_STATUS); @@ -981,55 +983,6 @@ installer::InstallStatus RegisterDevChrome( return status; } -// Migrates multi-install Chrome Frame to single-install at the current -// level. Does not remove the multi-install binaries if no other products are -// using them. --uncompressed-archive=chrome.7z is expected to be given on the -// command line to point this setup.exe at the (possibly patched) archive from -// the calling instance. -installer::InstallStatus MigrateChromeFrame( - const InstallationState& original_state, - InstallerState* installer_state) { - const bool system_level = installer_state->system_install(); - - // Nothing to do if multi-install Chrome Frame is not installed. - const ProductState* multi_chrome_frame = original_state.GetProductState( - system_level, BrowserDistribution::CHROME_FRAME); - if (!multi_chrome_frame || !multi_chrome_frame->is_multi_install()) - return installer::INVALID_STATE_FOR_OPTION; - - // Install SxS Chrome Frame. - InstallerState install_gcf(installer_state->level()); - { - scoped_ptr chrome_frame( - new Product(BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_FRAME))); - install_gcf.AddProduct(&chrome_frame); - } - DCHECK(!install_gcf.is_multi_install()); - - installer::ArchiveType archive_type = installer::UNKNOWN_ARCHIVE_TYPE; - bool delegated_to_existing = false; - installer::InstallStatus install_status = InstallProductsHelper( - original_state, *CommandLine::ForCurrentProcess(), - MasterPreferences::ForCurrentProcess(), install_gcf, - &archive_type, &delegated_to_existing); - - if (!InstallUtil::GetInstallReturnCode(install_status)) { - // Migration was successful. There's no turning back now. The multi-install - // npchrome_frame.dll and/or chrome.exe may still be in use at this point, - // although the user-level helper will not be. It is not safe to delete the - // multi-install binaries until npchrome_frame.dll and chrome.exe are no - // longer in use. The remaining tasks here are best-effort. Failure does not - // do any harm. - installer::MigrateGoogleUpdateStateMultiToSingle( - system_level, - BrowserDistribution::CHROME_FRAME, - original_state); - } - - return install_status; -} - // This method processes any command line options that make setup.exe do // various tasks other than installation (renaming chrome.exe, showing eula // among others). This function returns true if any such command line option @@ -1401,42 +1354,43 @@ google_breakpad::ExceptionHandler* InitializeCrashReporting( namespace installer { -installer::InstallStatus InstallProductsHelper( +InstallStatus InstallProductsHelper( const InstallationState& original_state, const CommandLine& cmd_line, const MasterPreferences& prefs, const InstallerState& installer_state, - installer::ArchiveType* archive_type, + base::FilePath* installer_directory, + ArchiveType* archive_type, bool* delegated_to_existing) { DCHECK(archive_type); DCHECK(delegated_to_existing); const bool system_install = installer_state.system_install(); - installer::InstallStatus install_status = installer::UNKNOWN_STATUS; + InstallStatus install_status = UNKNOWN_STATUS; // Drop to background processing mode if the process was started below the // normal process priority class. - bool entered_background_mode = installer::AdjustProcessPriority(); + bool entered_background_mode = AdjustProcessPriority(); VLOG_IF(1, entered_background_mode) << "Entered background processing mode."; // Create a temp folder where we will unpack Chrome archive. If it fails, // then we are doomed, so return immediately and no cleanup is required. - installer::SelfCleaningTempDir temp_path; + SelfCleaningTempDir temp_path; base::FilePath unpack_path; if (!CreateTemporaryAndUnpackDirectories(installer_state, &temp_path, &unpack_path)) { - installer_state.WriteInstallerResult(installer::TEMP_DIR_FAILED, + installer_state.WriteInstallerResult(TEMP_DIR_FAILED, IDS_INSTALL_TEMP_DIR_FAILED_BASE, NULL); - return installer::TEMP_DIR_FAILED; + return TEMP_DIR_FAILED; } // Uncompress and optionally patch the archive if an uncompressed archive was // not specified on the command line and a compressed archive is found. - *archive_type = installer::UNKNOWN_ARCHIVE_TYPE; + *archive_type = UNKNOWN_ARCHIVE_TYPE; base::FilePath uncompressed_archive(cmd_line.GetSwitchValuePath( - installer::switches::kUncompressedArchive)); + switches::kUncompressedArchive)); if (uncompressed_archive.empty()) { - scoped_ptr archive_helper( + scoped_ptr archive_helper( CreateChromeArchiveHelper(cmd_line, installer_state, unpack_path)); if (archive_helper) { VLOG(1) << "Installing Chrome from compressed archive " @@ -1446,7 +1400,7 @@ installer::InstallStatus InstallProductsHelper( archive_helper.get(), archive_type, &install_status)) { - DCHECK_NE(install_status, installer::UNKNOWN_STATUS); + DCHECK_NE(install_status, UNKNOWN_STATUS); return install_status; } uncompressed_archive = archive_helper->target(); @@ -1458,19 +1412,19 @@ installer::InstallStatus InstallProductsHelper( // was not given or generated. if (uncompressed_archive.empty()) { uncompressed_archive = - cmd_line.GetProgram().DirName().Append(installer::kChromeArchive); + cmd_line.GetProgram().DirName().Append(kChromeArchive); } - if (*archive_type == installer::UNKNOWN_ARCHIVE_TYPE) { + if (*archive_type == UNKNOWN_ARCHIVE_TYPE) { // An archive was not uncompressed or patched above. if (uncompressed_archive.empty() || !base::PathExists(uncompressed_archive)) { LOG(ERROR) << "Cannot install Chrome without an uncompressed archive."; installer_state.WriteInstallerResult( - installer::INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); - return installer::INVALID_ARCHIVE; + INVALID_ARCHIVE, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); + return INVALID_ARCHIVE; } - *archive_type = installer::FULL_ARCHIVE_TYPE; + *archive_type = FULL_ARCHIVE_TYPE; } // Unpack the uncompressed archive. @@ -1478,20 +1432,20 @@ installer::InstallStatus InstallProductsHelper( unpack_path.value(), NULL)) { installer_state.WriteInstallerResult( - installer::UNCOMPRESSION_FAILED, + UNCOMPRESSION_FAILED, IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, NULL); - return installer::UNCOMPRESSION_FAILED; + return UNCOMPRESSION_FAILED; } VLOG(1) << "unpacked to " << unpack_path.value(); base::FilePath src_path( - unpack_path.Append(installer::kInstallSourceChromeDir)); + unpack_path.Append(kInstallSourceChromeDir)); scoped_ptr - installer_version(installer::GetMaxVersionFromArchiveDir(src_path)); + installer_version(GetMaxVersionFromArchiveDir(src_path)); if (!installer_version.get()) { LOG(ERROR) << "Did not find any valid version in installer."; - install_status = installer::INVALID_ARCHIVE; + install_status = INVALID_ARCHIVE; installer_state.WriteInstallerResult(install_status, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); } else { @@ -1508,7 +1462,7 @@ installer::InstallStatus InstallProductsHelper( if (GetExistingHigherInstaller(original_state, system_install, *installer_version, &setup_exe)) { VLOG(1) << "Deferring to existing installer."; - installer_state.UpdateStage(installer::DEFERRING_TO_HIGHER_VERSION); + installer_state.UpdateStage(DEFERRING_TO_HIGHER_VERSION); if (DeferToExistingInstall(setup_exe, cmd_line, installer_state, temp_path.path(), &install_status)) { *delegated_to_existing = true; @@ -1546,7 +1500,7 @@ installer::InstallStatus InstallProductsHelper( int message_id = 0; proceed_with_installation = false; - install_status = installer::HIGHER_VERSION_EXISTS; + install_status = HIGHER_VERSION_EXISTS; switch (higher_products) { case kBrowserBit: message_id = IDS_INSTALL_HIGHER_VERSION_BASE; @@ -1579,16 +1533,17 @@ installer::InstallStatus InstallProductsHelper( if (!google_update::EnsureUserLevelGoogleUpdatePresent()) { LOG(ERROR) << "Failed to install Google Update"; proceed_with_installation = false; - install_status = installer::INSTALL_OF_GOOGLE_UPDATE_FAILED; + install_status = INSTALL_OF_GOOGLE_UPDATE_FAILED; installer_state.WriteInstallerResult(install_status, 0, NULL); } } + } if (proceed_with_installation) { base::FilePath prefs_source_path(cmd_line.GetSwitchValueNative( - installer::switches::kInstallerData)); - install_status = installer::InstallOrUpdateProduct( + switches::kInstallerData)); + install_status = InstallOrUpdateProduct( original_state, installer_state, cmd_line.GetProgram(), uncompressed_archive, temp_path.path(), src_path, prefs_source_path, prefs, *installer_version); @@ -1596,27 +1551,26 @@ installer::InstallStatus InstallProductsHelper( int install_msg_base = IDS_INSTALL_FAILED_BASE; string16 chrome_exe; string16 quoted_chrome_exe; - if (install_status == installer::SAME_VERSION_REPAIR_FAILED) { + if (install_status == SAME_VERSION_REPAIR_FAILED) { if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_CF_BASE; } else { install_msg_base = IDS_SAME_VERSION_REPAIR_FAILED_BASE; } - } else if (install_status != installer::INSTALL_FAILED) { + } else if (install_status != INSTALL_FAILED) { if (installer_state.target_path().empty()) { // If we failed to construct install path, it means the OS call to // get %ProgramFiles% or %AppData% failed. Report this as failure. install_msg_base = IDS_INSTALL_OS_ERROR_BASE; - install_status = installer::OS_ERROR; + install_status = OS_ERROR; } else { - chrome_exe = installer_state.target_path() - .Append(installer::kChromeExe).value(); + chrome_exe = installer_state.target_path().Append(kChromeExe).value(); quoted_chrome_exe = L"\"" + chrome_exe + L"\""; install_msg_base = 0; } } - installer_state.UpdateStage(installer::FINISHING); + installer_state.UpdateStage(FINISHING); // Only do Chrome-specific stuff (like launching the browser) if // Chrome was specifically requested (rather than being upgraded as @@ -1627,48 +1581,45 @@ installer::InstallStatus InstallProductsHelper( bool do_not_register_for_update_launch = false; if (chrome_install) { - prefs.GetBool( - installer::master_preferences::kDoNotRegisterForUpdateLaunch, - &do_not_register_for_update_launch); + prefs.GetBool(master_preferences::kDoNotRegisterForUpdateLaunch, + &do_not_register_for_update_launch); } else { do_not_register_for_update_launch = true; // Never register. } bool write_chrome_launch_string = (!do_not_register_for_update_launch && - install_status != installer::IN_USE_UPDATED); + install_status != IN_USE_UPDATED); installer_state.WriteInstallerResult(install_status, install_msg_base, write_chrome_launch_string ? "ed_chrome_exe : NULL); - if (install_status == installer::FIRST_INSTALL_SUCCESS) { + if (install_status == FIRST_INSTALL_SUCCESS) { VLOG(1) << "First install successful."; if (chrome_install) { // We never want to launch Chrome in system level install mode. bool do_not_launch_chrome = false; - prefs.GetBool( - installer::master_preferences::kDoNotLaunchChrome, - &do_not_launch_chrome); + prefs.GetBool(master_preferences::kDoNotLaunchChrome, + &do_not_launch_chrome); if (!system_install && !do_not_launch_chrome) chrome_install->LaunchChrome(installer_state.target_path()); } - } else if ((install_status == installer::NEW_VERSION_UPDATED) || - (install_status == installer::IN_USE_UPDATED)) { + } else if ((install_status == NEW_VERSION_UPDATED) || + (install_status == IN_USE_UPDATED)) { const Product* chrome = installer_state.FindProduct( BrowserDistribution::CHROME_BROWSER); if (chrome != NULL) { DCHECK_NE(chrome_exe, string16()); - installer::RemoveChromeLegacyRegistryKeys(chrome->distribution(), - chrome_exe); + RemoveChromeLegacyRegistryKeys(chrome->distribution(), chrome_exe); } } if (prefs.install_chrome_app_launcher() && InstallUtil::GetInstallReturnCode(install_status) == 0) { std::string webstore_item(google_update::GetUntrustedDataValue( - installer::kInstallFromWebstore)); + kInstallFromWebstore)); if (!webstore_item.empty()) { - bool success = installer::InstallFromWebstore(webstore_item); + bool success = InstallFromWebstore(webstore_item); VLOG_IF(1, !success) << "Failed to launch app installation."; } } @@ -1699,6 +1650,15 @@ installer::InstallStatus InstallProductsHelper( } } + // If installation completed successfully, return the path to the directory + // containing the newly installed setup.exe and uncompressed archive if the + // caller requested it. + if (installer_directory && + InstallUtil::GetInstallReturnCode(install_status) == 0) { + *installer_directory = + installer_state.GetInstallerDirectory(*installer_version); + } + // temp_path's dtor will take care of deleting or scheduling itself for // deletion at reboot when this scope closes. VLOG(1) << "Deleting temporary directory " << temp_path.path().value(); @@ -1798,6 +1758,7 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, } } + base::FilePath installer_directory; installer::InstallStatus install_status = installer::UNKNOWN_STATUS; // If --uninstall option is given, uninstall the identified product(s) if (is_uninstall) { @@ -1806,7 +1767,8 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, } else { // If --uninstall option is not specified, we assume it is install case. install_status = - InstallProducts(original_state, cmd_line, prefs, &installer_state); + InstallProducts(original_state, cmd_line, prefs, &installer_state, + &installer_directory); } // Validate that the machine is now in a good state following the operation. @@ -1820,7 +1782,6 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, const Product* cf_install = installer_state.FindProduct(BrowserDistribution::CHROME_FRAME); - if (cf_install && !cmd_line.HasSwitch(installer::switches::kForceUninstall)) { if (install_status == installer::UNINSTALL_REQUIRES_REBOOT) { @@ -1849,11 +1810,31 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, // MSI demands that custom actions always return 0 (ERROR_SUCCESS) or it will // rollback the action. If we're uninstalling we want to avoid this, so always // report success, squashing any more informative return codes. - if (!(installer_state.is_msi() && is_uninstall)) + if (!(installer_state.is_msi() && is_uninstall)) { // Note that we allow the status installer::UNINSTALL_REQUIRES_REBOOT // to pass through, since this is only returned on uninstall which is // never invoked directly by Google Update. return_code = InstallUtil::GetInstallReturnCode(install_status); + } + + // Reinitialize original_state to make sure it reflects the now-current + // state of the system. + original_state.Initialize(); + + // If multi Chrome Frame was just updated, migrate the installation to a SxS + // install. Do this right before quitting. + const ProductState* chrome_frame_state = + original_state.GetProductState(system_install, + BrowserDistribution::CHROME_FRAME); + if ((install_status == installer::NEW_VERSION_UPDATED || + install_status == installer::IN_USE_UPDATED) && + chrome_frame_state && + installer_state.operation() == InstallerState::MULTI_UPDATE) { + // Call the newly updated setup.exe with kUncompressedArchive and + // kMigrateChromeFrame to perform the migration. + LaunchChromeFrameMigrationProcess(*chrome_frame_state, cmd_line, + installer_directory, system_install); + } VLOG(1) << "Installation complete, returning: " << return_code; diff --git a/chrome/installer/setup/setup_main.h b/chrome/installer/setup/setup_main.h index f39e742de0fca4..f87509cf684978 100644 --- a/chrome/installer/setup/setup_main.h +++ b/chrome/installer/setup/setup_main.h @@ -12,20 +12,27 @@ class CommandLine; -namespace installer { +namespace base { +class FilePath; +} // namespace base +namespace installer { class InstallationState; class InstallerState; class MasterPreferences; // Helper function that performs the installation of a set of products. -installer::InstallStatus InstallProductsHelper( - const installer::InstallationState& original_state, - const CommandLine& cmd_line, - const installer::MasterPreferences& prefs, - const installer::InstallerState& installer_state, - installer::ArchiveType* archive_type, - bool* delegated_to_existing); +// |installer_directory|, if non-NULL, is populated with the path to the +// directory containing the newly installed setup.exe. |archive_type| is +// populated with the type of archive found. |delegated_to_existing| is set to +// |true| if installation was delegated to a pre-existing higher version. +InstallStatus InstallProductsHelper(const InstallationState& original_state, + const CommandLine& cmd_line, + const MasterPreferences& prefs, + const InstallerState& installer_state, + base::FilePath* installer_directory, + ArchiveType* archive_type, + bool* delegated_to_existing); } // namespace installer diff --git a/chrome/installer/util/channel_info.cc b/chrome/installer/util/channel_info.cc index e52ae77afeab62..48a4e321272631 100644 --- a/chrome/installer/util/channel_info.cc +++ b/chrome/installer/util/channel_info.cc @@ -22,6 +22,7 @@ const wchar_t kModMultiInstall[] = L"-multi"; const wchar_t kModReadyMode[] = L"-readymode"; const wchar_t kModStage[] = L"-stage:"; const wchar_t kSfxFull[] = L"-full"; +const wchar_t kSfxMigrating[] = L"-migrating"; const wchar_t kSfxMultiFail[] = L"-multifail"; const wchar_t* const kChannels[] = { @@ -38,7 +39,8 @@ const wchar_t* const kModifiers[] = { kModAppLauncher, kModReadyMode, kSfxMultiFail, - kSfxFull + kSfxMigrating, + kSfxFull, }; enum ModifierIndex { @@ -50,6 +52,7 @@ enum ModifierIndex { MOD_APP_LAUNCHER, MOD_READY_MODE, SFX_MULTI_FAIL, + SFX_MIGRATING, SFX_FULL, NUM_MODIFIERS }; @@ -281,6 +284,14 @@ bool ChannelInfo::SetMultiFailSuffix(bool value) { return SetModifier(SFX_MULTI_FAIL, value, &value_); } +bool ChannelInfo::SetMigratingSuffix(bool value) { + return SetModifier(SFX_MIGRATING, value, &value_); +} + +bool ChannelInfo::HasMigratingSuffix() const { + return HasModifier(SFX_MIGRATING, value_); +} + bool ChannelInfo::RemoveAllModifiersAndSuffixes() { bool modified = false; diff --git a/chrome/installer/util/channel_info.h b/chrome/installer/util/channel_info.h index 438966f24702ac..f14811390af910 100644 --- a/chrome/installer/util/channel_info.h +++ b/chrome/installer/util/channel_info.h @@ -101,6 +101,13 @@ class ChannelInfo { // modified. bool SetMultiFailSuffix(bool value); + // Adds or removes the -migrating suffix, returning true if the value is + // modified. + bool SetMigratingSuffix(bool value); + + // Returns true if the -migrating suffix is present in the value. + bool HasMigratingSuffix() const; + // Removes all modifiers and suffixes. For example, 2.0-dev-multi-chrome-full // becomes 2.0-dev. Returns true if the value is modified. bool RemoveAllModifiersAndSuffixes(); diff --git a/chrome/installer/util/channel_info_unittest.cc b/chrome/installer/util/channel_info_unittest.cc index 1aacb24730d40a..d08f2c31049de4 100644 --- a/chrome/installer/util/channel_info_unittest.cc +++ b/chrome/installer/util/channel_info_unittest.cc @@ -118,6 +118,38 @@ TEST(ChannelInfoTest, MultiInstall) { EXPECT_EQ(L"2.0-beta", ci.value()); } +TEST(ChannelInfoTest, Migration) { + ChannelInfo ci; + + ci.set_value(L""); + EXPECT_TRUE(ci.SetMigratingSuffix(true)); + EXPECT_TRUE(ci.HasMigratingSuffix()); + EXPECT_EQ(L"-migrating", ci.value()); + EXPECT_FALSE(ci.SetMigratingSuffix(true)); + EXPECT_TRUE(ci.HasMigratingSuffix()); + EXPECT_EQ(L"-migrating", ci.value()); + EXPECT_TRUE(ci.SetMigratingSuffix(false)); + EXPECT_FALSE(ci.HasMigratingSuffix()); + EXPECT_EQ(L"", ci.value()); + EXPECT_FALSE(ci.SetMigratingSuffix(false)); + EXPECT_FALSE(ci.HasMigratingSuffix()); + EXPECT_EQ(L"", ci.value()); + + ci.set_value(L"2.0-beta"); + EXPECT_TRUE(ci.SetMigratingSuffix(true)); + EXPECT_TRUE(ci.HasMigratingSuffix()); + EXPECT_EQ(L"2.0-beta-migrating", ci.value()); + EXPECT_FALSE(ci.SetMigratingSuffix(true)); + EXPECT_TRUE(ci.HasMigratingSuffix()); + EXPECT_EQ(L"2.0-beta-migrating", ci.value()); + EXPECT_TRUE(ci.SetMigratingSuffix(false)); + EXPECT_FALSE(ci.HasMigratingSuffix()); + EXPECT_EQ(L"2.0-beta", ci.value()); + EXPECT_FALSE(ci.SetMigratingSuffix(false)); + EXPECT_FALSE(ci.HasMigratingSuffix()); + EXPECT_EQ(L"2.0-beta", ci.value()); +} + TEST(ChannelInfoTest, Combinations) { ChannelInfo ci; @@ -212,7 +244,7 @@ TEST(ChannelInfoTest, RemoveAllModifiersAndSuffixes) { EXPECT_FALSE(ci.RemoveAllModifiersAndSuffixes()); EXPECT_EQ(L"", ci.value()); - ci.set_value(L"2.0-dev-multi-chrome-chromeframe"); + ci.set_value(L"2.0-dev-multi-chrome-chromeframe-migrating"); EXPECT_TRUE(ci.RemoveAllModifiersAndSuffixes()); EXPECT_EQ(L"2.0-dev", ci.value()); }