From 36b6f4f462adc4ff57d65acef4c60939c2dfd8fe Mon Sep 17 00:00:00 2001 From: "tommi@chromium.org" Date: Thu, 23 Dec 2010 19:54:02 +0000 Subject: Support for GCF ready-mode. The installer can now be run in a way that installs both Chrome and Chrome Frame at the same time, into a shared directory. The command line to do this is: mini_installer.exe --multi-install --chrome --chrome-frame --ready-mode --system-level --verbose-logging This installs both products although only Chrome will have an entry in the add/remove programs dialog that serves as an uninstallation command for both products. Chrome Frame will be installed in ready-mode, which means that the user will have to opt-in or opt-out of using it. The installer will create a REG_DWORD value to indicate this mode, here: HKEY_LOCAL_MACHINE\SOFTWARE\Google\Update\ClientState\{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D} Value name: ChromeFrameReadyMode Value: 1 If the user opts-in to use GCF, setup will be run again to remove this value, update Chrome's uninstallation commands to only uninstall Chrome, and add an entry to the Add/Remove Programs dialog for GCF. To do this, the installer needs to be run with this switch: --multi-install --chrome-frame --system-level --ready-mode-opt-in --verbose-logging If the user opts-out, Chrome Frame will be uninstalled by running the installer with these arguments: --uninstall --chrome-frame --ready-mode --system-level --multi-install --verbose-logging In addition to uninstalling GCF, this updates Chrome's uninstallation commands accordingly and sets the ChromeFrameReadyMode value to 0 to avoid enabling ready mode again. Requirements that must currently be met in order to install Chrome and Chrome Frame into the same shared location: - Chrome Frame must not already be installed OR - Chrome Frame must be already installed as multi, into the shared location **** other changes **** The installer does no use the ap value for detecting other multi-install products anymore. Instead we inspect the uninstallation switches to see if --multi-install is present. Updated unit tests accordingly. When uninstalling Chrome Frame along with Chrome, the Chrome Frame uninstallation prompt is not shown. Added safeguards: If we attempt to install Chrome Frame multi over a single, install aborts and returns an error. If we attempt to install Chrome Frame single over a multi, install aborts and returns an error. TEST=See description above. BUG=61609 Review URL: http://codereview.chromium.org/5989007 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@70083 0039d316-1c4b-4281-b951-d872f2087c98 --- chrome/chrome_installer_util.gypi | 4 +- chrome/installer/setup/install.cc | 251 ++++++++++++++++++--- chrome/installer/setup/install.h | 15 +- chrome/installer/setup/setup_main.cc | 88 +++++--- chrome/installer/setup/uninstall.cc | 91 ++++---- chrome/installer/setup/uninstall.h | 5 +- chrome/installer/util/browser_distribution.cc | 4 + chrome/installer/util/browser_distribution.h | 4 + chrome/installer/util/chrome_frame_distribution.cc | 21 +- chrome/installer/util/chrome_frame_distribution.h | 5 + .../installer/util/delete_after_reboot_helper.cc | 6 +- chrome/installer/util/helper.cc | 77 ++++++- chrome/installer/util/helper.h | 16 ++ chrome/installer/util/install_util.cc | 6 +- chrome/installer/util/master_preferences.cc | 2 + .../installer/util/master_preferences_constants.cc | 1 + .../installer/util/master_preferences_constants.h | 2 + chrome/installer/util/package.cc | 17 +- chrome/installer/util/package_unittest.cc | 43 ++-- chrome/installer/util/product.cc | 31 ++- chrome/installer/util/product.h | 12 + chrome/installer/util/util_constants.cc | 10 +- chrome/installer/util/util_constants.h | 87 ++++--- 23 files changed, 596 insertions(+), 202 deletions(-) (limited to 'chrome') diff --git a/chrome/chrome_installer_util.gypi b/chrome/chrome_installer_util.gypi index 946ce9e..3e8cedd 100644 --- a/chrome/chrome_installer_util.gypi +++ b/chrome/chrome_installer_util.gypi @@ -33,6 +33,8 @@ 'installer/util/google_update_constants.h', 'installer/util/google_update_settings.cc', 'installer/util/google_update_settings.h', + 'installer/util/helper.cc', + 'installer/util/helper.h', 'installer/util/install_util.cc', 'installer/util/install_util.h', 'installer/util/l10n_string_util.cc', @@ -93,8 +95,6 @@ 'installer/util/delete_after_reboot_helper.h', 'installer/util/google_chrome_distribution.cc', 'installer/util/google_chrome_distribution.h', - 'installer/util/helper.cc', - 'installer/util/helper.h', 'installer/util/html_dialog.h', 'installer/util/html_dialog_impl.cc', 'installer/util/logging_installer.cc', diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc index 880f3e1..61f0b2c 100644 --- a/chrome/installer/setup/install.cc +++ b/chrome/installer/setup/install.cc @@ -19,12 +19,14 @@ #include "base/win/registry.h" #include "chrome/installer/setup/setup_constants.h" #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/create_reg_key_work_item.h" #include "chrome/installer/util/delete_after_reboot_helper.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/helper.h" #include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/master_preferences_constants.h" #include "chrome/installer/util/package.h" #include "chrome/installer/util/package_properties.h" @@ -38,13 +40,16 @@ #include "installer_util_strings.h" // NOLINT #include "registered_dlls.h" // NOLINT -namespace { - +using base::win::RegKey; +using installer::ChannelInfo; +using installer::MasterPreferences; using installer::Products; using installer::Product; using installer::Package; using installer::PackageProperties; +namespace { + void AddChromeToMediaPlayerList() { std::wstring reg_path(installer::kMediaPlayerRegPath); // registry paths can also be appended like file system path @@ -90,14 +95,18 @@ void AppendUninstallCommandLineFlags(CommandLine* uninstall_cmd, // Append the product-specific uninstall flags. product.distribution()->AppendUninstallCommandLineFlags(uninstall_cmd); - if (product.IsMsi()) + if (product.IsMsi()) { uninstall_cmd->AppendSwitch(installer::switches::kMsi); + // See comment in uninstall.cc where we check for the kDeleteProfile switch. + if (product.is_chrome_frame()) { + uninstall_cmd->AppendSwitch(installer::switches::kDeleteProfile); + } + } if (product.system_level()) uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel); // Propagate switches obtained from preferences as well. - const installer::MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); + const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); if (prefs.is_multi_install()) { uninstall_cmd->AppendSwitch(installer::switches::kMultiInstall); } @@ -133,6 +142,19 @@ void AddUninstallShortcutWorkItems(const FilePath& setup_path, CommandLine uninstall_arguments(CommandLine::NO_PROGRAM); AppendUninstallCommandLineFlags(&uninstall_arguments, product); + if (product.is_chrome()) { + // The Chrome uninstallation command serves as the master uninstall + // command for Chrome + all other products (i.e. Chrome Frame) that do + // not have an uninstall entry in the Add/Remove Programs dialog. + const Products& products = product.package().products(); + for (size_t i = 0; i < products.size(); ++i) { + const Product& p = *products[i]; + if (!p.is_chrome() && !p.ShouldCreateUninstallEntry()) { + p.distribution()->AppendUninstallCommandLineFlags(&uninstall_arguments); + } + } + } + std::wstring update_state_key(browser_dist->GetStateKey()); install_list->AddCreateRegKeyWorkItem(reg_root, update_state_key); install_list->AddSetRegValueWorkItem(reg_root, update_state_key, @@ -141,8 +163,7 @@ void AddUninstallShortcutWorkItems(const FilePath& setup_path, installer::kUninstallArgumentsField, uninstall_arguments.command_line_string(), true); - // MSI installations will manage their own uninstall shortcuts. - if (!product.IsMsi()) { + if (product.ShouldCreateUninstallEntry()) { // We need to quote the command line for the Add/Remove Programs dialog. CommandLine quoted_uninstall_cmd(installer_path); DCHECK_EQ(quoted_uninstall_cmd.command_line_string()[0], '"'); @@ -269,8 +290,7 @@ bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path, bool alt_shortcut) { // TODO(tommi): Change this function to use WorkItemList. #ifndef NDEBUG - const installer::MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); + const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); DCHECK(prefs.install_chrome()); #endif @@ -537,10 +557,10 @@ bool DoPostInstallTasks(bool multi_install, return false; } - // 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. 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; @@ -576,8 +596,7 @@ bool Is64bit() { void RegisterChromeOnMachine(const Product& product, bool make_chrome_default) { - DCHECK_EQ(product.distribution()->GetType(), - BrowserDistribution::CHROME_BROWSER); + DCHECK(product.is_chrome()); // Try to add Chrome to Media Player shim inclusion list. We don't do any // error checking here because this operation will fail if user doesn't @@ -601,6 +620,43 @@ void RegisterChromeOnMachine(const Product& product, } } +// Create Version key for a product (if not already present) and sets the new +// product version as the last step. +void AddVersionKeyWorkItems(HKEY root, + const Product& product, + const Version& new_version, + WorkItemList* list) { + // Create Version key for each distribution (if not already present) and set + // the new product version as the last step. + std::wstring version_key(product.distribution()->GetVersionKey()); + list->AddCreateRegKeyWorkItem(root, version_key); + + std::wstring product_name(product.distribution()->GetAppShortCutName()); + list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField, + product_name, true); // overwrite name also + list->AddSetRegValueWorkItem(root, version_key, + google_update::kRegOopcrashesField, 1, + false); // set during first install + list->AddSetRegValueWorkItem(root, version_key, + google_update::kRegVersionField, + UTF8ToWide(new_version.GetString()), + true); // overwrite version +} + +void AddProductSpecificWorkItems(bool install, + const FilePath& setup_path, + const Version& new_version, + const Package& package, + WorkItemList* list) { + const Products& products = package.products(); + for (size_t i = 0; i < products.size(); ++i) { + const Product& p = *products[i]; + if (p.is_chrome_frame()) { + AddChromeFrameWorkItems(install, setup_path, new_version, p, list); + } + } +} + // This function installs a new version of Chrome to the specified location. // // setup_path: Path to the executable (setup.exe) as it will be copied @@ -699,37 +755,19 @@ installer::InstallStatus InstallNewVersion( HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - std::wstring version_key; for (size_t i = 0; i < products.size(); ++i) { const Product* product = products[i]; AddUninstallShortcutWorkItems(setup_path, new_version, install_list.get(), *product); - // Create Version key for each distribution (if not already present) and set - // the new product version as the last step. - version_key = product->distribution()->GetVersionKey(); - install_list->AddCreateRegKeyWorkItem(root, version_key); - - std::wstring product_name(product->distribution()->GetAppShortCutName()); - install_list->AddSetRegValueWorkItem(root, version_key, - google_update::kRegNameField, - product_name, - true); // overwrite name also - install_list->AddSetRegValueWorkItem(root, version_key, - google_update::kRegOopcrashesField, - 1, - false); // set during first install - install_list->AddSetRegValueWorkItem(root, version_key, - google_update::kRegVersionField, - UTF8ToWide(new_version.GetString()), - true); // overwrite version + AddVersionKeyWorkItems(root, *product, new_version, install_list.get()); } if (multi_install) { PackageProperties* props = package.properties(); if (props->ReceivesUpdates()) { - version_key = props->GetVersionKey(); + std::wstring version_key(props->GetVersionKey()); install_list->AddCreateRegKeyWorkItem(root, version_key); install_list->AddSetRegValueWorkItem(root, version_key, google_update::kRegVersionField, @@ -742,6 +780,11 @@ installer::InstallStatus InstallNewVersion( } } + // Add any remaining work items that involve special settings for + // 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)) { @@ -787,7 +830,7 @@ installer::InstallStatus InstallNewVersion( namespace installer { -installer::InstallStatus InstallOrUpdateChrome( +installer::InstallStatus InstallOrUpdateProduct( const FilePath& setup_path, const FilePath& archive_path, const FilePath& install_temp_path, const FilePath& prefs_path, const installer::MasterPreferences& prefs, const Version& new_version, @@ -801,7 +844,7 @@ installer::InstallStatus InstallOrUpdateChrome( &existing_version, install); if (!InstallUtil::GetInstallReturnCode(result)) { - if (result == installer::FIRST_INSTALL_SUCCESS) + if (result == installer::FIRST_INSTALL_SUCCESS && !prefs_path.empty()) CopyPreferenceFileForFirstRun(install, prefs_path); bool do_not_create_shortcuts = false; @@ -884,5 +927,143 @@ bool RegisterComDllList(const FilePath& dll_folder, return success; } +void AddChromeFrameWorkItems(bool install, + const FilePath& setup_path, + const Version& new_version, + const Product& product, + WorkItemList* list) { + DCHECK(product.is_chrome_frame()); + if (!product.package().multi_install()) { + VLOG(1) << "Not adding GCF specific work items for single install."; + return; + } + + const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); + bool ready_mode = false; + prefs.GetBool(installer::master_preferences::kChromeFrameReadyMode, + &ready_mode); + HKEY root = product.package().system_level() ? HKEY_LOCAL_MACHINE : + HKEY_CURRENT_USER; + bool update_chrome_uninstall_command = false; + if (ready_mode) { + // If GCF is being installed in ready mode, we write an entry to the + // multi-install state key. If the value already exists, we will not + // overwrite it since the user might have opted out. + list->AddCreateRegKeyWorkItem(root, + product.package().properties()->GetStateKey()); + list->AddSetRegValueWorkItem(root, + product.package().properties()->GetStateKey(), + installer::kChromeFrameReadyModeField, + install ? 1 : 0, // The value we want to set. + install ? false : true); // Overwrite existing value. + if (!install) { + // If Chrome is not also being uninstalled, we need to update its command + // line so that it doesn't include uninstalling Chrome Frame now. + update_chrome_uninstall_command = + (installer::FindProduct(product.package().products(), + BrowserDistribution::CHROME_BROWSER) == NULL); + } + } else { + // It doesn't matter here if we're installing or uninstalling Chrome Frame. + // If ready mode isn't specified on the command line for installs, we need + // to delete the ready mode flag from the registry if it exists - this + // constitutes an opt-in for the user. If we're uninstalling CF and ready + // mode isn't specified on the command line, that means that CF wasn't + // installed with ready mode enabled (the --ready-mode switch should be set + // in the registry) so deleting the value should have no effect. + // In both cases (install/uninstall), we need to make sure that Chrome's + // uninstallation command line does not include the --chrome-frame switch + // so that uninstalling Chrome will no longer uninstall Chrome Frame. + + list->AddDeleteRegValueWorkItem(root, + product.package().properties()->GetStateKey(), + installer::kChromeFrameReadyModeField, false); + + const Product* chrome = installer::FindProduct(product.package().products(), + BrowserDistribution::CHROME_BROWSER); + if (chrome) { + // Chrome is already a part of this installation run, so we can assume + // that the uninstallation arguments will be updated correctly. + } else { + // Chrome is not a part of this installation run, so we have to explicitly + // check if Chrome is installed, and if so, update its uninstallation + // command lines. + update_chrome_uninstall_command = true; + } + } + + if (update_chrome_uninstall_command) { + // Chrome is not a part of this installation run, so we have to explicitly + // check if Chrome is installed, and if so, update its uninstallation + // command lines. + BrowserDistribution* chrome_dist = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER, prefs); + const Package& pack = product.package(); + scoped_refptr package(new Package(pack.multi_install(), + pack.system_level(), pack.path(), pack.properties())); + scoped_refptr chrome_product(new Product(chrome_dist, package)); + AddUninstallShortcutWorkItems(setup_path, new_version, list, + *chrome_product.get()); + } +} + +InstallStatus ChromeFrameReadyModeOptIn(const CommandLine& cmd_line) { + VLOG(1) << "Opting into Chrome Frame"; + InstallStatus status = INSTALL_REPAIRED; + + const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); + bool system_install = false; + prefs.GetBool(master_preferences::kSystemLevel, &system_install); + BrowserDistribution* cf = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_FRAME, prefs); + BrowserDistribution* chrome = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER, prefs); + +#if defined(GOOGLE_CHROME_BUILD) + ChromePackageProperties package_properties; +#else + ChromiumPackageProperties package_properties; +#endif + + // Remove ChromeFrameReadyMode, update Chrome's uninstallation commands to + // only uninstall Chrome, and add an entry to the Add/Remove Programs + // dialog for GCF. + + FilePath path(GetChromeFrameInstallPath(true, system_install, cf)); + if (path.empty()) { + LOG(ERROR) << "Conflicting installations"; + status = NON_MULTI_INSTALLATION_EXISTS; + } else { + scoped_refptr package(new Package(prefs.is_multi_install(), + system_install, path, &package_properties)); + scoped_refptr cf_product(new Product(cf, package)); + DCHECK(cf_product->ShouldCreateUninstallEntry() || cf_product->IsMsi()); + scoped_refptr chrome_product(new Product(chrome, package)); + + const Version* version = cf_product->GetInstalledVersion(); + scoped_ptr item_list(WorkItem::CreateWorkItemList()); + + // This creates the uninstallation entry for GCF. + AddUninstallShortcutWorkItems(cmd_line.GetProgram(), *version, + item_list.get(), *cf_product.get()); + // This updates the Chrome uninstallation entries. + AddUninstallShortcutWorkItems(cmd_line.GetProgram(), *version, + item_list.get(), *chrome_product.get()); + + // Add a work item to delete the ChromeFrameReadyMode registry value. + HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + item_list->AddDeleteRegValueWorkItem(root, package_properties.GetStateKey(), + kChromeFrameReadyModeField, false); + + if (!item_list->Do()) { + LOG(ERROR) << "Failed to opt into GCF"; + item_list->Rollback(); + status = READY_MODE_OPT_IN_FAILED; + } + } + + return status; +} } // namespace installer diff --git a/chrome/installer/setup/install.h b/chrome/installer/setup/install.h index 5b5a4d9..b1ba24c 100644 --- a/chrome/installer/setup/install.h +++ b/chrome/installer/setup/install.h @@ -18,6 +18,7 @@ class DictionaryValue; class FilePath; +class WorkItemList; namespace installer { @@ -40,7 +41,7 @@ class Package; // // Note: since caller unpacks Chrome to install_temp_path\source, the caller // is responsible for cleaning up install_temp_path. -installer::InstallStatus InstallOrUpdateChrome( +installer::InstallStatus InstallOrUpdateProduct( const FilePath& setup_path, const FilePath& archive_path, const FilePath& install_temp_path, const FilePath& prefs_path, const installer::MasterPreferences& prefs, const Version& new_version, @@ -60,6 +61,18 @@ bool RegisterComDllList(const FilePath& dll_folder, bool do_register, bool rollback_on_failure); +// Called for either installation or uninstallation. This method updates the +// registry according to Chrome Frame specific options for the current +// installation. This includes handling of the ready-mode option. +void AddChromeFrameWorkItems(bool install, const FilePath& setup_path, + const Version& new_version, const Product& product, + WorkItemList* list); + +// Removes the ChromeFrameReadyMode flag from the registry, updates Chrome's +// uninstallation commands to only uninstall Chrome, and adds an entry to the +// Add/Remove Programs list for GCF. +InstallStatus ChromeFrameReadyModeOptIn(const CommandLine& cmd_line); + } // namespace installer #endif // CHROME_INSTALLER_SETUP_INSTALL_H_ diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index 386c023..a3db00d 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc @@ -201,7 +201,7 @@ bool CheckPreInstallConditions(const Package& installation, if (chrome_version.get()) { LOG(ERROR) << "Already installed version " << chrome_version->GetString() << " conflicts with the current install mode."; - if (!system_level && is_first_install) { + if (!system_level && is_first_install && product->is_chrome()) { // This is user-level install and there is a system-level chrome // installation. Instruct Omaha to launch the existing one. There // should be no error dialog. @@ -323,8 +323,7 @@ installer::InstallStatus InstallChrome(const CommandLine& cmd_line, higher_version_installed = true; install_status = installer::HIGHER_VERSION_EXISTS; - if (product->distribution()->GetType() != - BrowserDistribution::CHROME_BROWSER) { + if (product->is_chrome()) { // TODO(robertshield): We should take the installer result text // strings from the Product. installation.WriteInstallerResult(install_status, @@ -342,9 +341,9 @@ installer::InstallStatus InstallChrome(const CommandLine& cmd_line, FilePath archive_to_copy(temp_path.Append(installer::kChromeArchive)); FilePath prefs_source_path(cmd_line.GetSwitchValueNative( installer::switches::kInstallerData)); - install_status = installer::InstallOrUpdateChrome(cmd_line.GetProgram(), - archive_to_copy, temp_path, prefs_source_path, prefs, - *installer_version, installation); + install_status = installer::InstallOrUpdateProduct( + cmd_line.GetProgram(), archive_to_copy, temp_path, + prefs_source_path, prefs, *installer_version, installation); int install_msg_base = IDS_INSTALL_FAILED_BASE; std::wstring chrome_exe; @@ -456,10 +455,8 @@ installer::InstallStatus InstallChrome(const CommandLine& cmd_line, return install_status; } -installer::InstallStatus UninstallChrome(const CommandLine& cmd_line, - const Product& product) { - VLOG(1) << "Uninstalling Chome"; - +installer::InstallStatus UninstallProduct(const CommandLine& cmd_line, + const Product& product) { bool force = cmd_line.HasSwitch(installer::switches::kForceUninstall); if (product.IsInstalled()) { VLOG(1) << "version on the system: " @@ -474,8 +471,8 @@ installer::InstallStatus UninstallChrome(const CommandLine& cmd_line, bool remove_all = !cmd_line.HasSwitch( installer::switches::kDoNotRemoveSharedItems); - return installer::UninstallChrome(cmd_line.GetProgram(), product, remove_all, - force, cmd_line); + return installer::UninstallProduct(cmd_line.GetProgram(), product, remove_all, + force, cmd_line); } installer::InstallStatus ShowEULADialog(const std::wstring& inner_frame) { @@ -513,6 +510,9 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, int& exit_code, const ProductPackageMapping& installs) { DCHECK(installs.products().size()); + bool handled = true; + // TODO(tommi): Split these checks up into functions and use a data driven + // map of switch->function. if (cmd_line.HasSwitch(installer::switches::kUpdateSetupExe)) { installer::InstallStatus status = installer::SETUP_PATCH_FAILED; // If --update-setup-exe command line option is given, we apply the given @@ -546,7 +546,6 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, IDS_SETUP_PATCH_FAILED_BASE, NULL); } file_util::Delete(temp_path, true); - return true; } else if (cmd_line.HasSwitch(installer::switches::kShowEula)) { // Check if we need to show the EULA. If it is passed as a command line // then the dialog is shown and regardless of the outcome setup exits here. @@ -555,7 +554,6 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, exit_code = ShowEULADialog(inner_frame); if (installer::EULA_REJECTED != exit_code) GoogleUpdateSettings::SetEULAConsent(*installs.packages()[0].get(), true); - return true; } else if (cmd_line.HasSwitch( installer::switches::kRegisterChromeBrowser)) { const Product* chrome_install = @@ -581,7 +579,6 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, LOG(ERROR) << "Can't register browser - Chrome distribution not found"; exit_code = installer::UNKNOWN_STATUS; } - return true; } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) { // If --rename-chrome-exe is specified, we want to rename the executables // and exit. @@ -589,7 +586,6 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, DCHECK_EQ(1U, packages.size()); for (size_t i = 0; i < packages.size(); ++i) exit_code = RenameChromeExecutables(*packages[i].get()); - return true; } else if (cmd_line.HasSwitch( installer::switches::kRemoveChromeRegistration)) { // This is almost reverse of --register-chrome-browser option above. @@ -611,7 +607,6 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, HKEY_LOCAL_MACHINE, suffix, tmp); } exit_code = tmp; - return true; } else if (cmd_line.HasSwitch(installer::switches::kInactiveUserToast)) { // Launch the inactive user toast experiment. int flavor = -1; @@ -628,7 +623,6 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, browser_dist->InactiveUserToastExperiment(flavor, *product); } } - return true; } else if (cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) { const Products& products = installs.products(); for (size_t i = 0; i < products.size(); ++i) { @@ -641,9 +635,15 @@ bool HandleNonInstallCmdLineOptions(const CommandLine& cmd_line, browser_dist->LaunchUserExperiment(installer::REENTRY_SYS_UPDATE, *installed_version, *product, true); } - return true; + } else if (cmd_line.HasSwitch( + installer::switches::kChromeFrameReadyModeOptIn)) { + exit_code = InstallUtil::GetInstallReturnCode( + installer::ChromeFrameReadyModeOptIn(cmd_line)); + } else { + handled = false; } - return false; + + return handled; } bool ShowRebootDialog() { @@ -700,18 +700,22 @@ class AutoCom { bool initialized_; }; -void PopulateInstallations(const MasterPreferences& prefs, +bool PopulateInstallations(const MasterPreferences& prefs, ProductPackageMapping* installations) { DCHECK(installations); + bool success = true; if (prefs.install_chrome()) { VLOG(1) << "Install distribution: Chrome"; - installations->AddDistribution(BrowserDistribution::CHROME_BROWSER, prefs); + success = installations->AddDistribution( + BrowserDistribution::CHROME_BROWSER, prefs); } - if (prefs.install_chrome_frame()) { + if (success && prefs.install_chrome_frame()) { VLOG(1) << "Install distribution: Chrome Frame"; - installations->AddDistribution(BrowserDistribution::CHROME_FRAME, prefs); + success = installations->AddDistribution( + BrowserDistribution::CHROME_FRAME, prefs); } + return success; } // Returns the Custom information for the client identified by the exe path @@ -790,8 +794,7 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, base::AtExitManager exit_manager; CommandLine::Init(0, NULL); - const MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); + const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); installer::InitInstallerLogging(prefs); const CommandLine& cmd_line = *CommandLine::ForCurrentProcess(); @@ -806,7 +809,18 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, InitializeCrashReporting(system_install)); ProductPackageMapping installations(prefs.is_multi_install(), system_install); - PopulateInstallations(prefs, &installations); + if (!PopulateInstallations(prefs, &installations)) { + // Currently this can only fail if one of the installations is a multi and + // a pre-existing single installation exists or vice versa. + installer::InstallStatus status; + if (prefs.is_multi_install()) { + status = installer::NON_MULTI_INSTALLATION_EXISTS; + } else { + status = installer::MULTI_INSTALLATION_EXISTS; + } + LOG(ERROR) << "Failed to populate installations: " << status; + return status; + } // Check to make sure current system is WinXP or later. If not, log // error message and get out. @@ -869,12 +883,8 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, installer::InstallStatus install_status = installer::UNKNOWN_STATUS; // If --uninstall option is given, uninstall chrome if (is_uninstall) { - DCHECK_EQ(1U, installations.products().size()) << - "We currently really only support uninstalling one distribution " - "at a time"; for (size_t i = 0; i < installations.products().size(); ++i) { - install_status = UninstallChrome(cmd_line, - *installations.products()[i]); + install_status = UninstallProduct(cmd_line, *installations.products()[i]); } } else { // If --uninstall option is not specified, we assume it is install case. @@ -893,11 +903,15 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, if (install_status == installer::UNINSTALL_REQUIRES_REBOOT) { ShowRebootDialog(); } else if (is_uninstall) { - ::MessageBoxW(NULL, - installer::GetLocalizedString( - IDS_UNINSTALL_COMPLETE_BASE).c_str(), - cf_install->distribution()->GetApplicationName().c_str(), - MB_OK); + // Only show the message box if Chrome Frame was the only product being + // uninstalled. + if (installations.products().size() == 1U) { + ::MessageBoxW(NULL, + installer::GetLocalizedString( + IDS_UNINSTALL_COMPLETE_BASE).c_str(), + cf_install->distribution()->GetApplicationName().c_str(), + MB_OK); + } } } diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc index b5b1986..1a623d6 100644 --- a/chrome/installer/setup/uninstall.cc +++ b/chrome/installer/setup/uninstall.cc @@ -20,11 +20,11 @@ #include "chrome/installer/setup/install.h" #include "chrome/installer/setup/setup_constants.h" #include "chrome/installer/util/browser_distribution.h" -#include "chrome/installer/util/channel_info.h" #include "chrome/installer/util/delete_after_reboot_helper.h" #include "chrome/installer/util/helper.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/logging_installer.h" +#include "chrome/installer/util/package_properties.h" #include "chrome/installer/util/shell_util.h" #include "chrome/installer/util/util_constants.h" @@ -32,7 +32,6 @@ #include "registered_dlls.h" // NOLINT using base::win::RegKey; -using installer::ChannelInfo; using installer::InstallStatus; namespace installer { @@ -135,8 +134,7 @@ bool CurrentUserHasDefaultBrowser(const Product& product) { // to remove the alternate desktop shortcut. Only one of them should be // present in a given install but at this point we don't know which one. void DeleteChromeShortcuts(const Product& product) { - if (product.distribution()->GetType() != - BrowserDistribution::CHROME_BROWSER) { + if (!product.is_chrome()) { VLOG(1) << __FUNCTION__ " called for non-CHROME distribution"; return; } @@ -251,8 +249,7 @@ DeleteResult DeleteLocalState(const Product& product) { if (!file_util::Delete(user_local_state, true)) { LOG(ERROR) << "Failed to delete user profile dir: " << user_local_state.value(); - if (product.distribution()->GetType() == - BrowserDistribution::CHROME_FRAME) { + if (product.is_chrome_frame()) { ScheduleDirectoryForDeletion(user_local_state.value().c_str()); result = DELETE_REQUIRES_REBOOT; } else { @@ -279,7 +276,9 @@ bool MoveSetupOutOfInstallFolder(const Package& package, if (!file_util::CreateTemporaryFile(&temp_file)) { LOG(ERROR) << "Failed to create temporary file for setup.exe."; } else { + VLOG(1) << "Attempting to move setup to: " << temp_file.value(); ret = file_util::Move(setup_exe, temp_file); + LOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value(); } return ret; } @@ -371,8 +370,7 @@ bool ShouldDeleteProfile(const CommandLine& cmd_line, InstallStatus status, // UI to prompt otherwise and the profile stores no useful data anyway) // unless they are managed by MSI. MSI uninstalls will explicitly include // the --delete-profile flag to distinguish them from MSI upgrades. - if (product.distribution()->GetType() != - BrowserDistribution::CHROME_BROWSER && !product.IsMsi()) { + if (!product.is_chrome() && !product.IsMsi()) { should_delete = true; } else { should_delete = @@ -478,11 +476,23 @@ const wchar_t kChromeExtProgId[] = L"ChromiumExt"; } } -InstallStatus UninstallChrome(const FilePath& setup_path, - const Product& product, - bool remove_all, - bool force_uninstall, - const CommandLine& cmd_line) { +bool ProcessChromeFrameWorkItems(const FilePath& setup_path, + const Product& product) { + if (!product.is_chrome_frame()) + return false; + + const Version* version = product.GetInstalledVersion(); + scoped_ptr item_list(WorkItem::CreateWorkItemList()); + AddChromeFrameWorkItems(false, setup_path, *version, product, + item_list.get()); + return item_list->Do(); +} + +InstallStatus UninstallProduct(const FilePath& setup_path, + const Product& product, + bool remove_all, + bool force_uninstall, + const CommandLine& cmd_line) { InstallStatus status = installer::UNINSTALL_CONFIRMED; std::wstring suffix; if (!ShellUtil::GetUserSpecificDefaultBrowserSuffix(product.distribution(), @@ -490,21 +500,9 @@ InstallStatus UninstallChrome(const FilePath& setup_path, suffix = L""; BrowserDistribution* browser_dist = product.distribution(); - bool is_chrome = (browser_dist->GetType() == - BrowserDistribution::CHROME_BROWSER); - - VLOG(1) << "UninstallChrome: " << browser_dist->GetApplicationName(); - - // Stash away information about the channel which the product is currently - // installed on. We'll need this later to determine if we should delete - // the binaries or not. - ChannelInfo channel_info; - { - HKEY root_key = product.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; - RegKey key(root_key, browser_dist->GetStateKey().c_str(), KEY_READ); - channel_info.Initialize(key); - } + bool is_chrome = product.is_chrome(); + + VLOG(1) << "UninstallProduct: " << browser_dist->GetApplicationName(); if (force_uninstall) { // Since --force-uninstall command line option is used, we are going to @@ -543,9 +541,6 @@ InstallStatus UninstallChrome(const FilePath& setup_path, } } - // Get the version of installed Chrome (if any) - const Version* installed_version = product.GetInstalledVersion(); - // Chrome is not in use so lets uninstall Chrome by deleting various files // and registry entries. Here we will just make best effort and keep going // in case of errors. @@ -576,6 +571,9 @@ InstallStatus UninstallChrome(const FilePath& setup_path, InstallStatus ret = installer::UNKNOWN_STATUS; DeleteChromeRegistrationKeys(product.distribution(), reg_root, suffix, ret); + if (!is_chrome) + ProcessChromeFrameWorkItems(setup_path, product); + // For user level install also we end up creating some keys in HKLM if user // sets Chrome as default browser. So delete those as well (needs admin). if (remove_all && !product.system_level() && @@ -584,6 +582,9 @@ InstallStatus UninstallChrome(const FilePath& setup_path, suffix, ret); } + // Get the version of the installed product (if any) + const Version* installed_version = product.GetInstalledVersion(); + // Delete shared registry keys as well (these require admin rights) if // remove_all option is specified. if (remove_all) { @@ -610,7 +611,7 @@ InstallStatus UninstallChrome(const FilePath& setup_path, } // Close any Chrome Frame helper processes that may be running. - if (product.distribution()->GetType() == BrowserDistribution::CHROME_FRAME) { + if (product.is_chrome_frame()) { VLOG(1) << "Closing the Chrome Frame helper process"; CloseChromeFrameHelperProcess(); } @@ -623,33 +624,37 @@ InstallStatus UninstallChrome(const FilePath& setup_path, bool delete_profile = ShouldDeleteProfile(cmd_line, status, product); ret = installer::UNINSTALL_SUCCESSFUL; - // In order to be able to remove the folder in which we're running, we - // need to move setup.exe out of the install folder. - // TODO(tommi): What if the temp folder is on a different volume? - MoveSetupOutOfInstallFolder(product.package(), setup_path, - *installed_version); - - FilePath backup_state_file(BackupLocalStateFile( - GetLocalStateFolder(product))); - // When deleting files, we must make sure that we're either a "single" // (aka non-multi) installation or, in the case of multi, that no other // "multi" products share the binaries we are about to delete. bool can_delete_files; - if (channel_info.IsMultiInstall()) { + if (product.package().multi_install()) { can_delete_files = (product.package().GetMultiInstallDependencyCount() == 0); LOG(INFO) << (can_delete_files ? "Shared binaries will be deleted." : "Shared binaries still in use."); + PackageProperties* props = product.package().properties(); + if (can_delete_files && props->ReceivesUpdates()) { + InstallUtil::DeleteRegistryKey(key, props->GetVersionKey()); + } } else { can_delete_files = true; } + FilePath backup_state_file(BackupLocalStateFile( + GetLocalStateFolder(product))); + DeleteResult delete_result = DELETE_SUCCEEDED; - if (can_delete_files) + if (can_delete_files) { + // In order to be able to remove the folder in which we're running, we + // need to move setup.exe out of the install folder. + // TODO(tommi): What if the temp folder is on a different volume? + MoveSetupOutOfInstallFolder(product.package(), setup_path, + *installed_version); delete_result = DeleteFilesAndFolders(product.package(), *installed_version); + } if (delete_profile) DeleteLocalState(product); diff --git a/chrome/installer/setup/uninstall.h b/chrome/installer/setup/uninstall.h index 2ec7775..274f55e 100644 --- a/chrome/installer/setup/uninstall.h +++ b/chrome/installer/setup/uninstall.h @@ -30,7 +30,8 @@ bool DeleteChromeRegistrationKeys(BrowserDistribution* dist, HKEY root, // uninstalls/reinstalls to update. void RemoveLegacyRegistryKeys(BrowserDistribution* dist); -// This function uninstalls Chrome. +// This function uninstalls a product. Hence we came up with this awesome +// name for it. // // setup_path: Path to the executable (setup.exe) as it will be copied // to temp folder before deleting Chrome folder. @@ -40,7 +41,7 @@ void RemoveLegacyRegistryKeys(BrowserDistribution* dist); // any checks for Chrome running. // cmd_line: CommandLine that contains information about the command that // was used to launch current uninstaller. -installer::InstallStatus UninstallChrome( +installer::InstallStatus UninstallProduct( const FilePath& setup_path, const Product& dist, bool remove_all, bool force_uninstall, const CommandLine& cmd_line); diff --git a/chrome/installer/util/browser_distribution.cc b/chrome/installer/util/browser_distribution.cc index 3ac4b1c..d684c97 100644 --- a/chrome/installer/util/browser_distribution.cc +++ b/chrome/installer/util/browser_distribution.cc @@ -232,3 +232,7 @@ void BrowserDistribution::AppendUninstallCommandLineFlags( DCHECK(cmd_line); cmd_line->AppendSwitch(installer::switches::kChrome); } + +bool BrowserDistribution::ShouldCreateUninstallEntry() { + return true; +} diff --git a/chrome/installer/util/browser_distribution.h b/chrome/installer/util/browser_distribution.h index 5175656..a967116 100644 --- a/chrome/installer/util/browser_distribution.h +++ b/chrome/installer/util/browser_distribution.h @@ -124,6 +124,10 @@ class BrowserDistribution { // for this distribution will require. virtual void AppendUninstallCommandLineFlags(CommandLine* cmd_line); + // Returns true if install should create an uninstallation entry in the + // Add/Remove Programs dialog for this distribution. + virtual bool ShouldCreateUninstallEntry(); + protected: explicit BrowserDistribution(const installer::MasterPreferences& prefs); diff --git a/chrome/installer/util/chrome_frame_distribution.cc b/chrome/installer/util/chrome_frame_distribution.cc index 0798366..c02e643 100644 --- a/chrome/installer/util/chrome_frame_distribution.cc +++ b/chrome/installer/util/chrome_frame_distribution.cc @@ -17,6 +17,7 @@ #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/l10n_string_util.h" #include "chrome/installer/util/master_preferences.h" +#include "chrome/installer/util/master_preferences_constants.h" #include "installer_util_strings.h" // NOLINT @@ -26,9 +27,11 @@ const wchar_t kChromeFrameGuid[] = L"{8BA986DA-5100-405E-AA35-86F34A02ACBF}"; ChromeFrameDistribution::ChromeFrameDistribution( const installer::MasterPreferences& prefs) - : BrowserDistribution(prefs) { + : BrowserDistribution(prefs), ceee_(prefs.install_ceee()), + ready_mode_(false) { type_ = BrowserDistribution::CHROME_FRAME; - ceee_ = prefs.install_ceee(); + prefs.GetBool(installer::master_preferences::kChromeFrameReadyMode, + &ready_mode_); } std::wstring ChromeFrameDistribution::GetAppGuid() { @@ -137,9 +140,17 @@ std::vector ChromeFrameDistribution::GetComDllList() { void ChromeFrameDistribution::AppendUninstallCommandLineFlags( CommandLine* cmd_line) { DCHECK(cmd_line); - cmd_line->AppendSwitch(installer::switches::kDeleteProfile); cmd_line->AppendSwitch(installer::switches::kChromeFrame); - if (ceee_) { + + if (ceee_) cmd_line->AppendSwitch(installer::switches::kCeee); - } + + if (ready_mode_) + cmd_line->AppendSwitch(installer::switches::kChromeFrameReadyMode); +} + +bool ChromeFrameDistribution::ShouldCreateUninstallEntry() { + // If Chrome Frame is being installed in ready mode, then we will not + // add an entry to the add/remove dialog. + return !ready_mode_; } diff --git a/chrome/installer/util/chrome_frame_distribution.h b/chrome/installer/util/chrome_frame_distribution.h index 6bf7e0f..94df4bd 100644 --- a/chrome/installer/util/chrome_frame_distribution.h +++ b/chrome/installer/util/chrome_frame_distribution.h @@ -62,6 +62,8 @@ class ChromeFrameDistribution : public BrowserDistribution { virtual void AppendUninstallCommandLineFlags(CommandLine* cmd_line); + virtual bool ShouldCreateUninstallEntry(); + protected: friend class BrowserDistribution; @@ -72,6 +74,9 @@ class ChromeFrameDistribution : public BrowserDistribution { // Determines whether this Chrome Frame distribution is being used to work // with CEEE bits as well. bool ceee_; + + // True when Chrome Frame is installed in ready mode (users have to opt in). + bool ready_mode_; }; #endif // CHROME_INSTALLER_UTIL_CHROME_FRAME_DISTRIBUTION_H_ diff --git a/chrome/installer/util/delete_after_reboot_helper.cc b/chrome/installer/util/delete_after_reboot_helper.cc index 1c44676..a678c73 100644 --- a/chrome/installer/util/delete_after_reboot_helper.cc +++ b/chrome/installer/util/delete_after_reboot_helper.cc @@ -243,11 +243,13 @@ std::wstring GetShortPathName(const wchar_t* path) { std::wstring short_path; DWORD length = GetShortPathName(path, WriteInto(&short_path, MAX_PATH), MAX_PATH); - DLOG_IF(WARNING, length == 0) << __FUNCTION__ << " gle=" << GetLastError(); + DWORD last_error = ::GetLastError(); + DLOG_IF(WARNING, length == 0 && last_error != ERROR_PATH_NOT_FOUND) + << __FUNCTION__ << " gle=" << last_error; if (length == 0) { // GetShortPathName fails if the path is no longer present. Instead of // returning an empty string, just return the original string. This will - // serve for our purposes. + // serve our purposes. return path; } diff --git a/chrome/installer/util/helper.cc b/chrome/installer/util/helper.cc index d0151fd..c1b9d42 100644 --- a/chrome/installer/util/helper.cc +++ b/chrome/installer/util/helper.cc @@ -4,9 +4,16 @@ #include "chrome/installer/util/helper.h" +#include "base/command_line.h" #include "base/file_path.h" +#include "base/logging.h" #include "base/path_service.h" +#include "base/win/registry.h" #include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/master_preferences.h" + +using base::win::RegKey; namespace { @@ -30,13 +37,73 @@ FilePath GetChromeInstallBasePath(bool system, } // namespace -FilePath installer::GetChromeInstallPath(bool system_install, - BrowserDistribution* dist) { +namespace installer { + +bool IsInstalledAsMulti(bool system_install, BrowserDistribution* dist) { + bool installed_as_multi = false; + HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + RegKey key(root, dist->GetStateKey().c_str(), KEY_READ); + if (key.Valid()) { + std::wstring args; + key.ReadValue(installer::kUninstallArgumentsField, &args); + if (!args.empty()) { + args.insert(0, L"fake.exe "); + CommandLine cmd(CommandLine::FromString(args)); + installed_as_multi = cmd.HasSwitch(installer::switches::kMultiInstall); + } + } + return installed_as_multi; +} + +FilePath GetChromeInstallPath(bool system_install, BrowserDistribution* dist) { return GetChromeInstallBasePath(system_install, dist, installer::kInstallBinaryDir); } -FilePath installer::GetChromeUserDataPath(BrowserDistribution* dist) { - return GetChromeInstallBasePath(false, dist, - installer::kInstallUserDataDir); +FilePath GetChromeUserDataPath(BrowserDistribution* dist) { + return GetChromeInstallBasePath(false, dist, kInstallUserDataDir); } + +FilePath GetChromeFrameInstallPath(bool multi_install, bool system_install, + BrowserDistribution* dist) { + DCHECK_EQ(BrowserDistribution::CHROME_FRAME, dist->GetType()); + + scoped_ptr installed_version( + InstallUtil::GetChromeVersion(dist, system_install)); + + if (!multi_install) { + // Check if Chrome Frame is installed as multi. If it is, return an empty + // path and log an error. + if (installed_version.get() && IsInstalledAsMulti(system_install, dist)) { + LOG(ERROR) << "Cannot install Chrome Frame in single mode as a multi mode" + " installation already exists."; + return FilePath(); + } + VLOG(1) << "Chrome Frame will be installed as 'single'"; + return GetChromeInstallPath(system_install, dist); + } + + // TODO(tommi): If Chrome Frame is installed as single and the installed + // channel is older than the one we're installing, we should migrate + // CF to Chrome's install folder and change its channel. + + // Multi install. Check if Chrome Frame is already installed. + // If CF is installed as single (i.e. not multi), we will return an empty + // path (for now). Otherwise, if CF is not installed or if it is installed + // as multi, we will return Chrome's install folder. + if (installed_version.get() && !IsInstalledAsMulti(system_install, dist)) { + LOG(ERROR) << "Cannot install Chrome Frame in multi mode as a single mode" + " installation already exists."; + return FilePath(); + } + + // Return Chrome's installation folder. + VLOG(1) << "Chrome Frame will be installed as 'multi'"; + const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); + BrowserDistribution* chrome = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER, prefs); + return GetChromeInstallPath(system_install, chrome); +} + +} // namespace installer. diff --git a/chrome/installer/util/helper.h b/chrome/installer/util/helper.h index 07bc893..3845806 100644 --- a/chrome/installer/util/helper.h +++ b/chrome/installer/util/helper.h @@ -13,6 +13,9 @@ class FilePath; namespace installer { +// Checks if a distribution is currently installed as part of a multi-install. +bool IsInstalledAsMulti(bool system_install, BrowserDistribution* dist); + // This function returns the install path for Chrome depending on whether its // system wide install or user specific install. // system_install: if true, the function returns system wide location @@ -26,6 +29,19 @@ FilePath GetChromeInstallPath(bool system_install, BrowserDistribution* dist); // that it can be overriden with a command line parameter. FilePath GetChromeUserDataPath(BrowserDistribution* dist); +// This is a workaround while we unify Chrome and Chrome Frame installation +// folders. Right now, Chrome Frame can be installed into two different +// folders: 1) A special "Chrome Frame" folder next to Chrome's folder +// 2) The same folder as Chrome is installed into. +// Right now this function will only return Chrome's installation folder +// if Chrome Frame is not already installed or if Chrome Frame is installed +// in multi_install mode. +// If multi_install is false or if CF is installed in single mode, then the +// returned path will be the "Chrome Frame" subfolder of either the user or +// system default installation folders. +FilePath GetChromeFrameInstallPath(bool multi_install, bool system_install, + BrowserDistribution* dist); + } // namespace installer #endif // CHROME_INSTALLER_UTIL_HELPER_H_ diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc index db5c17d8..9ba1cec 100644 --- a/chrome/installer/util/install_util.cc +++ b/chrome/installer/util/install_util.cc @@ -87,12 +87,14 @@ Version* InstallUtil::GetChromeVersion(BrowserDistribution* dist, HKEY reg_root = (system_install) ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; if (!key.Open(reg_root, dist->GetVersionKey().c_str(), KEY_READ) || !key.ReadValue(google_update::kRegVersionField, &version_str)) { - VLOG(1) << "No existing Chrome install found."; + VLOG(1) << "No existing " << dist->GetApplicationName() + << " install found."; key.Close(); return NULL; } key.Close(); - VLOG(1) << "Existing Chrome version found " << version_str; + VLOG(1) << "Existing " << dist->GetApplicationName() + << " version found " << version_str; return Version::GetVersionFromString(version_str); } diff --git a/chrome/installer/util/master_preferences.cc b/chrome/installer/util/master_preferences.cc index 2ca1a58..898e370 100644 --- a/chrome/installer/util/master_preferences.cc +++ b/chrome/installer/util/master_preferences.cc @@ -140,6 +140,8 @@ void MasterPreferences::InitializeFromCommandLine(const CommandLine& cmd_line) { installer::master_preferences::kChrome }, { installer::switches::kChromeFrame, installer::master_preferences::kChromeFrame }, + { installer::switches::kChromeFrameReadyMode, + installer::master_preferences::kChromeFrameReadyMode }, { installer::switches::kCreateAllShortcuts, installer::master_preferences::kCreateAllShortcuts }, { installer::switches::kDisableLogging, diff --git a/chrome/installer/util/master_preferences_constants.cc b/chrome/installer/util/master_preferences_constants.cc index 0b71d9e..cea5b7f 100644 --- a/chrome/installer/util/master_preferences_constants.cc +++ b/chrome/installer/util/master_preferences_constants.cc @@ -11,6 +11,7 @@ namespace master_preferences { const char kCeee[] = "ceee"; const char kChrome[] = "chrome"; const char kChromeFrame[] = "chrome_frame"; + const char kChromeFrameReadyMode[] = "ready_mode"; const char kChromeShortcutIconIndex[] = "chrome_shortcut_icon_index"; const char kCreateAllShortcuts[] = "create_all_shortcuts"; const char kDisableLogging[] = "disable_logging"; diff --git a/chrome/installer/util/master_preferences_constants.h b/chrome/installer/util/master_preferences_constants.h index 1220a38..cd3d034 100644 --- a/chrome/installer/util/master_preferences_constants.h +++ b/chrome/installer/util/master_preferences_constants.h @@ -27,6 +27,8 @@ extern const char kCeee[]; extern const char kChrome[]; // Boolean. This is to be a Chrome Frame install. extern const char kChromeFrame[]; +// Boolean. Chrome Frame is to be installed in ready-mode. +extern const char kChromeFrameReadyMode[]; // Integer. Icon index from chrome.exe to use for shortcuts. extern const char kChromeShortcutIconIndex[]; // Boolean. Create Desktop and QuickLaunch shortcuts. Cmd line override present. diff --git a/chrome/installer/util/package.cc b/chrome/installer/util/package.cc index 5f5ff57..72c819c 100644 --- a/chrome/installer/util/package.cc +++ b/chrome/installer/util/package.cc @@ -11,6 +11,7 @@ #include "chrome/installer/util/channel_info.h" #include "chrome/installer/util/delete_tree_work_item.h" #include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/helper.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/l10n_string_util.h" #include "chrome/installer/util/master_preferences.h" @@ -200,19 +201,9 @@ size_t Package::GetMultiInstallDependencyCount() const { if (!version_key.Valid()) { VLOG(1) << "Product not installed: " << dist->GetApplicationName(); } else { - RegKey key(root_key, dist->GetStateKey().c_str(), KEY_READ); - ChannelInfo channel_info; - if (channel_info.Initialize(key)) { - if (channel_info.IsMultiInstall()) { - VLOG(1) << "Product dependency: " << dist->GetApplicationName(); - ret++; - } else { - VLOG(1) << "Product is installed, but not multi: " - << dist->GetApplicationName(); - } - } else { - LOG(INFO) << "Product missing 'ap' value: " - << dist->GetApplicationName(); + if (installer::IsInstalledAsMulti(system_level_, dist)) { + VLOG(1) << "Product dependency: " << dist->GetApplicationName(); + ++ret; } } } diff --git a/chrome/installer/util/package_unittest.cc b/chrome/installer/util/package_unittest.cc index 2bfd44aa..c456482 100644 --- a/chrome/installer/util/package_unittest.cc +++ b/chrome/installer/util/package_unittest.cc @@ -2,11 +2,11 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include "base/command_line.h" #include "base/logging.h" #include "base/scoped_handle.h" #include "base/utf_string_conversions.h" #include "chrome/installer/util/browser_distribution.h" -#include "chrome/installer/util/channel_info.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/package.h" @@ -17,7 +17,6 @@ using base::win::RegKey; using base::win::ScopedHandle; -using installer::ChannelInfo; using installer::ChromePackageProperties; using installer::ChromiumPackageProperties; using installer::Package; @@ -134,6 +133,21 @@ TEST_F(PackageTest, WithProduct) { } } +namespace { +bool SetUninstallArguments(HKEY root, BrowserDistribution* dist, + const CommandLine& args) { + RegKey key(root, dist->GetStateKey().c_str(), KEY_ALL_ACCESS); + return key.WriteValue(installer::kUninstallArgumentsField, + args.command_line_string().c_str()); +} + +bool SetInstalledVersion(HKEY root, BrowserDistribution* dist, + const std::wstring& version) { + RegKey key(root, dist->GetVersionKey().c_str(), KEY_ALL_ACCESS); + return key.WriteValue(google_update::kRegVersionField, version.c_str()); +} +} // end namespace + TEST_F(PackageTest, Dependency) { const bool multi_install = false; const bool system_level = true; @@ -152,26 +166,25 @@ TEST_F(PackageTest, Dependency) { BrowserDistribution* cf = BrowserDistribution::GetSpecificDistribution( BrowserDistribution::CHROME_FRAME, prefs); + CommandLine multi_uninstall_cmd(CommandLine::NO_PROGRAM); + multi_uninstall_cmd.AppendSwitch(installer::switches::kUninstall); + multi_uninstall_cmd.AppendSwitch(installer::switches::kMultiInstall); + + CommandLine single_uninstall_cmd(CommandLine::NO_PROGRAM); + single_uninstall_cmd.AppendSwitch(installer::switches::kUninstall); + // "install" Chrome. - RegKey chrome_version_key(root, chrome->GetVersionKey().c_str(), - KEY_ALL_ACCESS); - RegKey chrome_key(root, chrome->GetStateKey().c_str(), KEY_ALL_ACCESS); - ChannelInfo channel; - channel.set_value(L""); - channel.SetMultiInstall(true); - channel.Write(&chrome_key); + SetUninstallArguments(root, chrome, multi_uninstall_cmd); + SetInstalledVersion(root, chrome, L"1.2.3.4"); EXPECT_EQ(1U, package->GetMultiInstallDependencyCount()); // "install" Chrome Frame without multi-install. - RegKey cf_version_key(root, cf->GetVersionKey().c_str(), KEY_ALL_ACCESS); - RegKey cf_key(root, cf->GetStateKey().c_str(), KEY_ALL_ACCESS); - channel.SetMultiInstall(false); - channel.Write(&cf_key); + SetUninstallArguments(root, cf, single_uninstall_cmd); + SetInstalledVersion(root, cf, L"1.2.3.4"); EXPECT_EQ(1U, package->GetMultiInstallDependencyCount()); // "install" Chrome Frame with multi-install. - channel.SetMultiInstall(true); - channel.Write(&cf_key); + SetUninstallArguments(root, cf, multi_uninstall_cmd); EXPECT_EQ(2U, package->GetMultiInstallDependencyCount()); } diff --git a/chrome/installer/util/product.cc b/chrome/installer/util/product.cc index 8d4617a..43f371a 100644 --- a/chrome/installer/util/product.cc +++ b/chrome/installer/util/product.cc @@ -117,8 +117,7 @@ bool Product::IsMsi() const { if ((cache_state_ & MSI_STATE) == 0) { msi_ = false; // Covers failure cases below. - const MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); + const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); bool is_msi = false; prefs.GetBool(installer::master_preferences::kMsi, &is_msi); @@ -177,6 +176,15 @@ bool Product::IsInstalled() const { return GetInstalledVersion() != NULL; } +bool Product::ShouldCreateUninstallEntry() const { + if (IsMsi()) { + // MSI installations will manage their own uninstall shortcuts. + return false; + } + + return distribution_->ShouldCreateUninstallEntry(); +} + /////////////////////////////////////////////////////////////////////////////// ProductPackageMapping::ProductPackageMapping(bool multi_install, bool system_level) @@ -202,7 +210,22 @@ bool ProductPackageMapping::AddDistribution(BrowserDistribution* distribution) { // Each product type can be added exactly once. DCHECK(FindProduct(products_, distribution->GetType()) == NULL); - FilePath install_package(GetChromeInstallPath(system_level_, distribution)); + FilePath install_package; + if (distribution->GetType() == BrowserDistribution::CHROME_BROWSER) { + install_package = GetChromeInstallPath(system_level_, distribution); + } else { + DCHECK_EQ(BrowserDistribution::CHROME_FRAME, distribution->GetType()); + install_package = GetChromeFrameInstallPath(multi_install_, system_level_, + distribution); + } + + if (install_package.empty()) { + LOG(ERROR) << "Got an empty installation path for " + << distribution->GetApplicationName() + << ". It's likely that there's a conflicting " + "installation present"; + return false; + } scoped_refptr target_package; for (size_t i = 0; i < packages_.size(); ++i) { @@ -234,7 +257,7 @@ bool ProductPackageMapping::AddDistribution(BrowserDistribution* distribution) { bool ProductPackageMapping::AddDistribution( BrowserDistribution::Type type, - const installer::MasterPreferences& prefs) { + const MasterPreferences& prefs) { BrowserDistribution* distribution = BrowserDistribution::GetSpecificDistribution(type, prefs); if (!distribution) { diff --git a/chrome/installer/util/product.h b/chrome/installer/util/product.h index 4204071..36362af 100644 --- a/chrome/installer/util/product.h +++ b/chrome/installer/util/product.h @@ -60,6 +60,14 @@ class Product : public base::RefCounted { return package().system_level(); } + bool is_chrome() const { + return distribution_->GetType() == BrowserDistribution::CHROME_BROWSER; + } + + bool is_chrome_frame() const { + return distribution_->GetType() == BrowserDistribution::CHROME_FRAME; + } + // Returns the path to the directory that holds the user data. This is always // inside "Users\\Local Settings". Note that this is the default user // data directory and does not take into account that it can be overriden with @@ -95,6 +103,10 @@ class Product : public base::RefCounted { // found. The returned Version object is owned by |this| Product instance. const Version* GetInstalledVersion() const; + // Returns true if setup should create an entry in the Add/Remove list + // of installed applications. + bool ShouldCreateUninstallEntry() const; + // Returns true if the product is already installed. bool IsInstalled() const; diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc index f3a062f..cda927d 100644 --- a/chrome/installer/util/util_constants.cc +++ b/chrome/installer/util/util_constants.cc @@ -15,9 +15,16 @@ const char kCeee[] = "ceee"; // Currently this is only required when used in combination with kMultiInstall. const char kChrome[] = "chrome"; -// Run the installer in Chrome Frame mode. +// Install Chrome Frame. const char kChromeFrame[] = "chrome-frame"; +// When installing Chrome Frame, install it in ready mode. +// If --chrome-frame is not on the command line, this switch has no effect. +const char kChromeFrameReadyMode[] = "ready-mode"; + +// GCF ready mode opt-in. This enables a full installation of GCF. +const char kChromeFrameReadyModeOptIn[] = "ready-mode-opt-in"; + // Run the installer for Chrome SxS. const char kChromeSxS[] = "chrome-sxs"; @@ -139,6 +146,7 @@ const wchar_t kChromeExe[] = L"chrome.exe"; const wchar_t kChromeFrameDll[] = L"npchrome_frame.dll"; const wchar_t kChromeFrameHelperExe[] = L"chrome_frame_helper.exe"; const wchar_t kChromeFrameHelperWndClass[] = L"ChromeFrameHelperWindowClass"; +const wchar_t kChromeFrameReadyModeField[] = L"ChromeFrameReadyMode"; const wchar_t kChromeNaCl64Dll[] = L"nacl64.dll"; const wchar_t kChromeNewExe[] = L"new_chrome.exe"; const wchar_t kChromeOldExe[] = L"old_chrome.exe"; diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h index 933cab5..0028c78 100644 --- a/chrome/installer/util/util_constants.h +++ b/chrome/installer/util/util_constants.h @@ -13,47 +13,63 @@ namespace installer { // Return status of installer enum InstallStatus { - FIRST_INSTALL_SUCCESS, // Successfully installed Chrome for the first time - INSTALL_REPAIRED, // Same version reinstalled for repair - NEW_VERSION_UPDATED, // Chrome successfully updated to new version - EXISTING_VERSION_LAUNCHED, // No work done, just launched existing chrome - HIGHER_VERSION_EXISTS, // Higher version of Chrome already exists - USER_LEVEL_INSTALL_EXISTS, // User level install already exists - SYSTEM_LEVEL_INSTALL_EXISTS, // Machine level install already exists - INSTALL_FAILED, // Install/update failed - SETUP_PATCH_FAILED, // Failed to patch setup.exe - OS_NOT_SUPPORTED, // Current OS not supported - OS_ERROR, // OS API call failed - TEMP_DIR_FAILED, // Unable to get Temp directory - UNCOMPRESSION_FAILED, // Failed to uncompress Chrome archive - INVALID_ARCHIVE, // Something wrong with the installer archive - INSUFFICIENT_RIGHTS, // User trying system level install is not Admin - CHROME_NOT_INSTALLED, // Chrome not installed (returned in case of uninstall) - CHROME_RUNNING, // Chrome currently running (when trying to uninstall) - UNINSTALL_CONFIRMED, // User has confirmed Chrome uninstall - UNINSTALL_DELETE_PROFILE, // User confirmed uninstall and profile deletion - UNINSTALL_SUCCESSFUL, // Chrome successfully uninstalled - UNINSTALL_FAILED, // Chrome uninstallation failed - UNINSTALL_CANCELLED, // User cancelled Chrome uninstallation - UNKNOWN_STATUS, // Unknown status (this should never happen) - RENAME_SUCCESSFUL, // Rename of new_chrome.exe to chrome.exe worked - RENAME_FAILED, // Rename of new_chrome.exe failed - EULA_REJECTED, // EULA dialog was not accepted by user. - EULA_ACCEPTED, // EULA dialog was accepted by user. - EULA_ACCEPTED_OPT_IN, // EULA accepted wtih the crash optin selected. - INSTALL_DIR_IN_USE, // Installation directory is in use by another process - UNINSTALL_REQUIRES_REBOOT, // Uninstallation required a reboot. - IN_USE_UPDATED, // Chrome successfully updated but old version running - SAME_VERSION_REPAIR_FAILED, // Chrome repair failed as Chrome was running - REENTRY_SYS_UPDATE, // Setup has been re-launched as the interactive user - SXS_OPTION_NOT_SUPPORTED // The chrome-sxs option provided does not work - // with other command line options. + FIRST_INSTALL_SUCCESS, // 0. Successfully installed Chrome for the first time + INSTALL_REPAIRED, // 1. Same version reinstalled for repair + NEW_VERSION_UPDATED, // 2. Chrome successfully updated to new version + EXISTING_VERSION_LAUNCHED, // 3. No work done, just launched existing chrome + HIGHER_VERSION_EXISTS, // 4. Higher version of Chrome already exists + USER_LEVEL_INSTALL_EXISTS, // 5. User level install already exists + SYSTEM_LEVEL_INSTALL_EXISTS, // 6. Machine level install already exists + INSTALL_FAILED, // 7. Install/update failed + SETUP_PATCH_FAILED, // 8. Failed to patch setup.exe + OS_NOT_SUPPORTED, // 9. Current OS not supported + OS_ERROR, // 10. OS API call failed + TEMP_DIR_FAILED, // 11. Unable to get Temp directory + UNCOMPRESSION_FAILED, // 12. Failed to uncompress Chrome archive + INVALID_ARCHIVE, // 13. Something wrong with the installer archive + INSUFFICIENT_RIGHTS, // 14. User trying system level install is not Admin + CHROME_NOT_INSTALLED, // 15. Chrome not installed (returned in case of + // uninstall) + CHROME_RUNNING, // 16. Chrome currently running (when trying to + // uninstall) + UNINSTALL_CONFIRMED, // 17. User has confirmed Chrome uninstall + UNINSTALL_DELETE_PROFILE, // 18. User confirmed uninstall and profile deletion + UNINSTALL_SUCCESSFUL, // 19. Chrome successfully uninstalled + UNINSTALL_FAILED, // 20. Chrome uninstallation failed + UNINSTALL_CANCELLED, // 21. User cancelled Chrome uninstallation + UNKNOWN_STATUS, // 22. Unknown status (this should never happen) + RENAME_SUCCESSFUL, // 23. Rename of new_chrome.exe to chrome.exe worked + RENAME_FAILED, // 24. Rename of new_chrome.exe failed + EULA_REJECTED, // 25. EULA dialog was not accepted by user. + EULA_ACCEPTED, // 26. EULA dialog was accepted by user. + EULA_ACCEPTED_OPT_IN, // 27. EULA accepted wtih the crash optin selected. + INSTALL_DIR_IN_USE, // 28. Installation directory is in use by another + // process + UNINSTALL_REQUIRES_REBOOT, // 29. Uninstallation required a reboot. + IN_USE_UPDATED, // 30. Chrome successfully updated but old version + // running + SAME_VERSION_REPAIR_FAILED, // 31. Chrome repair failed as Chrome was running + REENTRY_SYS_UPDATE, // 32. Setup has been re-launched as the interactive + // user + SXS_OPTION_NOT_SUPPORTED, // 33. The chrome-sxs option provided does not work + // with other command line options. + NON_MULTI_INSTALLATION_EXISTS, // 34. We tried to do a multi-install but + // failed because there's an existing + // installation of the same product on the + // system, but in 'single' mode. + MULTI_INSTALLATION_EXISTS, // 35. We tried to do a 'single' install but + // failed because there's an existing + // multi-install installation of the same product + // on the system. + READY_MODE_OPT_IN_FAILED, // 36. Failed to opt-into Chrome Frame. }; namespace switches { extern const char kCeee[]; extern const char kChrome[]; extern const char kChromeFrame[]; +extern const char kChromeFrameReadyMode[]; +extern const char kChromeFrameReadyModeOptIn[]; extern const char kChromeSxS[]; extern const char kCreateAllShortcuts[]; extern const char kDeleteProfile[]; @@ -95,6 +111,7 @@ extern const wchar_t kChromeExe[]; extern const wchar_t kChromeFrameDll[]; extern const wchar_t kChromeFrameHelperExe[]; extern const wchar_t kChromeFrameHelperWndClass[]; +extern const wchar_t kChromeFrameReadyModeField[]; extern const wchar_t kChromeNaCl64Dll[]; extern const wchar_t kChromeOldExe[]; extern const wchar_t kChromeNewExe[]; -- cgit v1.1