diff options
author | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-25 16:44:37 +0000 |
---|---|---|
committer | grt@chromium.org <grt@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2011-01-25 16:44:37 +0000 |
commit | f0260d27d34e40f2e619fb7a16dcd7f9d63fc683 (patch) | |
tree | 7e7c9e83bbd2f67b4bbbe11d076c7cc70abd8855 | |
parent | ce7937429f63f3d966346fcd973d720f268cd888 (diff) | |
download | chromium_src-f0260d27d34e40f2e619fb7a16dcd7f9d63fc683.zip chromium_src-f0260d27d34e40f2e619fb7a16dcd7f9d63fc683.tar.gz chromium_src-f0260d27d34e40f2e619fb7a16dcd7f9d63fc683.tar.bz2 |
More installer refactoring in the interest of fixing some bugs and cleaning things up:
- Introduced ProductOperations: an interface implemented for each product that takes care of product-specific functions. Each Product owns an instance and delegates certain operations to it.
- Removed the use of MasterPreferences by BrowserDistribution so that the former isn't needed outside of the installer.
- Replaced PackageProperties with a new BrowserDistribution type (CHROME_BINARIES)
- Plumbed the concept of InstallerState more thoroughly through installer
- Removed ProductPackageMapping and Package
- Moved more registry read ops into ProductState
- Validation of products to be installed is now done in CheckPreInstallConditions
- Ignore --chrome-frame --ready-mode if chrome is also being installed/updated and a SxS GCF is found (chrome is updated).
- Migrates existing single-install Chrome to multi-install where appropriate.
- Fixes update to Chrome's uninstallation arguments when Chrome Frame is uninstalled.
- Removed dead code from install.cc.
- Added code to update products' "ap" values when ready-mode is accepted.
- Skip post-install things such as launching the browser when Chrome was implicitly added to the install/upgrade process by virtue of being part of a multi-install.
BUG=61609
TEST=run the installer, see it work. existing tests in installer_util_unittests have been updated; new tests are included for ProductState, ChannelInfo, etc.
Review URL: http://codereview.chromium.org/6288009
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@72497 0039d316-1c4b-4281-b951-d872f2087c98
67 files changed, 3511 insertions, 2668 deletions
diff --git a/chrome/chrome_installer.gypi b/chrome/chrome_installer.gypi index 8aaf57f..4599130 100644 --- a/chrome/chrome_installer.gypi +++ b/chrome/chrome_installer.gypi @@ -86,18 +86,17 @@ 'installer/util/delete_tree_work_item_unittest.cc', 'installer/util/google_chrome_distribution_unittest.cc', 'installer/util/google_update_settings_unittest.cc', - 'installer/util/helper_unittest.cc', 'installer/util/install_util_unittest.cc', + 'installer/util/installer_state_unittest.cc', 'installer/util/installer_util_unittests.rc', 'installer/util/installer_util_unittests_resource.h', 'installer/util/language_selector_unittest.cc', 'installer/util/lzma_util_unittest.cc', 'installer/util/master_preferences_unittest.cc', 'installer/util/move_tree_work_item_unittest.cc', - 'installer/util/package_properties_unittest.cc', - 'installer/util/package_unittest.cc', 'installer/util/product_unittest.h', 'installer/util/product_unittest.cc', + 'installer/util/product_state_unittest.cc', 'installer/util/run_all_unittests.cc', 'installer/util/set_reg_value_work_item_unittest.cc', 'installer/util/shell_util_unittest.cc', @@ -309,6 +308,7 @@ 'msvs_guid': 'C0AE4E06-F023-460F-BC14-6302CEAC51F8', 'dependencies': [ 'installer_util', + 'installer_util_strings', '<(DEPTH)/base/base.gyp:base', '<(DEPTH)/base/base.gyp:base_i18n', '<(DEPTH)/base/base.gyp:test_support_base', @@ -329,6 +329,9 @@ 'installer/setup/run_all_unittests.cc', 'installer/setup/setup_constants.cc', # Move to lib 'installer/setup/setup_constants.h', # Move to lib + 'installer/setup/setup_unittests.rc', + 'installer/setup/setup_unittests.rc', + 'installer/setup/setup_unittests_resource.h', 'installer/setup/setup_util.cc', 'installer/setup/setup_util_unittest.cc', ], diff --git a/chrome/chrome_installer_util.gypi b/chrome/chrome_installer_util.gypi index 72601bb..3f852bc 100644 --- a/chrome/chrome_installer_util.gypi +++ b/chrome/chrome_installer_util.gypi @@ -17,6 +17,8 @@ 'installer/util/channel_info.h', 'installer/util/chrome_frame_distribution.cc', 'installer/util/chrome_frame_distribution.h', + 'installer/util/chromium_binaries_distribution.cc', + 'installer/util/chromium_binaries_distribution.h', 'installer/util/conditional_work_item_list.cc', 'installer/util/conditional_work_item_list.h', 'installer/util/copy_tree_work_item.cc', @@ -31,6 +33,8 @@ 'installer/util/delete_reg_value_work_item.h', 'installer/util/delete_tree_work_item.cc', 'installer/util/delete_tree_work_item.h', + 'installer/util/google_chrome_binaries_distribution.cc', + 'installer/util/google_chrome_binaries_distribution.h', 'installer/util/google_chrome_sxs_distribution.cc', 'installer/util/google_chrome_sxs_distribution.h', 'installer/util/google_update_constants.cc', @@ -53,10 +57,6 @@ 'installer/util/master_preferences_constants.h', 'installer/util/move_tree_work_item.cc', 'installer/util/move_tree_work_item.h', - 'installer/util/package.h', - 'installer/util/package.cc', - 'installer/util/package_properties.h', - 'installer/util/package_properties.cc', 'installer/util/self_reg_work_item.cc', 'installer/util/self_reg_work_item.h', 'installer/util/set_reg_value_work_item.cc', @@ -99,6 +99,12 @@ '<(DEPTH)/third_party/lzma_sdk/lzma_sdk.gyp:lzma_sdk', ], 'sources': [ + 'installer/util/chrome_browser_operations.cc', + 'installer/util/chrome_browser_operations.h', + 'installer/util/chrome_browser_sxs_operations.cc', + 'installer/util/chrome_browser_sxs_operations.h', + 'installer/util/chrome_frame_operations.cc', + 'installer/util/chrome_frame_operations.h', 'installer/util/compat_checks.cc', 'installer/util/compat_checks.h', 'installer/util/delete_after_reboot_helper.cc', @@ -115,6 +121,7 @@ 'installer/util/master_preferences.h', 'installer/util/product.h', 'installer/util/product.cc', + 'installer/util/product_operations.h', 'installer/util/shell_util.cc', 'installer/util/shell_util.h', ], diff --git a/chrome/common/chrome_paths_win.cc b/chrome/common/chrome_paths_win.cc index 062c43b..1892b43 100644 --- a/chrome/common/chrome_paths_win.cc +++ b/chrome/common/chrome_paths_win.cc @@ -15,7 +15,6 @@ #include "base/path_service.h" #include "chrome/common/chrome_constants.h" #include "chrome/installer/util/browser_distribution.h" -#include "chrome/installer/util/master_preferences.h" namespace chrome { @@ -32,8 +31,7 @@ bool GetChromeFrameUserDataDirectory(FilePath* result) { if (!PathService::Get(base::DIR_LOCAL_APP_DATA, result)) return false; BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_FRAME, - installer::MasterPreferences::ForCurrentProcess()); + BrowserDistribution::CHROME_FRAME); *result = result->Append(dist->GetInstallSubDir()); *result = result->Append(chrome::kUserDataDirname); return true; diff --git a/chrome/installer/setup/chrome_frame_ready_mode.cc b/chrome/installer/setup/chrome_frame_ready_mode.cc index 26584d2..c647a9b 100644 --- a/chrome/installer/setup/chrome_frame_ready_mode.cc +++ b/chrome/installer/setup/chrome_frame_ready_mode.cc @@ -22,8 +22,6 @@ #include "chrome/installer/util/installer_state.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" #include "chrome/installer/util/product.h" #include "chrome/installer/util/util_constants.h" #include "chrome/installer/util/work_item.h" @@ -31,76 +29,100 @@ namespace installer { -InstallStatus ChromeFrameReadyModeOptIn(const InstallerState& installer_state, - const CommandLine& cmd_line) { +// If Chrome is not multi-installed at the appropriate level, error. +// If Chrome Frame is already multi-installed at the appropriate level, noop. +// If Chrome Frame is single-installed at the appropriate level, error. +// Add uninstall for Chrome Frame. +// Update uninstall for Chrome. +// Update ChannelInfo for all multi-installed products. +// Remove ready-mode. +InstallStatus ChromeFrameReadyModeOptIn( + const InstallationState& machine_state, + const InstallerState& installer_state) { 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); - DCHECK(cf->ShouldCreateUninstallEntry()) - << "Opting into CF should create an uninstall entry"; - BrowserDistribution* chrome = BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs); - - ActivePackageProperties package_properties; - - // 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 { - InstallationState original_state; - original_state.Initialize(prefs); - - scoped_refptr<Package> package(new Package(prefs.is_multi_install(), - system_install, path, &package_properties)); - scoped_refptr<Product> cf_product(new Product(cf, package)); - DCHECK(cf_product->ShouldCreateUninstallEntry() || cf_product->IsMsi()); - scoped_refptr<Product> chrome_product(new Product(chrome, package)); - const ProductState* product_state = - original_state.GetProductState(system_install, cf->GetType()); - if (product_state == NULL) { - LOG(ERROR) << "No Chrome Frame installation found for opt-in."; - return CHROME_NOT_INSTALLED; - } - scoped_ptr<WorkItemList> item_list(WorkItem::CreateWorkItemList()); - - // This creates the uninstallation entry for GCF. - AddUninstallShortcutWorkItems(cmd_line.GetProgram(), - product_state->version(), item_list.get(), *cf_product.get()); - // This updates the Chrome uninstallation entries. - AddUninstallShortcutWorkItems(cmd_line.GetProgram(), - product_state->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); - - // Delete the command elevation registry keys - std::wstring version_key(cf->GetVersionKey()); - item_list->AddDeleteRegValueWorkItem( - root, version_key, google_update::kRegCFTempOptOutCmdField); - item_list->AddDeleteRegValueWorkItem( - root, version_key, google_update::kRegCFEndTempOptOutCmdField); - item_list->AddDeleteRegValueWorkItem(root, version_key, - google_update::kRegCFOptOutCmdField); - item_list->AddDeleteRegValueWorkItem(root, version_key, - google_update::kRegCFOptInCmdField); - - if (!item_list->Do()) { - LOG(ERROR) << "Failed to opt into GCF"; - item_list->Rollback(); - status = READY_MODE_OPT_IN_FAILED; - } + // Make sure Chrome and Chrome Frame are both multi-installed. + const ProductState* chrome_state = + machine_state.GetProductState(installer_state.system_install(), + BrowserDistribution::CHROME_BROWSER); + const ProductState* cf_state = + machine_state.GetProductState(installer_state.system_install(), + BrowserDistribution::CHROME_FRAME); + if (chrome_state == NULL) { + LOG(ERROR) << "Chrome Frame opt-in requires multi-install of Chrome."; + return CHROME_NOT_INSTALLED; + } + if (!chrome_state->is_multi_install()) { + LOG(ERROR) << "Chrome Frame opt-in requires multi-install of Chrome."; + return NON_MULTI_INSTALLATION_EXISTS; + } + if (cf_state == NULL) { + LOG(ERROR) << "Chrome Frame opt-in requires multi-install of Chrome Frame."; + return CHROME_NOT_INSTALLED; + } + if (!cf_state->is_multi_install()) { + LOG(ERROR) << "Chrome Frame opt-in requires multi-install of Chrome Frame."; + return NON_MULTI_INSTALLATION_EXISTS; + } + + // Create a new InstallerState to be used for this operation. + InstallerState opt_in_state(installer_state.level()); + + // Add the two products we're going to operate on. + const Product* chrome = + opt_in_state.AddProductFromState(BrowserDistribution::CHROME_BROWSER, + *chrome_state); + Product* cf = + opt_in_state.AddProductFromState(BrowserDistribution::CHROME_FRAME, + *cf_state); + // DCHECKs will fire in this case if it ever happens (it won't). + if (chrome == NULL || cf == NULL) + return READY_MODE_OPT_IN_FAILED; + + // Turn off ready-mode on Chrome Frame, thereby making it fully installed. + if (!cf->SetOption(kOptionReadyMode, false)) { + LOG(WARNING) + << "Chrome Frame is already fully installed; opting-in nonetheless."; + } + + // Update Chrome's uninstallation commands to only uninstall Chrome, and add + // an entry to the Add/Remove Programs dialog for GCF. + DCHECK(cf->ShouldCreateUninstallEntry() || opt_in_state.is_msi()); + + scoped_ptr<WorkItemList> item_list(WorkItem::CreateWorkItemList()); + + // This creates the uninstallation entry for GCF. + AddUninstallShortcutWorkItems(opt_in_state, cf_state->GetSetupPath(), + cf_state->version(), item_list.get(), *cf); + // This updates the Chrome uninstallation entries. + AddUninstallShortcutWorkItems(opt_in_state, chrome_state->GetSetupPath(), + chrome_state->version(), item_list.get(), *chrome); + + // Add a work item to delete the ChromeFrameReadyMode registry value. + HKEY root = opt_in_state.root_key(); + item_list->AddDeleteRegValueWorkItem(root, + opt_in_state.multi_package_binaries_distribution()->GetStateKey(), + kChromeFrameReadyModeField); + + // Update the Google Update channel ("ap") value. + AddGoogleUpdateWorkItems(opt_in_state, item_list.get()); + + // Delete the command elevation registry keys + std::wstring version_key(cf->distribution()->GetVersionKey()); + item_list->AddDeleteRegValueWorkItem( + root, version_key, google_update::kRegCFTempOptOutCmdField); + item_list->AddDeleteRegValueWorkItem( + root, version_key, google_update::kRegCFEndTempOptOutCmdField); + item_list->AddDeleteRegValueWorkItem(root, version_key, + google_update::kRegCFOptOutCmdField); + item_list->AddDeleteRegValueWorkItem(root, version_key, + google_update::kRegCFOptInCmdField); + + if (!item_list->Do()) { + LOG(ERROR) << "Failed to opt into GCF"; + item_list->Rollback(); + status = READY_MODE_OPT_IN_FAILED; } return status; @@ -111,100 +133,104 @@ const wchar_t kPostPlatformUAKey[] = L"User Agent\\Post Platform"; const wchar_t kChromeFramePrefix[] = L"chromeframe/"; -InstallStatus ChromeFrameReadyModeTempOptOut(const CommandLine& cmd_line) { +InstallStatus ChromeFrameReadyModeTempOptOut( + const InstallationState& machine_state, + const InstallerState& installer_state) { VLOG(1) << "Temporarily opting out of 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); + // Make sure Chrome Frame is multi-installed. + const ProductState* cf_state = + machine_state.GetProductState(installer_state.system_install(), + BrowserDistribution::CHROME_FRAME); + if (cf_state == NULL) { + LOG(ERROR) + << "Chrome Frame temp opt-out requires multi-install of Chrome Frame."; + return CHROME_NOT_INSTALLED; + } + if (!cf_state->is_multi_install()) { + LOG(ERROR) + << "Chrome Frame temp opt-out requires multi-install of Chrome Frame."; + return NON_MULTI_INSTALLATION_EXISTS; + } - installer::ActivePackageProperties package_properties; + scoped_ptr<WorkItemList> item_list(WorkItem::CreateWorkItemList()); - // Remove the ChromeFrame user agent string from the registry, modify the - // ReadyMode state flag. - FilePath path(GetChromeFrameInstallPath(true, system_install, cf)); - if (path.empty()) { - LOG(ERROR) << "Conflicting installations"; - status = NON_MULTI_INSTALLATION_EXISTS; - } else { - scoped_ptr<WorkItemList> item_list(WorkItem::CreateWorkItemList()); - - HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - // Add a work item to delete the ChromeFrame user agent registry value. - base::win::RegistryValueIterator values(root, kPostPlatformUAKey); - while (values.Valid()) { - const wchar_t* name = values.Name(); - if (StartsWith(name, kChromeFramePrefix, true)) { - item_list->AddDeleteRegValueWorkItem(root, kPostPlatformUAKey, name); - } - ++values; - } + HKEY root = installer_state.root_key(); - // Add a work item to update the Ready Mode state flag - int64 timestamp = base::Time::Now().ToInternalValue(); - item_list->AddSetRegValueWorkItem(root, package_properties.GetStateKey(), - kChromeFrameReadyModeField, timestamp, - true); - - if (!item_list->Do()) { - LOG(ERROR) << "Failed to temporarily opt out of GCF"; - item_list->Rollback(); - status = READY_MODE_TEMP_OPT_OUT_FAILED; + // Add a work item to delete the ChromeFrame user agent registry value. + base::win::RegistryValueIterator values(root, kPostPlatformUAKey); + while (values.Valid()) { + const wchar_t* name = values.Name(); + if (StartsWith(name, kChromeFramePrefix, true)) { + item_list->AddDeleteRegValueWorkItem(root, kPostPlatformUAKey, name); } + ++values; + } + + // Add a work item to update the Ready Mode state flag + int64 timestamp = base::Time::Now().ToInternalValue(); + BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES); + item_list->AddSetRegValueWorkItem(root, dist->GetStateKey(), + kChromeFrameReadyModeField, timestamp, + true); + + if (!item_list->Do()) { + LOG(ERROR) << "Failed to temporarily opt out of GCF"; + item_list->Rollback(); + status = READY_MODE_TEMP_OPT_OUT_FAILED; } return status; } -InstallStatus ChromeFrameReadyModeEndTempOptOut(const CommandLine& cmd_line) { +InstallStatus ChromeFrameReadyModeEndTempOptOut( + const InstallationState& machine_state, + const InstallerState& installer_state) { VLOG(1) << "Ending temporary opt-out of 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); - - installer::ActivePackageProperties package_properties; + // Make sure Chrome Frame is multi-installed. + const ProductState* cf_state = + machine_state.GetProductState(installer_state.system_install(), + BrowserDistribution::CHROME_FRAME); + if (cf_state == NULL) { + LOG(ERROR) + << "Chrome Frame temp opt-out requires multi-install of Chrome Frame."; + return CHROME_NOT_INSTALLED; + } + if (!cf_state->is_multi_install()) { + LOG(ERROR) + << "Chrome Frame temp opt-out requires multi-install of Chrome Frame."; + return NON_MULTI_INSTALLATION_EXISTS; + } // Replace the ChromeFrame user agent string in the registry, modify the // ReadyMode state flag. - FilePath path(GetChromeFrameInstallPath(true, system_install, cf)); - scoped_ptr<Version> installed_version( - InstallUtil::GetChromeVersion(cf, system_install)); - - if (path.empty()) { - LOG(ERROR) << "Conflicting installations"; - status = NON_MULTI_INSTALLATION_EXISTS; - } else if (installed_version == NULL) { - LOG(ERROR) << "Failed to query installed version of Chrome Frame"; + const Version& installed_version = cf_state->version(); + + scoped_ptr<WorkItemList> item_list(WorkItem::CreateWorkItemList()); + + HKEY root = installer_state.root_key(); + + std::wstring chrome_frame_ua_value_name(kChromeFramePrefix); + chrome_frame_ua_value_name += ASCIIToWide(installed_version.GetString()); + + // Store the Chrome Frame user agent string + item_list->AddSetRegValueWorkItem(root, kPostPlatformUAKey, + chrome_frame_ua_value_name, L"", true); + // Add a work item to update the Ready Mode state flag + BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES); + item_list->AddSetRegValueWorkItem(root, dist->GetStateKey(), + kChromeFrameReadyModeField, + static_cast<int64>(1), true); + + if (!item_list->Do()) { + LOG(ERROR) << "Failed to end temporary opt out of GCF"; + item_list->Rollback(); status = READY_MODE_END_TEMP_OPT_OUT_FAILED; - } else { - scoped_ptr<WorkItemList> item_list(WorkItem::CreateWorkItemList()); - - HKEY root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - std::wstring chrome_frame_ua_value_name = kChromeFramePrefix; - chrome_frame_ua_value_name += ASCIIToWide(installed_version->GetString()); - - // Store the Chrome Frame user agent string - item_list->AddSetRegValueWorkItem(root, kPostPlatformUAKey, - chrome_frame_ua_value_name, L"", true); - // Add a work item to update the Ready Mode state flag - item_list->AddSetRegValueWorkItem(root, package_properties.GetStateKey(), - kChromeFrameReadyModeField, - static_cast<int64>(1), true); - - if (!item_list->Do()) { - LOG(ERROR) << "Failed to end temporary opt out of GCF"; - item_list->Rollback(); - status = READY_MODE_END_TEMP_OPT_OUT_FAILED; - } } return status; diff --git a/chrome/installer/setup/chrome_frame_ready_mode.h b/chrome/installer/setup/chrome_frame_ready_mode.h index 35c9507..2541bb8 100644 --- a/chrome/installer/setup/chrome_frame_ready_mode.h +++ b/chrome/installer/setup/chrome_frame_ready_mode.h @@ -13,21 +13,26 @@ class CommandLine; namespace installer { enum InstallStatus; +class InstallationState; class InstallerState; // 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 InstallerState& installer_state, - const CommandLine& cmd_line); +InstallStatus ChromeFrameReadyModeOptIn(const InstallationState& machine_state, + const InstallerState& installer_state); // Unregisters the ChromeFrame user agent modification, sets a timestamp for // restoring it. -InstallStatus ChromeFrameReadyModeTempOptOut(const CommandLine& cmd_line); +InstallStatus ChromeFrameReadyModeTempOptOut( + const InstallationState& machine_state, + const InstallerState& installer_state); // Re-registers the ChromeFrame user agent modification, restores Ready Mode // active state flag. -InstallStatus ChromeFrameReadyModeEndTempOptOut(const CommandLine& cmd_line); +InstallStatus ChromeFrameReadyModeEndTempOptOut( + const InstallationState& machine_state, + const InstallerState& installer_state); } // namespace installer diff --git a/chrome/installer/setup/install.cc b/chrome/installer/setup/install.cc index dced77c..b8ae6fd 100644 --- a/chrome/installer/setup/install.cc +++ b/chrome/installer/setup/install.cc @@ -16,13 +16,9 @@ #include "base/scoped_ptr.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" -#include "base/win/registry.h" #include "chrome/installer/setup/setup_constants.h" #include "chrome/installer/setup/install_worker.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/conditional_work_item_list.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" @@ -32,8 +28,6 @@ #include "chrome/installer/util/installer_state.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" #include "chrome/installer/util/product.h" #include "chrome/installer/util/set_reg_value_work_item.h" #include "chrome/installer/util/shell_util.h" @@ -44,15 +38,9 @@ #include "installer_util_strings.h" // NOLINT #include "registered_dlls.h" // NOLINT -using base::win::RegKey; -using installer::ChannelInfo; using installer::InstallerState; using installer::InstallationState; -using installer::MasterPreferences; -using installer::Products; using installer::Product; -using installer::Package; -using installer::PackageProperties; namespace { @@ -69,14 +57,14 @@ void AddChromeToMediaPlayerList() { LOG(ERROR) << "Could not add Chrome to media player inclusion list."; } -void AddInstallerCopyTasks(const FilePath& setup_path, +void AddInstallerCopyTasks(const InstallerState& installer_state, + const FilePath& setup_path, const FilePath& archive_path, const FilePath& temp_path, const Version& new_version, - WorkItemList* install_list, - const Package& package) { + WorkItemList* install_list) { DCHECK(install_list); - FilePath installer_dir(package.GetInstallerDirectory(new_version)); + FilePath installer_dir(installer_state.GetInstallerDirectory(new_version)); install_list->AddCreateDirWorkItem(installer_dir); FilePath exe_dst(installer_dir.Append(setup_path.BaseName())); @@ -84,7 +72,7 @@ void AddInstallerCopyTasks(const FilePath& setup_path, install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(), temp_path.value(), WorkItem::ALWAYS); - if (package.system_level()) { + if (installer_state.system_install()) { install_list->AddCopyTreeWorkItem(archive_path.value(), archive_dst.value(), temp_path.value(), WorkItem::ALWAYS); } else { @@ -93,139 +81,12 @@ void AddInstallerCopyTasks(const FilePath& setup_path, } } -// Adds work items that make registry adjustments for Google Update. When a -// product is installed (including overinstall), Google Update will write the -// channel ("ap") value into either Chrome or Chrome Frame's ClientState key. -// In the multi-install case, this value is used as the basis upon which the -// package's channel value is built (by adding the ordered list of installed -// products and their options). -void AddGoogleUpdateWorkItems(const InstallationState& original_state, - const InstallerState& installer_state, - const Package& package, - WorkItemList* install_list) { - // Is a multi-install product being installed or over-installed? - if (installer_state.operation() != InstallerState::MULTI_INSTALL) - return; - - const HKEY reg_root = package.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; - const std::wstring key_path = installer_state.state_key(); - ChannelInfo channel_info; - - // Update the "ap" value for the product being installed/updated. - // It is completely acceptable for there to be no "ap" value or even no - // ClientState key. Note that we check the registry rather than - // original_state since on a fresh install the "ap" value will be present - // sans "pv" value. - channel_info.Initialize(RegKey(reg_root, key_path.c_str(), KEY_QUERY_VALUE)); - - // This is a multi-install product. - bool modified = channel_info.SetMultiInstall(true); - - // Add the appropriate modifiers for all products and their options. - Products::const_iterator scan = package.products().begin(); - const Products::const_iterator end = package.products().end(); - for (; scan != end; ++scan) { - modified |= scan->get()->distribution()->SetChannelFlags(true, - &channel_info); - } - - // Write the results if needed. - if (modified) { - install_list->AddSetRegValueWorkItem(reg_root, key_path, - google_update::kRegApField, - channel_info.value(), true); - } - - // Synchronize the other products and the package with this one. - std::wstring other_key; - std::vector<std::wstring> keys; - - keys.reserve(package.products().size()); - other_key = package.properties()->GetStateKey(); - if (other_key != key_path) - keys.push_back(other_key); - scan = package.products().begin(); - for (; scan != end; ++scan) { - other_key = scan->get()->distribution()->GetStateKey(); - if (other_key != key_path) - keys.push_back(other_key); - } - - RegKey key; - ChannelInfo other_info; - std::vector<std::wstring>::const_iterator kscan = keys.begin(); - std::vector<std::wstring>::const_iterator kend = keys.end(); - for (; kscan != kend; ++kscan) { - // Handle the case where the ClientState key doesn't exist by creating it. - // This takes care of the multi-installer's package key, which is not - // created by Google Update for us. - if (key.Open(reg_root, kscan->c_str(), KEY_QUERY_VALUE) != ERROR_SUCCESS || - !other_info.Initialize(key)) { - other_info.set_value(std::wstring()); - } - if (!other_info.Equals(channel_info)) { - if (!key.Valid()) - install_list->AddCreateRegKeyWorkItem(reg_root, *kscan); - install_list->AddSetRegValueWorkItem(reg_root, *kscan, - google_update::kRegApField, - channel_info.value(), true); - } - } - // TODO(grt): check for other keys/values we should put in the package's - // ClientState and/or Clients key. -} - -// This is called when an MSI installation is run. It may be that a user is -// attempting to install the MSI on top of a non-MSI managed installation. -// If so, try and remove any existing uninstallation shortcuts, as we want the -// uninstall to be managed entirely by the MSI machinery (accessible via the -// Add/Remove programs dialog). -void AddDeleteUninstallShortcutsForMSIWorkItems(const Product& product, - WorkItemList* work_item_list) { - DCHECK(product.IsMsi()) << "This must only be called for MSI installations!"; - - // First attempt to delete the old installation's ARP dialog entry. - HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; - base::win::RegKey root_key(reg_root, L"", KEY_ALL_ACCESS); - std::wstring uninstall_reg(product.distribution()->GetUninstallRegPath()); - - WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem( - reg_root, uninstall_reg); - delete_reg_key->set_ignore_failure(true); - - // Then attempt to delete the old installation's start menu shortcut. - FilePath uninstall_link; - if (product.system_level()) { - PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link); - } else { - PathService::Get(base::DIR_START_MENU, &uninstall_link); - } - - if (uninstall_link.empty()) { - LOG(ERROR) << "Failed to get location for shortcut."; - } else { - uninstall_link = uninstall_link.Append( - product.distribution()->GetAppShortCutName()); - uninstall_link = uninstall_link.Append( - product.distribution()->GetUninstallLinkName() + L".lnk"); - VLOG(1) << "Deleting old uninstall shortcut (if present): " - << uninstall_link.value(); - WorkItem* delete_link = work_item_list->AddDeleteTreeWorkItem( - uninstall_link); - delete_link->set_ignore_failure(true); - delete_link->set_log_message( - "Failed to delete old uninstall shortcut."); - } -} - // Copy master preferences file provided to installer, in the same folder // as chrome.exe so Chrome first run can find it. This function will be called // only on the first install of Chrome. -void CopyPreferenceFileForFirstRun(const Package& package, +void CopyPreferenceFileForFirstRun(const InstallerState& installer_state, const FilePath& prefs_source_path) { - FilePath prefs_dest_path(package.path().AppendASCII( + FilePath prefs_dest_path(installer_state.target_path().AppendASCII( installer::kDefaultMasterPrefs)); if (!file_util::CopyFile(prefs_source_path, prefs_dest_path)) { VLOG(1) << "Failed to copy master preferences from:" @@ -247,7 +108,8 @@ void CopyPreferenceFileForFirstRun(const Package& package, // // If the shortcuts do not exist, the function does not recreate them during // update. -bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path, +bool CreateOrUpdateChromeShortcuts(const InstallerState& installer_state, + const FilePath& setup_path, const Version& new_version, installer::InstallStatus install_status, const Product& product, @@ -257,7 +119,7 @@ bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path, DCHECK(product.is_chrome()); FilePath shortcut_path; - int dir_enum = product.system_level() ? + int dir_enum = installer_state.system_install() ? base::DIR_COMMON_START_MENU : base::DIR_START_MENU; if (!PathService::Get(dir_enum, &shortcut_path)) { LOG(ERROR) << "Failed to get location for shortcut."; @@ -283,7 +145,7 @@ bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path, chrome_link = chrome_link.Append(product_name + L".lnk"); // Chrome link target FilePath chrome_exe( - product.package().path().Append(installer::kChromeExe)); + installer_state.target_path().Append(installer::kChromeExe)); if ((install_status == installer::FIRST_INSTALL_SUCCESS) || (install_status == installer::INSTALL_REPAIRED)) { @@ -309,7 +171,7 @@ bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path, // installations are, for the time being, managed only through the // Add/Remove Programs dialog. // TODO(robertshield): We could add a shortcut to msiexec /X {GUID} here. - if (ret && !product.IsMsi()) { + if (ret && !installer_state.is_msi()) { FilePath uninstall_link(shortcut_path); // Uninstall Chrome link uninstall_link = uninstall_link.Append( browser_dist->GetUninstallLinkName() + L".lnk"); @@ -319,12 +181,11 @@ bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path, if (!file_util::PathExists(shortcut_path)) file_util::CreateDirectory(shortcut_path); - FilePath setup_exe( - product.package().GetInstallerDirectory(new_version) - .Append(setup_path.BaseName())); + FilePath setup_exe(installer_state.GetInstallerDirectory(new_version) + .Append(setup_path.BaseName())); CommandLine arguments(CommandLine::NO_PROGRAM); - AppendUninstallCommandLineFlags(&arguments, product); + AppendUninstallCommandLineFlags(installer_state, product, &arguments); VLOG(1) << "Creating/updating uninstall link at " << uninstall_link.value(); ret = file_util::CreateShortcutLink(setup_exe.value().c_str(), @@ -342,7 +203,7 @@ bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path, // is specified we want to create them, otherwise we update them only if // they exist. if (ret) { - if (product.system_level()) { + if (installer_state.system_install()) { ret = ShellUtil::CreateChromeDesktopShortcut(product.distribution(), chrome_exe.value(), product_desc, ShellUtil::SYSTEM_LEVEL, alt_shortcut, create_all_shortcut); @@ -367,8 +228,8 @@ bool CreateOrUpdateChromeShortcuts(const FilePath& setup_path, return ret; } - -void RegisterChromeOnMachine(const Product& product, +void RegisterChromeOnMachine(const InstallerState& installer_state, + const Product& product, bool make_chrome_default) { DCHECK(product.is_chrome()); @@ -380,11 +241,11 @@ void RegisterChromeOnMachine(const Product& product, // Is --make-chrome-default option is given we make Chrome default browser // otherwise we only register it on the machine as a valid browser. FilePath chrome_exe( - product.package().path().Append(installer::kChromeExe)); + installer_state.target_path().Append(installer::kChromeExe)); VLOG(1) << "Registering Chrome as browser: " << chrome_exe.value(); if (make_chrome_default) { int level = ShellUtil::CURRENT_USER; - if (product.system_level()) + if (installer_state.system_install()) level = level | ShellUtil::SYSTEM_LEVEL; ShellUtil::MakeChromeDefault(product.distribution(), level, chrome_exe.value(), true); @@ -417,33 +278,29 @@ void RegisterChromeOnMachine(const Product& product, installer::InstallStatus InstallNewVersion( const InstallationState& original_state, const InstallerState& installer_state, - bool multi_install, const FilePath& setup_path, const FilePath& archive_path, const FilePath& src_path, const FilePath& temp_dir, const Version& new_version, - scoped_ptr<Version>* current_version, - const Package& package) { + scoped_ptr<Version>* current_version) { DCHECK(current_version); - current_version->reset(package.GetCurrentVersion()); + current_version->reset(installer_state.GetCurrentVersion(original_state)); scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); AddInstallWorkItems(original_state, installer_state, - multi_install, setup_path, archive_path, src_path, temp_dir, new_version, current_version, - package, install_list.get()); FilePath new_chrome_exe( - package.path().Append(installer::kChromeNewExe)); + installer_state.target_path().Append(installer::kChromeNewExe)); if (!install_list->Do()) { installer::InstallStatus result = @@ -488,61 +345,63 @@ installer::InstallStatus InstallNewVersion( namespace installer { -installer::InstallStatus InstallOrUpdateProduct( +InstallStatus InstallOrUpdateProduct( const InstallationState& original_state, const InstallerState& installer_state, - 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, - const Package& install) { + const FilePath& setup_path, + const FilePath& archive_path, + const FilePath& install_temp_path, + const FilePath& prefs_path, + const MasterPreferences& prefs, + const Version& new_version) { FilePath src_path(install_temp_path); src_path = src_path.Append(kInstallSourceDir).Append(kInstallSourceChromeDir); // TODO(robertshield): Removing the pending on-reboot moves should be done // elsewhere. - const Products& products = install.products(); + const Products& products = installer_state.products(); DCHECK(products.size()); - if (FindProduct(products, BrowserDistribution::CHROME_FRAME)) { + if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { // Make sure that we don't end up deleting installed files on next reboot. - if (!RemoveFromMovesPendingReboot(install.path().value().c_str())) { + if (!RemoveFromMovesPendingReboot( + installer_state.target_path().value().c_str())) { LOG(ERROR) << "Error accessing pending moves value."; } } scoped_ptr<Version> existing_version; - installer::InstallStatus result = InstallNewVersion(original_state, - installer_state, prefs.is_multi_install(), setup_path, archive_path, - src_path, install_temp_path, new_version, &existing_version, install); + InstallStatus result = InstallNewVersion(original_state, installer_state, + setup_path, archive_path, src_path, install_temp_path, new_version, + &existing_version); // TODO(robertshield): Everything below this line should instead be captured // by WorkItems. if (!InstallUtil::GetInstallReturnCode(result)) { - if (result == installer::FIRST_INSTALL_SUCCESS && !prefs_path.empty()) - CopyPreferenceFileForFirstRun(install, prefs_path); + if (result == FIRST_INSTALL_SUCCESS && !prefs_path.empty()) + CopyPreferenceFileForFirstRun(installer_state, prefs_path); bool do_not_create_shortcuts = false; - prefs.GetBool(installer::master_preferences::kDoNotCreateShortcuts, + prefs.GetBool(master_preferences::kDoNotCreateShortcuts, &do_not_create_shortcuts); // Currently this only creates shortcuts for Chrome, but for other products // we might want to create shortcuts. const Product* chrome_install = - FindProduct(install.products(), BrowserDistribution::CHROME_BROWSER); + installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER); if (chrome_install && !do_not_create_shortcuts) { bool create_all_shortcut = false; - prefs.GetBool(installer::master_preferences::kCreateAllShortcuts, + prefs.GetBool(master_preferences::kCreateAllShortcuts, &create_all_shortcut); bool alt_shortcut = false; - prefs.GetBool(installer::master_preferences::kAltShortcutText, - &alt_shortcut); - if (!CreateOrUpdateChromeShortcuts(setup_path, new_version, result, - *chrome_install, create_all_shortcut, - alt_shortcut)) { + prefs.GetBool(master_preferences::kAltShortcutText, &alt_shortcut); + if (!CreateOrUpdateChromeShortcuts(installer_state, setup_path, + new_version, result, *chrome_install, + create_all_shortcut, alt_shortcut)) { PLOG(WARNING) << "Failed to create/update start menu shortcut."; } bool make_chrome_default = false; - prefs.GetBool(installer::master_preferences::kMakeChromeDefault, + prefs.GetBool(master_preferences::kMakeChromeDefault, &make_chrome_default); // If this is not the user's first Chrome install, but they have chosen @@ -550,18 +409,17 @@ installer::InstallStatus InstallOrUpdateProduct( // force it here because the master_preferences file will not get copied // into the build. bool force_chrome_default_for_user = false; - if (result == installer::NEW_VERSION_UPDATED || - result == installer::INSTALL_REPAIRED) { - prefs.GetBool( - installer::master_preferences::kMakeChromeDefaultForUser, - &force_chrome_default_for_user); + if (result == NEW_VERSION_UPDATED || + result == INSTALL_REPAIRED) { + prefs.GetBool(master_preferences::kMakeChromeDefaultForUser, + &force_chrome_default_for_user); } - RegisterChromeOnMachine(*chrome_install, + RegisterChromeOnMachine(installer_state, *chrome_install, make_chrome_default || force_chrome_default_for_user); } - install.RemoveOldVersionDirectories(existing_version.get() ? + installer_state.RemoveOldVersionDirectories(existing_version.get() ? *existing_version.get() : new_version); } diff --git a/chrome/installer/setup/install.h b/chrome/installer/setup/install.h index c3ff033..d05a955 100644 --- a/chrome/installer/setup/install.h +++ b/chrome/installer/setup/install.h @@ -13,7 +13,6 @@ #include "base/version.h" #include "chrome/installer/util/product.h" -#include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/util_constants.h" class DictionaryValue; @@ -24,7 +23,7 @@ namespace installer { class InstallationState; class InstallerState; -class Package; +class MasterPreferences; // This function installs or updates a new version of Chrome. It returns // install status (failed, new_install, updated etc). @@ -46,12 +45,12 @@ class Package; InstallStatus InstallOrUpdateProduct( const InstallationState& original_state, const InstallerState& installer_state, - 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, - const Package& package); - - + 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); } // namespace installer diff --git a/chrome/installer/setup/install_worker.cc b/chrome/installer/setup/install_worker.cc index f9a534a..8965edf 100644 --- a/chrome/installer/setup/install_worker.cc +++ b/chrome/installer/setup/install_worker.cc @@ -21,7 +21,6 @@ #include "base/win/registry.h" #include "chrome/installer/setup/install.h" #include "chrome/installer/setup/setup_constants.h" -#include "chrome/installer/util/chrome_frame_distribution.h" #include "chrome/installer/util/conditional_work_item_list.h" #include "chrome/installer/util/create_reg_key_work_item.h" #include "chrome/installer/util/google_update_constants.h" @@ -29,10 +28,6 @@ #include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/installer_state.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" #include "chrome/installer/util/product.h" #include "chrome/installer/util/set_reg_value_work_item.h" #include "chrome/installer/util/shell_util.h" @@ -63,20 +58,13 @@ namespace installer { // Local helper to call AddRegisterComDllWorkItems for all DLLs in a set of // products managed by a given package. -void AddRegisterComDllWorkItemsForPackage(const Package& package, +void AddRegisterComDllWorkItemsForPackage(const InstallerState& installer_state, const Version* old_version, const Version& new_version, WorkItemList* work_item_list) { // First collect the list of DLLs to be registered from each product. - const Products& products = package.products(); - Products::const_iterator product_iter(products.begin()); std::vector<FilePath> com_dll_list; - for (; product_iter != products.end(); ++product_iter) { - BrowserDistribution* dist = product_iter->get()->distribution(); - std::vector<FilePath> dist_dll_list(dist->GetComDllList()); - com_dll_list.insert(com_dll_list.end(), dist_dll_list.begin(), - dist_dll_list.end()); - } + installer_state.AddComDllList(&com_dll_list); // Then, if we got some, attempt to unregister the DLLs from the old // version directory and then re-register them in the new one. @@ -88,36 +76,36 @@ void AddRegisterComDllWorkItemsForPackage(const Package& package, // saved state instead of assuming it is the same as the registration list. if (!com_dll_list.empty()) { if (old_version) { - FilePath old_dll_path( - package.path().Append(UTF8ToWide(old_version->GetString()))); + FilePath old_dll_path(installer_state.target_path().Append( + UTF8ToWide(old_version->GetString()))); installer::AddRegisterComDllWorkItems(old_dll_path, com_dll_list, - package.system_level(), + installer_state.system_install(), false, // Unregister true, // May fail work_item_list); } - FilePath dll_path( - package.path().Append(UTF8ToWide(new_version.GetString()))); + FilePath dll_path(installer_state.target_path().Append( + UTF8ToWide(new_version.GetString()))); installer::AddRegisterComDllWorkItems(dll_path, com_dll_list, - package.system_level(), + installer_state.system_install(), true, // Register false, // Must succeed. work_item_list); } } -void AddInstallerCopyTasks(const FilePath& setup_path, +void AddInstallerCopyTasks(const InstallerState& installer_state, + const FilePath& setup_path, const FilePath& archive_path, const FilePath& temp_path, const Version& new_version, - WorkItemList* install_list, - const Package& package) { + WorkItemList* install_list) { DCHECK(install_list); - FilePath installer_dir(package.GetInstallerDirectory(new_version)); + FilePath installer_dir(installer_state.GetInstallerDirectory(new_version)); install_list->AddCreateDirWorkItem(installer_dir); FilePath exe_dst(installer_dir.Append(setup_path.BaseName())); @@ -125,7 +113,7 @@ void AddInstallerCopyTasks(const FilePath& setup_path, install_list->AddCopyTreeWorkItem(setup_path.value(), exe_dst.value(), temp_path.value(), WorkItem::ALWAYS); - if (package.system_level()) { + if (installer_state.system_install()) { install_list->AddCopyTreeWorkItem(archive_path.value(), archive_dst.value(), temp_path.value(), WorkItem::ALWAYS); } else { @@ -137,12 +125,12 @@ void AddInstallerCopyTasks(const FilePath& setup_path, // This method adds work items to create (or update) Chrome uninstall entry in // either the Control Panel->Add/Remove Programs list or in the Omaha client // state key if running under an MSI installer. -void AddUninstallShortcutWorkItems(const FilePath& setup_path, +void AddUninstallShortcutWorkItems(const InstallerState& installer_state, + const FilePath& setup_path, const Version& new_version, WorkItemList* install_list, const Product& product) { - HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; + HKEY reg_root = installer_state.root_key(); BrowserDistribution* browser_dist = product.distribution(); DCHECK(browser_dist); @@ -152,24 +140,26 @@ void AddUninstallShortcutWorkItems(const FilePath& setup_path, // install is updated by a non-msi installer (which would confuse the MSI // machinery if these strings were not also updated). // Do not quote the command line for the MSI invocation. - FilePath install_path(product.package().path()); - FilePath installer_path( - product.package().GetInstallerDirectory(new_version)); + FilePath install_path(installer_state.target_path()); + FilePath installer_path(installer_state.GetInstallerDirectory(new_version)); installer_path = installer_path.Append(setup_path.BaseName()); 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(); + AppendUninstallCommandLineFlags(installer_state, product, + &uninstall_arguments); + + // 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. We skip this processing + // in case of uninstall since this means that Chrome Frame is being + // uninstalled, so there's no need to do any looping. + if (product.is_chrome() && + installer_state.operation() != InstallerState::UNINSTALL) { + const Products& products = installer_state.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); - } + if (!p.is_chrome() && !p.ShouldCreateUninstallEntry()) + p.AppendProductFlags(&uninstall_arguments); } } @@ -181,7 +171,8 @@ void AddUninstallShortcutWorkItems(const FilePath& setup_path, installer::kUninstallArgumentsField, uninstall_arguments.command_line_string(), true); - if (product.ShouldCreateUninstallEntry()) { + // MSI installations will manage their own uninstall shortcuts. + if (!installer_state.is_msi() && 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], '"'); @@ -241,15 +232,15 @@ void AddUninstallShortcutWorkItems(const FilePath& setup_path, // 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, + BrowserDistribution* dist, 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()); + std::wstring version_key(dist->GetVersionKey()); list->AddCreateRegKeyWorkItem(root, version_key); - std::wstring product_name(product.distribution()->GetAppShortCutName()); + std::wstring product_name(dist->GetAppShortCutName()); list->AddSetRegValueWorkItem(root, version_key, google_update::kRegNameField, product_name, true); // overwrite name also list->AddSetRegValueWorkItem(root, version_key, @@ -262,16 +253,17 @@ void AddVersionKeyWorkItems(HKEY root, true); // overwrite version } -void AddProductSpecificWorkItems(bool install, +void AddProductSpecificWorkItems(const InstallationState& original_state, + const InstallerState& installer_state, const FilePath& setup_path, const Version& new_version, - const Package& package, WorkItemList* list) { - const Products& products = package.products(); + const Products& products = installer_state.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); + AddChromeFrameWorkItems(original_state, installer_state, setup_path, + new_version, p, list); } } } @@ -282,36 +274,28 @@ void AddProductSpecificWorkItems(bool install, // In the multi-install case, this value is used as the basis upon which the // package's channel value is built (by adding the ordered list of installed // products and their options). -void AddGoogleUpdateWorkItems(const InstallationState& original_state, - const InstallerState& installer_state, - const Package& package, +void AddGoogleUpdateWorkItems(const InstallerState& installer_state, WorkItemList* install_list) { // Is a multi-install product being installed or over-installed? if (installer_state.operation() != InstallerState::MULTI_INSTALL) return; - const HKEY reg_root = package.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; + const HKEY reg_root = installer_state.root_key(); const std::wstring key_path = installer_state.state_key(); ChannelInfo channel_info; // Update the "ap" value for the product being installed/updated. // It is completely acceptable for there to be no "ap" value or even no - // ClientState key. Note that we check the registry rather than - // original_state since on a fresh install the "ap" value will be present - // sans "pv" value. + // ClientState key. Note that we check the registry rather than an + // InstallationState instance since on a fresh install the "ap" value will be + // present sans "pv" value. channel_info.Initialize(RegKey(reg_root, key_path.c_str(), KEY_QUERY_VALUE)); // This is a multi-install product. bool modified = channel_info.SetMultiInstall(true); // Add the appropriate modifiers for all products and their options. - Products::const_iterator scan = package.products().begin(); - const Products::const_iterator end = package.products().end(); - for (; scan != end; ++scan) { - modified |= scan->get()->distribution()->SetChannelFlags(true, - &channel_info); - } + modified |= installer_state.SetChannelFlags(true, &channel_info); // Write the results if needed. if (modified) { @@ -324,13 +308,15 @@ void AddGoogleUpdateWorkItems(const InstallationState& original_state, std::wstring other_key; std::vector<std::wstring> keys; - keys.reserve(package.products().size()); - other_key = package.properties()->GetStateKey(); + keys.reserve(installer_state.products().size()); + other_key = + installer_state.multi_package_binaries_distribution()->GetStateKey(); if (other_key != key_path) keys.push_back(other_key); - scan = package.products().begin(); + Products::const_iterator scan = installer_state.products().begin(); + Products::const_iterator end = installer_state.products().end(); for (; scan != end; ++scan) { - other_key = scan->get()->distribution()->GetStateKey(); + other_key = (*scan)->distribution()->GetStateKey(); if (other_key != key_path) keys.push_back(other_key); } @@ -364,14 +350,15 @@ void AddGoogleUpdateWorkItems(const InstallationState& original_state, // If so, try and remove any existing uninstallation shortcuts, as we want the // uninstall to be managed entirely by the MSI machinery (accessible via the // Add/Remove programs dialog). -void AddDeleteUninstallShortcutsForMSIWorkItems(const Product& product, - WorkItemList* work_item_list) { - DCHECK(product.IsMsi()) << "This must only be called for MSI installations!"; +void AddDeleteUninstallShortcutsForMSIWorkItems( + const InstallerState& installer_state, + const Product& product, + WorkItemList* work_item_list) { + DCHECK(installer_state.is_msi()) + << "This must only be called for MSI installations!"; // First attempt to delete the old installation's ARP dialog entry. - HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; - base::win::RegKey root_key(reg_root, L"", KEY_ALL_ACCESS); + HKEY reg_root = installer_state.root_key(); std::wstring uninstall_reg(product.distribution()->GetUninstallRegPath()); WorkItem* delete_reg_key = work_item_list->AddDeleteRegKeyWorkItem( @@ -380,7 +367,7 @@ void AddDeleteUninstallShortcutsForMSIWorkItems(const Product& product, // Then attempt to delete the old installation's start menu shortcut. FilePath uninstall_link; - if (product.system_level()) { + if (installer_state.system_install()) { PathService::Get(base::DIR_COMMON_START_MENU, &uninstall_link); } else { PathService::Get(base::DIR_START_MENU, &uninstall_link); @@ -412,18 +399,16 @@ void AddDeleteUninstallShortcutsForMSIWorkItems(const Product& product, // it if not. // If these operations are successful, the function returns true, otherwise // false. -bool AppendPostInstallTasks(bool multi_install, +bool AppendPostInstallTasks(const InstallerState& installer_state, const FilePath& setup_path, const FilePath& new_chrome_exe, const Version* current_version, const Version& new_version, - const Package& package, WorkItemList* post_install_task_list) { DCHECK(post_install_task_list); - HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; - const Products& products = package.products(); + HKEY root = installer_state.root_key(); + const Products& products = installer_state.products(); // Append work items that will only be executed if this was an update. // We update the 'opv' key with the current version that is active and 'cmd' @@ -434,18 +419,18 @@ bool AppendPostInstallTasks(bool multi_install, new ConditionRunIfFileExists(new_chrome_exe))); in_use_update_work_items->set_log_message("InUseUpdateWorkItemList"); - FilePath installer_path(package.GetInstallerDirectory(new_version) + FilePath installer_path(installer_state.GetInstallerDirectory(new_version) .Append(setup_path.BaseName())); CommandLine rename(installer_path); rename.AppendSwitch(installer::switches::kRenameChromeExe); - if (package.system_level()) + if (installer_state.system_install()) rename.AppendSwitch(installer::switches::kSystemLevel); if (InstallUtil::IsChromeSxSProcess()) rename.AppendSwitch(installer::switches::kChromeSxS); - if (multi_install) + if (installer_state.is_multi_install()) rename.AppendSwitch(installer::switches::kMultiInstall); std::wstring version_key; @@ -472,18 +457,17 @@ bool AppendPostInstallTasks(bool multi_install, true); } - if (multi_install) { - PackageProperties* props = package.properties(); - if (props->ReceivesUpdates() && current_version != NULL) { - in_use_update_work_items->AddSetRegValueWorkItem( - root, - props->GetVersionKey(), - google_update::kRegOldVersionField, - UTF8ToWide(current_version->GetString()), - true); - // TODO(tommi): We should move the rename command here. We also need to - // update Upgrade::SwapNewChromeExeIfPresent. - } + if (current_version != NULL && installer_state.is_multi_install()) { + BrowserDistribution* dist = + installer_state.multi_package_binaries_distribution(); + in_use_update_work_items->AddSetRegValueWorkItem( + root, + dist->GetVersionKey(), + google_update::kRegOldVersionField, + UTF8ToWide(current_version->GetString()), + true); + // TODO(tommi): We should move the rename command here. We also need to + // update Upgrade::SwapNewChromeExeIfPresent. } post_install_task_list->AddWorkItem(in_use_update_work_items.release()); @@ -494,8 +478,7 @@ bool AppendPostInstallTasks(bool multi_install, scoped_ptr<WorkItemList> regular_update_work_items( WorkItem::CreateConditionalWorkItemList( new Not(new ConditionRunIfFileExists(new_chrome_exe)))); - regular_update_work_items->set_log_message( - "RegularUpdateWorkItemList"); + regular_update_work_items->set_log_message("RegularUpdateWorkItemList"); // Since this was not an in-use-update, delete 'opv' and 'cmd' keys. for (size_t i = 0; i < products.size(); ++i) { @@ -510,23 +493,29 @@ bool AppendPostInstallTasks(bool multi_install, post_install_task_list->AddWorkItem(regular_update_work_items.release()); } - AddRegisterComDllWorkItemsForPackage(package, current_version, new_version, - post_install_task_list); + AddRegisterComDllWorkItemsForPackage(installer_state, current_version, + new_version, post_install_task_list); - 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()) { - AddSetMsiMarkerWorkItem(*product, true, post_install_task_list); + // 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 (installer_state.is_msi()) { + for (size_t i = 0; i < products.size(); ++i) { + const Product* product = products[i]; + AddSetMsiMarkerWorkItem(installer_state, product->distribution(), true, + post_install_task_list); // We want MSI installs to take over the Add/Remove Programs shortcut. // Make a best-effort attempt to delete any shortcuts left over from // previous non-MSI installations for the same type of install (system or // per user). - AddDeleteUninstallShortcutsForMSIWorkItems(*product, + AddDeleteUninstallShortcutsForMSIWorkItems(installer_state, *product, post_install_task_list); } + if (installer_state.is_multi_install()) { + AddSetMsiMarkerWorkItem(installer_state, + installer_state.multi_package_binaries_distribution(), true, + post_install_task_list); + } } return true; @@ -534,112 +523,101 @@ bool AppendPostInstallTasks(bool multi_install, void AddInstallWorkItems(const InstallationState& original_state, const InstallerState& installer_state, - bool multi_install, const FilePath& setup_path, const FilePath& archive_path, const FilePath& src_path, const FilePath& temp_dir, const Version& new_version, scoped_ptr<Version>* current_version, - const Package& package, WorkItemList* install_list) { DCHECK(install_list); + const FilePath& target_path = installer_state.target_path(); + // A temp directory that work items need and the actual install directory. install_list->AddCreateDirWorkItem(temp_dir); - install_list->AddCreateDirWorkItem(package.path()); + install_list->AddCreateDirWorkItem(target_path); // Delete any new_chrome.exe if present (we will end up creating a new one // if required) and then copy chrome.exe FilePath new_chrome_exe( - package.path().Append(installer::kChromeNewExe)); + target_path.Append(installer::kChromeNewExe)); install_list->AddDeleteTreeWorkItem(new_chrome_exe); install_list->AddCopyTreeWorkItem( src_path.Append(installer::kChromeExe).value(), - package.path().Append(installer::kChromeExe).value(), + target_path.Append(installer::kChromeExe).value(), temp_dir.value(), WorkItem::NEW_NAME_IF_IN_USE, new_chrome_exe.value()); // Extra executable for 64 bit systems. if (Is64bit()) { install_list->AddCopyTreeWorkItem( src_path.Append(installer::kWowHelperExe).value(), - package.path().Append(installer::kWowHelperExe).value(), + target_path.Append(installer::kWowHelperExe).value(), temp_dir.value(), WorkItem::ALWAYS); } // If it is system level install copy the version folder (since we want to // take the permissions of %ProgramFiles% folder) otherwise just move it. - if (package.system_level()) { + if (installer_state.system_install()) { install_list->AddCopyTreeWorkItem( src_path.Append(UTF8ToWide(new_version.GetString())).value(), - package.path().Append(UTF8ToWide(new_version.GetString())).value(), + target_path.Append(UTF8ToWide(new_version.GetString())).value(), temp_dir.value(), WorkItem::ALWAYS); } else { install_list->AddMoveTreeWorkItem( src_path.Append(UTF8ToWide(new_version.GetString())).value(), - package.path().Append(UTF8ToWide(new_version.GetString())).value(), + target_path.Append(UTF8ToWide(new_version.GetString())).value(), temp_dir.value()); } // Copy the default Dictionaries only if the folder doesn't exist already. install_list->AddCopyTreeWorkItem( src_path.Append(installer::kDictionaries).value(), - package.path().Append(installer::kDictionaries).value(), + target_path.Append(installer::kDictionaries).value(), temp_dir.value(), WorkItem::IF_NOT_PRESENT); // Delete any old_chrome.exe if present. install_list->AddDeleteTreeWorkItem( - package.path().Append(installer::kChromeOldExe)); + target_path.Append(installer::kChromeOldExe)); // Copy installer in install directory and // add shortcut in Control Panel->Add/Remove Programs. - AddInstallerCopyTasks(setup_path, archive_path, temp_dir, new_version, - install_list, package); + AddInstallerCopyTasks(installer_state, setup_path, archive_path, temp_dir, + new_version, install_list); - HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + const HKEY root = installer_state.root_key(); - const Products& products = package.products(); + const Products& products = installer_state.products(); for (size_t i = 0; i < products.size(); ++i) { const Product* product = products[i]; - AddUninstallShortcutWorkItems(setup_path, new_version, install_list, - *product); + AddUninstallShortcutWorkItems(installer_state, setup_path, new_version, + install_list, *product); - AddVersionKeyWorkItems(root, *product, new_version, install_list); + AddVersionKeyWorkItems(root, product->distribution(), new_version, + install_list); } - if (multi_install) { - PackageProperties* props = package.properties(); - if (props->ReceivesUpdates()) { - std::wstring version_key(props->GetVersionKey()); - install_list->AddCreateRegKeyWorkItem(root, version_key); - install_list->AddSetRegValueWorkItem(root, version_key, - google_update::kRegVersionField, - UTF8ToWide(new_version.GetString()), - true); // overwrite version - install_list->AddSetRegValueWorkItem(root, version_key, - google_update::kRegNameField, - ASCIIToWide(installer::PackageProperties::kPackageProductName), - true); // overwrite name also - } + if (installer_state.is_multi_install()) { + AddVersionKeyWorkItems(root, + installer_state.multi_package_binaries_distribution(), new_version, + install_list); } // Add any remaining work items that involve special settings for // each product. - AddProductSpecificWorkItems(true, setup_path, new_version, package, - install_list); + AddProductSpecificWorkItems(original_state, installer_state, setup_path, + new_version, install_list); - AddGoogleUpdateWorkItems(original_state, installer_state, package, - install_list); + AddGoogleUpdateWorkItems(installer_state, install_list); // Append the tasks that run after the installation. - AppendPostInstallTasks(multi_install, + AppendPostInstallTasks(installer_state, setup_path, new_chrome_exe, current_version->get(), new_version, - package, install_list); } @@ -664,93 +642,74 @@ void AddRegisterComDllWorkItems(const FilePath& dll_folder, } } -void AddSetMsiMarkerWorkItem(const Product& product, +void AddSetMsiMarkerWorkItem(const InstallerState& installer_state, + BrowserDistribution* dist, bool set, WorkItemList* work_item_list) { DCHECK(work_item_list); - BrowserDistribution* dist = product.distribution(); - HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; DWORD msi_value = set ? 1 : 0; WorkItem* set_msi_work_item = work_item_list->AddSetRegValueWorkItem( - reg_root, dist->GetStateKey(), google_update::kRegMSIField, - msi_value, true); + installer_state.root_key(), dist->GetStateKey(), + google_update::kRegMSIField, msi_value, true); DCHECK(set_msi_work_item); set_msi_work_item->set_ignore_failure(true); set_msi_work_item->set_log_message("Could not write MSI marker!"); } -void AddChromeFrameWorkItems(bool install, +void AddChromeFrameWorkItems(const InstallationState& original_state, + const InstallerState& installer_state, const FilePath& setup_path, const Version& new_version, const Product& product, WorkItemList* list) { DCHECK(product.is_chrome_frame()); - if (!product.package().multi_install()) { + if (!installer_state.is_multi_install()) { VLOG(1) << "Not adding GCF specific work items for single install."; return; } - const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); - - BrowserDistribution* cf = BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_FRAME, prefs); - std::wstring version_key(cf->GetVersionKey()); - - // TODO(tommi): This assumes we know exactly how ShouldCreateUninstallEntry - // is implemented. Since there is logic in ChromeFrameDistribution for how - // to determine when this is enabled, this is how we have to figure out if - // this feature is enabled right now, but it's a hack and we need a cleaner - // way to figure this out. - // Note that we cannot just check the master preferences for - // kChromeFrameReadyMode, since there are other things that need to be correct - // in the environment in order to enable this feature. - bool ready_mode = !product.distribution()->ShouldCreateUninstallEntry(); - - HKEY root = product.package().system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; + std::wstring version_key(product.distribution()->GetVersionKey()); + bool ready_mode = product.HasOption(kOptionReadyMode); + HKEY root = installer_state.root_key(); + const bool is_install = + (installer_state.operation() != InstallerState::UNINSTALL); bool update_chrome_uninstall_command = false; + BrowserDistribution* dist = + installer_state.multi_package_binaries_distribution(); 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, - static_cast<int64>(install ? 1 : 0), // The value we want to set. - install ? false : true); // Overwrite existing value. - if (install) { - FilePath installer_path(product.package() + list->AddCreateRegKeyWorkItem(root, dist->GetStateKey()); + list->AddSetRegValueWorkItem(root, dist->GetStateKey(), + kChromeFrameReadyModeField, + static_cast<int64>(is_install ? 1 : 0), // The value we want to set. + is_install ? false : true); // Overwrite existing value. + if (is_install) { + FilePath installer_path(installer_state .GetInstallerDirectory(new_version).Append(setup_path.BaseName())); CommandLine basic_cl(installer_path); - basic_cl.AppendSwitch(installer::switches::kChromeFrame); - basic_cl.AppendSwitch(installer::switches::kMultiInstall); - - if (product.package().system_level()) - basic_cl.AppendSwitch(installer::switches::kSystemLevel); + basic_cl.AppendSwitch(switches::kChromeFrame); + basic_cl.AppendSwitch(switches::kMultiInstall); - if (InstallUtil::IsChromeSxSProcess()) - basic_cl.AppendSwitch(installer::switches::kChromeSxS); + if (installer_state.system_install()) + basic_cl.AppendSwitch(switches::kSystemLevel); CommandLine temp_opt_out(basic_cl); - temp_opt_out.AppendSwitch( - installer::switches::kChromeFrameReadyModeTempOptOut); + temp_opt_out.AppendSwitch(switches::kChromeFrameReadyModeTempOptOut); CommandLine end_temp_opt_out(basic_cl); end_temp_opt_out.AppendSwitch( - installer::switches::kChromeFrameReadyModeEndTempOptOut); + switches::kChromeFrameReadyModeEndTempOptOut); CommandLine opt_out(installer_path); - AppendUninstallCommandLineFlags(&opt_out, product); + AppendUninstallCommandLineFlags(installer_state, product, &opt_out); // Force Uninstall silences the prompt to reboot to complete uninstall. - opt_out.AppendSwitch(installer::switches::kForceUninstall); + opt_out.AppendSwitch(switches::kForceUninstall); CommandLine opt_in(basic_cl); - opt_in.AppendSwitch( - installer::switches::kChromeFrameReadyModeOptIn); + opt_in.AppendSwitch(switches::kChromeFrameReadyModeOptIn); list->AddSetRegValueWorkItem(root, version_key, google_update::kRegCFTempOptOutCmdField, @@ -769,8 +728,8 @@ void AddChromeFrameWorkItems(bool 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); + (installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER) == + NULL); } } else { // It doesn't matter here if we're installing or uninstalling Chrome Frame. @@ -784,15 +743,11 @@ void AddChromeFrameWorkItems(bool install, // uninstallation command line does not include the --chrome-frame switch // so that uninstalling Chrome will no longer uninstall Chrome Frame. - if (RegKey(root, product.package().properties()->GetStateKey().c_str(), - KEY_QUERY_VALUE).Valid()) { - list->AddDeleteRegValueWorkItem(root, - product.package().properties()->GetStateKey(), - installer::kChromeFrameReadyModeField); - } + list->AddDeleteRegValueWorkItem(root, dist->GetStateKey(), + kChromeFrameReadyModeField); - const Product* chrome = installer::FindProduct(product.package().products(), - BrowserDistribution::CHROME_BROWSER); + const Product* chrome = + installer_state.FindProduct(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. @@ -800,15 +755,15 @@ void AddChromeFrameWorkItems(bool install, // 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* dist = BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, - MasterPreferences::ForCurrentProcess()); + const ProductState* chrome_state = original_state.GetProductState( + installer_state.system_install(), + BrowserDistribution::CHROME_BROWSER); update_chrome_uninstall_command = - IsInstalledAsMulti(product.system_level(), dist); + (chrome_state != NULL) && chrome_state->is_multi_install(); } } - if (!ready_mode || !install) { + if (!ready_mode || !is_install) { list->AddDeleteRegValueWorkItem(root, version_key, google_update::kRegCFTempOptOutCmdField); list->AddDeleteRegValueWorkItem(root, version_key, @@ -823,44 +778,41 @@ void AddChromeFrameWorkItems(bool install, // 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> package(new Package(pack.multi_install(), - pack.system_level(), pack.path(), pack.properties())); - scoped_refptr<Product> chrome_product(new Product(chrome_dist, package)); - AddUninstallShortcutWorkItems(setup_path, new_version, list, - *chrome_product.get()); + const ProductState* chrome_state = original_state.GetProductState( + installer_state.system_install(), + BrowserDistribution::CHROME_BROWSER); + if (chrome_state != NULL) { + DCHECK(chrome_state->is_multi_install()); + Product chrome(BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER)); + chrome.InitializeFromUninstallCommand(chrome_state->uninstall_command()); + AddUninstallShortcutWorkItems(installer_state, setup_path, + chrome_state->version(), list, chrome); + } else { + NOTREACHED() << "What happened to Chrome?"; + } } } -void AppendUninstallCommandLineFlags(CommandLine* uninstall_cmd, - const Product& product) { +void AppendUninstallCommandLineFlags(const InstallerState& installer_state, + const Product& product, + CommandLine* uninstall_cmd) { DCHECK(uninstall_cmd); uninstall_cmd->AppendSwitch(installer::switches::kUninstall); // Append the product-specific uninstall flags. - product.distribution()->AppendUninstallCommandLineFlags(uninstall_cmd); - if (product.IsMsi()) { + product.AppendProductFlags(uninstall_cmd); + if (installer_state.is_msi()) { 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()) + if (installer_state.system_install()) uninstall_cmd->AppendSwitch(installer::switches::kSystemLevel); - - // Propagate switches obtained from preferences as well. - const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); - if (prefs.is_multi_install()) { - uninstall_cmd->AppendSwitch(installer::switches::kMultiInstall); - } - bool value = false; - if (prefs.GetBool(installer::master_preferences::kVerboseLogging, - &value) && value) + if (installer_state.verbose_logging()) uninstall_cmd->AppendSwitch(installer::switches::kVerboseLogging); } diff --git a/chrome/installer/setup/install_worker.h b/chrome/installer/setup/install_worker.h index 167ed9b..0769ec3 100644 --- a/chrome/installer/setup/install_worker.h +++ b/chrome/installer/setup/install_worker.h @@ -13,6 +13,7 @@ #include "base/scoped_ptr.h" +class BrowserDistribution; class CommandLine; class FilePath; class Version; @@ -25,6 +26,15 @@ class InstallerState; class Package; class Product; +// Adds work items that make registry adjustments for Google Update. When a +// product is installed (including overinstall), Google Update will write the +// channel ("ap") value into either Chrome or Chrome Frame's ClientState key. +// In the multi-install case, this value is used as the basis upon which the +// package's channel value is built (by adding the ordered list of installed +// products and their options). +void AddGoogleUpdateWorkItems(const InstallerState& installer_state, + WorkItemList* install_list); + // Builds the complete WorkItemList used to build the set of installation steps // needed to lay down one or more installed products. // @@ -38,14 +48,12 @@ class Product; // does not need to exist. void AddInstallWorkItems(const InstallationState& original_state, const InstallerState& installer_state, - bool multi_install, const FilePath& setup_path, const FilePath& archive_path, const FilePath& src_path, const FilePath& temp_dir, const Version& new_version, scoped_ptr<Version>* current_version, - const Package& package, WorkItemList* install_list); // Appends registration or unregistration work items to |work_item_list| for the @@ -64,34 +72,34 @@ void AddRegisterComDllWorkItems(const FilePath& dll_folder, bool ignore_failures, WorkItemList* work_item_list); -void AddSetMsiMarkerWorkItem(const Product& product, +void AddSetMsiMarkerWorkItem(const InstallerState& installer_state, + BrowserDistribution* dist, bool set, WorkItemList* work_item_list); // 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, +void AddChromeFrameWorkItems(const InstallationState& original_state, + const InstallerState& installer_state, + const FilePath& setup_path, + const Version& new_version, + const Product& product, WorkItemList* list); - // This method adds work items to create (or update) Chrome uninstall entry in // either the Control Panel->Add/Remove Programs list or in the Omaha client // state key if running under an MSI installer. -void AddUninstallShortcutWorkItems(const FilePath& setup_path, - const Version& new_version, - WorkItemList* install_list, - const Product& product); - -void AddUninstallShortcutWorkItems(const FilePath& setup_path, +void AddUninstallShortcutWorkItems(const InstallerState& installer_state, + const FilePath& setup_path, const Version& new_version, WorkItemList* install_list, const Product& product); // Utility method currently shared between install.cc and install_worker.cc -void AppendUninstallCommandLineFlags(CommandLine* uninstall_cmd, - const Product& product); +void AppendUninstallCommandLineFlags(const InstallerState& installer_state, + const Product& product, + CommandLine* uninstall_cmd); } // namespace installer diff --git a/chrome/installer/setup/install_worker_unittest.cc b/chrome/installer/setup/install_worker_unittest.cc index 51f066e..5703ba3 100644 --- a/chrome/installer/setup/install_worker_unittest.cc +++ b/chrome/installer/setup/install_worker_unittest.cc @@ -8,19 +8,13 @@ #include "base/version.h" #include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/installer_state.h" -#include "chrome/installer/util/package.h" -#include "chrome/installer/util/package_properties.h" #include "chrome/installer/util/work_item_list.h" #include "testing/gtest/include/gtest/gtest.h" #include "testing/gmock/include/gmock/gmock.h" -using installer::ChromiumPackageProperties; using installer::InstallationState; using installer::InstallerState; -using installer::Package; -using installer::PackageProperties; -using installer::PackagePropertiesImpl; using installer::ProductState; using ::testing::_; @@ -66,20 +60,17 @@ class MockWorkItemList : public WorkItemList { bool)); }; +class MockProductState : public ProductState { + public: + // Takes ownership of |version|. + void set_version(Version* version) { version_.reset(version); } +}; + // Okay, so this isn't really a mock as such, but it does add setter methods // to make it easier to build custom InstallationStates. class MockInstallationState : public InstallationState { public: // Included for testing. - void SetMultiPackageState(bool system_install, - const ProductState& package_state) { - ProductState& target = - (system_install ? system_products_ : user_products_) - [MULTI_PACKAGE_INDEX]; - target.CopyFrom(package_state); - } - - // Included for testing. void SetProductState(bool system_install, BrowserDistribution::Type type, const ProductState& product_state) { @@ -91,8 +82,8 @@ class MockInstallationState : public InstallationState { class MockInstallerState : public InstallerState { public: - void set_system_install(bool system_install) { - system_install_ = system_install; + void set_level(Level level) { + level_ = level; } void set_operation(Operation operation) { operation_ = operation; } @@ -125,14 +116,13 @@ class InstallWorkerTest : public testing::Test { } virtual void TearDown() { - } MockInstallationState* BuildChromeSingleSystemInstallationState() { scoped_ptr<MockInstallationState> installation_state( new MockInstallationState()); - ProductState product_state; + MockProductState product_state; product_state.set_version(current_version_->Clone()); // Do not call SetMultiPackageState since this is a single install. installation_state->SetProductState(true, @@ -142,14 +132,19 @@ class InstallWorkerTest : public testing::Test { return installation_state.release(); } - MockInstallerState* BuildChromeSingleSystemInstallerState() { + MockInstallerState* BuildChromeSingleSystemInstallerState( + const InstallationState& machine_state) { scoped_ptr<MockInstallerState> installer_state(new MockInstallerState()); - installer_state->set_system_install(true); + installer_state->set_level(InstallerState::SYSTEM_LEVEL); installer_state->set_operation(InstallerState::SINGLE_INSTALL_OR_UPDATE); // Hope this next one isn't checked for now. installer_state->set_state_key(L"PROBABLY_INVALID_REG_PATH"); - + const ProductState* chrome = + machine_state.GetProductState(true, + BrowserDistribution::CHROME_BROWSER); + installer_state->AddProductFromState(BrowserDistribution::CHROME_BROWSER, + *chrome); return installer_state.release(); } @@ -173,18 +168,7 @@ TEST_F(InstallWorkerTest, TestInstallChromeSingleSystem) { BuildChromeSingleSystemInstallationState()); scoped_ptr<InstallerState> installer_state( - BuildChromeSingleSystemInstallerState()); - - // This MUST outlive the package, since the package doesn't assume ownership - // of it. Note: This feels like an implementation bug: - // The PackageProperties <-> Package relationship is 1:1 and nothing else - // uses the PackageProperties. I have the feeling that PackageProperties, and - // perhaps Package itself should not exist at all. - scoped_ptr<PackageProperties> package_properties( - new ChromiumPackageProperties()); - - scoped_refptr<Package> package( - new Package(false, true, installation_path_, package_properties.get())); + BuildChromeSingleSystemInstallerState(*installation_state)); // Set up some expectations. // TODO(robertshield): Set up some real expectations. @@ -193,13 +177,11 @@ TEST_F(InstallWorkerTest, TestInstallChromeSingleSystem) { AddInstallWorkItems(*installation_state.get(), *installer_state.get(), - false, setup_path_, archive_path_, src_path_, temp_dir_, *new_version_.get(), ¤t_version_, - *package.get(), &work_item_list); } diff --git a/chrome/installer/setup/setup_main.cc b/chrome/installer/setup/setup_main.cc index b385b8b..78d641c 100644 --- a/chrome/installer/setup/setup_main.cc +++ b/chrome/installer/setup/setup_main.cc @@ -20,6 +20,7 @@ #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/values.h" +#include "base/win/registry.h" #include "base/win/scoped_handle.h" #include "base/win/win_util.h" #include "base/win/windows_version.h" @@ -31,6 +32,7 @@ #include "chrome/installer/setup/setup_util.h" #include "chrome/installer/setup/uninstall.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/delete_tree_work_item.h" #include "chrome/installer/util/google_update_settings.h" @@ -45,7 +47,6 @@ #include "chrome/installer/util/lzma_util.h" #include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/master_preferences_constants.h" -#include "chrome/installer/util/package_properties.h" #include "chrome/installer/util/shell_util.h" #include "chrome/installer/util/util_constants.h" @@ -54,11 +55,8 @@ using installer::InstallerState; using installer::InstallationState; using installer::Product; -using installer::ProductPackageMapping; using installer::ProductState; using installer::Products; -using installer::Package; -using installer::Packages; using installer::MasterPreferences; const wchar_t kChromePipeName[] = L"\\\\.\\pipe\\ChromeCrashServices"; @@ -83,7 +81,7 @@ namespace { // present on the system already. As the final step the new archive file // is unpacked in the path specified by parameter "output_directory". DWORD UnPackArchive(const FilePath& archive, - const Package& installation, + const InstallerState& installer_state, const FilePath& temp_path, const FilePath& output_directory, bool& incremental_install) { @@ -98,7 +96,7 @@ DWORD UnPackArchive(const FilePath& archive, FilePath uncompressed_archive(temp_path.Append(installer::kChromeArchive)); scoped_ptr<Version> archive_version( - installer::GetVersionFromArchiveDir(installation.path())); + installer::GetVersionFromArchiveDir(installer_state.target_path())); // Check if this is differential update and if it is, patch it to the // installer archive that should already be on the machine. We assume @@ -112,7 +110,7 @@ DWORD UnPackArchive(const FilePath& archive, return installer::CHROME_NOT_INSTALLED; } - FilePath existing_archive(installation.path().Append( + FilePath existing_archive(installer_state.target_path().Append( UTF8ToWide(archive_version->GetString()))); existing_archive = existing_archive.Append(installer::kInstallerDir); existing_archive = existing_archive.Append(installer::kChromeArchive); @@ -136,12 +134,11 @@ DWORD UnPackArchive(const FilePath& archive, // system and a key called 'opv' in the registry. This function will move // new_chrome.exe to chrome.exe and delete 'opv' key in one atomic operation. installer::InstallStatus RenameChromeExecutables( - const Package& installation) { - FilePath chrome_exe(installation.path().Append(installer::kChromeExe)); - FilePath chrome_old_exe(installation.path().Append( - installer::kChromeOldExe)); - FilePath chrome_new_exe(installation.path().Append( - installer::kChromeNewExe)); + const InstallerState& installer_state) { + const FilePath &target_path = installer_state.target_path(); + FilePath chrome_exe(target_path.Append(installer::kChromeExe)); + FilePath chrome_old_exe(target_path.Append(installer::kChromeOldExe)); + FilePath chrome_new_exe(target_path.Append(installer::kChromeNewExe)); scoped_ptr<WorkItemList> install_list(WorkItem::CreateWorkItemList()); install_list->AddDeleteTreeWorkItem(chrome_old_exe); @@ -158,9 +155,8 @@ installer::InstallStatus RenameChromeExecutables( std::wstring()); install_list->AddDeleteTreeWorkItem(chrome_new_exe); - HKEY reg_root = installation.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; - const Products& products = installation.products(); + HKEY reg_root = installer_state.root_key(); + const Products& products = installer_state.products(); for (size_t i = 0; i < products.size(); ++i) { const Product* product = products[i]; BrowserDistribution* browser_dist = product->distribution(); @@ -182,44 +178,218 @@ installer::InstallStatus RenameChromeExecutables( return ret; } +// The supported multi-install modes are: +// --multi-install --chrome --chrome-frame --ready-mode +// - If a non-multi Chrome Frame installation is present, Chrome Frame is +// removed from |installer_state|'s list of products (thereby preserving +// the existing SxS install). +// - If a multi Chrome Frame installation is present, its options are +// preserved (i.e., the --ready-mode command-line option is ignored). +// --multi-install --chrome-frame +// - If a non-multi Chrome Frame installation is present, fail. +// - If a Chrome installation on a different channel is present, fail. +// - If --ready-mode and no Chrome installation is present, fail. +// - If a Chrome installation is present, add it to the set of products to +// install. +bool CheckMultiInstallConditions(const InstallationState& original_state, + InstallerState* installer_state, + installer::InstallStatus* status) { + const Products& products = installer_state->products(); + DCHECK(products.size()); + + bool is_first_install = true; + const bool system_level = installer_state->system_install(); + + if (installer_state->is_multi_install()) { + const Product* chrome = + installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER); + const Product* chrome_frame = + installer_state->FindProduct(BrowserDistribution::CHROME_FRAME); + const ProductState* cf_state = + original_state.GetProductState(system_level, + BrowserDistribution::CHROME_FRAME); + if (chrome != NULL) { + if (chrome_frame != NULL && + chrome_frame->HasOption(installer::kOptionReadyMode)) { + // We're being asked to install Chrome with Chrome Frame in ready-mode. + // This is an optimistic operation: if a SxS install of Chrome Frame + // is already present, don't touch it; if a multi-install of Chrome + // Frame is present, preserve its settings (ready-mode, CEEE). + if (cf_state != NULL) { + installer_state->RemoveProduct(chrome_frame); + chrome_frame = NULL; + if (cf_state->is_multi_install()) { + chrome_frame = installer_state->AddProductFromState( + BrowserDistribution::CHROME_FRAME, *cf_state); + VLOG(1) << "Upgrading existing multi-install Chrome Frame rather " + "than installing in ready-mode."; + } else { + VLOG(1) << "Skipping upgrade of single-install Chrome Frame rather " + "than installing in ready-mode."; + } + } else { + VLOG(1) << "Performing initial install of Chrome Frame ready-mode."; + } + } + } else if (chrome_frame != NULL) { + // We're being asked to install or update Chrome Frame alone. + const ProductState* chrome_state = + original_state.GetProductState(system_level, + BrowserDistribution::CHROME_BROWSER); + if (chrome_state != NULL) { + base::win::RegKey key; + installer::ChannelInfo cf_channel; + // Chrome Frame may not yet be installed, so peek into the registry + // directly to see what channel Google Update has specified. There will + // be no value if we're not being managed by Google Update. + if (key.Open(installer_state->root_key(), + chrome_frame->distribution()->GetStateKey().c_str(), + KEY_QUERY_VALUE) == ERROR_SUCCESS) { + cf_channel.Initialize(key); + } + // Fail if Chrome is already installed but is on a different update + // channel. + if (!cf_channel.EqualsBaseOf(chrome_state->channel())) { + LOG(ERROR) << "Cannot install Chrome Frame because existing Chrome " + "install is on a different update channel."; + *status = installer::CONFLICTING_CHANNEL_EXISTS; + InstallUtil::WriteInstallerResult(system_level, + installer_state->state_key(), *status, + IDS_INSTALL_CONFLICTING_CHANNEL_EXISTS_BASE, NULL); + return false; + } + // Otherwise, add Chrome to the set of products (making it multi-install + // in the process) so that it is updated, too. + scoped_ptr<Product> multi_chrome(new Product( + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER))); + multi_chrome->SetOption(installer::kOptionMultiInstall, true); + chrome = installer_state->AddProduct(&multi_chrome); + VLOG(1) << "Upgrading existing multi-install Chrome browser along with " + << chrome_frame->distribution()->GetApplicationName(); + } else if (chrome_frame->HasOption(installer::kOptionReadyMode)) { + // Chrome Frame with ready-mode is to be installed, yet Chrome is + // neither installed nor being installed. Fail. + LOG(ERROR) << "Cannot install Chrome Frame in ready mode without " + "Chrome."; + *status = installer::READY_MODE_REQUIRES_CHROME; + InstallUtil::WriteInstallerResult(system_level, + installer_state->state_key(), *status, + IDS_INSTALL_READY_MODE_REQUIRES_CHROME_BASE, NULL); + return false; + } + } + + // Fail if we're installing Chrome Frame when a single-install of it is + // already installed. + // TODO(grt): Add support for migration of Chrome Frame from single- to + // multi-install. + if (chrome_frame != NULL && + cf_state != NULL && !cf_state->is_multi_install()) { + LOG(ERROR) << "Cannot migrate existing Chrome Frame installation to " + "multi-install."; + *status = installer::NON_MULTI_INSTALLATION_EXISTS; + InstallUtil::WriteInstallerResult(system_level, + installer_state->state_key(), *status, + IDS_INSTALL_NON_MULTI_INSTALLATION_EXISTS_BASE, NULL); + return false; + } + } else if (DCHECK_IS_ON()) { + // It isn't possible to stuff two products into a single-install + // InstallerState. Abort the process here in debug builds just in case + // someone finds a way. + DCHECK_EQ(1U, products.size()); + } + + return true; +} + +// In multi-install, adds all products to |installer_state| that are +// multi-installed and must be updated along with the products already present +// in |installer_state|. +void AddExistingMultiInstalls(const InstallationState& original_state, + InstallerState* installer_state) { + if (installer_state->is_multi_install()) { + BrowserDistribution::Type product_checks[] = { + BrowserDistribution::CHROME_BROWSER, + BrowserDistribution::CHROME_FRAME + }; + + for (size_t i = 0; i < arraysize(product_checks); ++i) { + BrowserDistribution::Type type = product_checks[i]; + if (!installer_state->FindProduct(type)) { + const ProductState* state = + original_state.GetProductState(installer_state->system_install(), + type); + if ((state != NULL) && state->is_multi_install()) { + installer_state->AddProductFromState(type, *state); + VLOG(1) << "Product already installed and must be included: " + << BrowserDistribution::GetSpecificDistribution( + type)->GetApplicationName(); + } + } + } + } +} + +// Checks for compatibility between the current state of the system and the +// desired operation. Also applies policy that mutates the desired operation; +// specifically, the |installer_state| object. +// Also blocks simultaneous user-level and system-level installs. In the case +// of trying to install user-level Chrome when system-level exists, the +// existing system-level Chrome is launched. bool CheckPreInstallConditions(const InstallationState& original_state, - const InstallerState& installer_state, - const Package& installation, - const MasterPreferences& prefs, + InstallerState* installer_state, installer::InstallStatus* status) { - const Products& products = installation.products(); + const Products& products = installer_state->products(); DCHECK(products.size()); + if (!CheckMultiInstallConditions(original_state, installer_state, status)) + return false; + bool is_first_install = true; - const bool system_level = installation.system_level(); + const bool system_level = installer_state->system_install(); for (size_t i = 0; i < products.size(); ++i) { const Product* product = products[i]; BrowserDistribution* browser_dist = product->distribution(); + + // Check for an existing installation of the product. const ProductState* product_state = original_state.GetProductState(system_level, browser_dist->GetType()); - if (product_state != NULL) + if (product_state != NULL) { is_first_install = false; + // Block downgrades from multi-install to single-install. + if (!installer_state->is_multi_install() && + product_state->is_multi_install()) { + LOG(ERROR) << "Multi-install " << browser_dist->GetApplicationName() + << " exists; aborting single install."; + *status = installer::MULTI_INSTALLATION_EXISTS; + InstallUtil::WriteInstallerResult(system_level, + installer_state->state_key(), *status, + IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE, NULL); + return false; + } + } // Check to avoid simultaneous per-user and per-machine installs. const ProductState* other_state = original_state.GetProductState(!system_level, browser_dist->GetType()); - if (other_state != NULL) { LOG(ERROR) << "Already installed version " << other_state->version().GetString() << " conflicts with the current install mode."; 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. + // installation. Instruct Google Update to launch the existing one. + // There should be no error dialog. FilePath chrome_exe(installer::GetChromeInstallPath(!system_level, browser_dist)); if (chrome_exe.empty()) { // If we failed to construct install path. Give up. *status = installer::OS_ERROR; InstallUtil::WriteInstallerResult(system_level, - installer_state.state_key(), *status, IDS_INSTALL_OS_ERROR_BASE, + installer_state->state_key(), *status, IDS_INSTALL_OS_ERROR_BASE, NULL); } else { *status = installer::EXISTING_VERSION_LAUNCHED; @@ -227,7 +397,7 @@ bool CheckPreInstallConditions(const InstallationState& original_state, CommandLine cmd(chrome_exe); cmd.AppendSwitch(switches::kFirstRun); InstallUtil::WriteInstallerResult(system_level, - installer_state.state_key(), *status, 0, NULL); + installer_state->state_key(), *status, 0, NULL); VLOG(1) << "Launching existing system-level chrome instead."; base::LaunchApp(cmd, false, false, NULL); } @@ -237,7 +407,7 @@ bool CheckPreInstallConditions(const InstallationState& original_state, // If the following compile assert fires it means that the InstallStatus // enumeration changed which will break the contract between the old // chrome installed and the new setup.exe that is trying to upgrade. - COMPILE_ASSERT(installer::SXS_OPTION_NOT_SUPPORTED == 33, + COMPILE_ASSERT(installer::CONFLICTING_CHANNEL_EXISTS == 39, dont_change_enum); // This is an update, not an install. Omaha should know the difference @@ -247,23 +417,28 @@ bool CheckPreInstallConditions(const InstallationState& original_state, int str_id = system_level ? IDS_INSTALL_USER_LEVEL_EXISTS_BASE : IDS_INSTALL_SYSTEM_LEVEL_EXISTS_BASE; InstallUtil::WriteInstallerResult(system_level, - installer_state.state_key(), *status, str_id, NULL); + installer_state->state_key(), *status, str_id, NULL); return false; } } + // See what products are already installed in multi mode. When we do multi + // installs, we must upgrade all installations since they share the binaries. + AddExistingMultiInstalls(original_state, installer_state); + // If no previous installation of Chrome, make sure installation directory // either does not exist or can be deleted (i.e. is not locked by some other // process). if (is_first_install) { - if (file_util::PathExists(installation.path()) && - !file_util::Delete(installation.path(), true)) { - LOG(ERROR) << "Installation directory " << installation.path().value() + if (file_util::PathExists(installer_state->target_path()) && + !file_util::Delete(installer_state->target_path(), true)) { + LOG(ERROR) << "Installation directory " + << installer_state->target_path().value() << " exists and can not be deleted."; *status = installer::INSTALL_DIR_IN_USE; int str_id = IDS_INSTALL_DIR_IN_USE_BASE; InstallUtil::WriteInstallerResult(system_level, - installer_state.state_key(), *status, str_id, NULL); + installer_state->state_key(), *status, str_id, NULL); return false; } } @@ -271,13 +446,15 @@ bool CheckPreInstallConditions(const InstallationState& original_state, return true; } -installer::InstallStatus InstallChrome(const InstallationState& original_state, - const InstallerState& installer_state, - const CommandLine& cmd_line, const Package& installation, - const MasterPreferences& prefs) { +installer::InstallStatus InstallProducts( + const InstallationState& original_state, + const CommandLine& cmd_line, + const MasterPreferences& prefs, + InstallerState* installer_state) { + const bool system_install = installer_state->system_install(); installer::InstallStatus install_status = installer::UNKNOWN_STATUS; - if (!CheckPreInstallConditions(original_state, installer_state, installation, - prefs, &install_status)) + if (!CheckPreInstallConditions(original_state, installer_state, + &install_status)) return install_status; // For install the default location for chrome.packed.7z is in current @@ -291,15 +468,15 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, installer::switches::kInstallArchive); } VLOG(1) << "Archive found to install Chrome " << archive.value(); - const Products& products = installation.products(); + const Products& products = installer_state->products(); // 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. FilePath temp_path; if (!file_util::CreateNewTempDirectory(L"chrome_", &temp_path)) { LOG(ERROR) << "Could not create temporary path."; - InstallUtil::WriteInstallerResult(installer_state.system_install(), - installer_state.state_key(), installer::TEMP_DIR_FAILED, + InstallUtil::WriteInstallerResult(system_install, + installer_state->state_key(), installer::TEMP_DIR_FAILED, IDS_INSTALL_TEMP_DIR_FAILED_BASE, NULL); return installer::TEMP_DIR_FAILED; } @@ -307,11 +484,11 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, FilePath unpack_path(temp_path.Append(installer::kInstallSourceDir)); bool incremental_install = false; - if (UnPackArchive(archive, installation, temp_path, unpack_path, + if (UnPackArchive(archive, *installer_state, temp_path, unpack_path, incremental_install)) { install_status = installer::UNCOMPRESSION_FAILED; - InstallUtil::WriteInstallerResult(installer_state.system_install(), - installer_state.state_key(), install_status, + InstallUtil::WriteInstallerResult(system_install, + installer_state->state_key(), install_status, IDS_INSTALL_UNCOMPRESSION_FAILED_BASE, NULL); } else { VLOG(1) << "unpacked to " << unpack_path.value(); @@ -321,8 +498,8 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, if (!installer_version.get()) { LOG(ERROR) << "Did not find any valid version in installer."; install_status = installer::INVALID_ARCHIVE; - InstallUtil::WriteInstallerResult(installer_state.system_install(), - installer_state.state_key(), install_status, + InstallUtil::WriteInstallerResult(system_install, + installer_state->state_key(), install_status, IDS_INSTALL_INVALID_ARCHIVE_BASE, NULL); } else { // TODO(tommi): Move towards having only a single version that is common @@ -332,10 +509,10 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, // (or rather must) be upgraded. VLOG(1) << "version to install: " << installer_version->GetString(); bool higher_version_installed = false; - for (size_t i = 0; i < installation.products().size(); ++i) { - const Product* product = installation.products()[i]; + for (size_t i = 0; i < installer_state->products().size(); ++i) { + const Product* product = installer_state->products()[i]; const ProductState* product_state = - original_state.GetProductState(installer_state.system_install(), + original_state.GetProductState(system_install, product->distribution()->GetType()); if (product_state != NULL && (product_state->version().CompareTo(*installer_version) > 0)) { @@ -346,12 +523,12 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, if (product->is_chrome()) { // TODO(robertshield): We should take the installer result text // strings from the Product. - InstallUtil::WriteInstallerResult(installer_state.system_install(), - installer_state.state_key(), install_status, + InstallUtil::WriteInstallerResult(system_install, + installer_state->state_key(), install_status, IDS_INSTALL_HIGHER_VERSION_BASE, NULL); } else { - InstallUtil::WriteInstallerResult(installer_state.system_install(), - installer_state.state_key(), install_status, + InstallUtil::WriteInstallerResult(system_install, + installer_state->state_key(), install_status, IDS_INSTALL_HIGHER_VERSION_CF_BASE, NULL); } } @@ -364,33 +541,37 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, FilePath prefs_source_path(cmd_line.GetSwitchValueNative( installer::switches::kInstallerData)); install_status = installer::InstallOrUpdateProduct(original_state, - installer_state, cmd_line.GetProgram(), archive_to_copy, temp_path, - prefs_source_path, prefs, *installer_version, installation); + *installer_state, cmd_line.GetProgram(), archive_to_copy, temp_path, + prefs_source_path, prefs, *installer_version); int install_msg_base = IDS_INSTALL_FAILED_BASE; std::wstring chrome_exe; if (install_status == installer::SAME_VERSION_REPAIR_FAILED) { - if (FindProduct(products, BrowserDistribution::CHROME_FRAME)) { + 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) { - if (installation.path().empty()) { + 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; } else { - chrome_exe = installation.path() + chrome_exe = installer_state->target_path() .Append(installer::kChromeExe).value(); chrome_exe = L"\"" + chrome_exe + L"\""; install_msg_base = 0; } } - const Product* chrome_install = - FindProduct(products, BrowserDistribution::CHROME_BROWSER); + // Only do Chrome-specific stuff (like launching the browser) if + // Chrome was specifically requested (rather than being upgraded as + // part of a multi-install). + const Product* chrome_install = prefs.install_chrome() ? + installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER) : + NULL; bool value = false; if (chrome_install) { @@ -404,8 +585,8 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, bool write_chrome_launch_string = (!value) && (install_status != installer::IN_USE_UPDATED); - InstallUtil::WriteInstallerResult(installer_state.system_install(), - installer_state.state_key(), install_status, install_msg_base, + InstallUtil::WriteInstallerResult(system_install, + installer_state->state_key(), install_status, install_msg_base, write_chrome_launch_string ? &chrome_exe : NULL); if (install_status == installer::FIRST_INSTALL_SUCCESS) { @@ -416,8 +597,8 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, prefs.GetBool( installer::master_preferences::kDoNotLaunchChrome, &do_not_launch_chrome); - if (!installation.system_level() && !do_not_launch_chrome) - chrome_install->LaunchChrome(); + 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)) { @@ -438,7 +619,7 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, for (size_t i = 0; i < products.size(); ++i) { const Product* product = products[i]; product->distribution()->LaunchUserExperiment(install_status, - *installer_version, *product, installation.system_level()); + *installer_version, *product, system_install); } } @@ -472,14 +653,13 @@ installer::InstallStatus InstallChrome(const InstallationState& original_state, for (size_t i = 0; i < products.size(); ++i) { const Product* product = products[i]; product->distribution()->UpdateInstallStatus( - installer_state.system_install(), incremental_install, - prefs.is_multi_install(), install_status); - } - if (prefs.is_multi_install()) { - installation.properties()->UpdateInstallStatus( - installer_state.system_install(), incremental_install, true, + system_install, incremental_install, prefs.is_multi_install(), install_status); } + if (installer_state->is_multi_install()) { + installer_state->multi_package_binaries_distribution()->UpdateInstallStatus( + system_install, incremental_install, true, install_status); + } return install_status; } @@ -539,11 +719,10 @@ installer::InstallStatus ShowEULADialog(const std::wstring& inner_frame) { // various tasks other than installation (renaming chrome.exe, showing eula // among others). This function returns true if any such command line option // has been found and processed (so setup.exe should exit at that point). -bool HandleNonInstallCmdLineOptions(const InstallerState& installer_state, +bool HandleNonInstallCmdLineOptions(const InstallationState& original_state, const CommandLine& cmd_line, - const ProductPackageMapping& installs, + const InstallerState& installer_state, int* exit_code) { - DCHECK(installs.products().size()); bool handled = true; // TODO(tommi): Split these checks up into functions and use a data driven // map of switch->function. @@ -588,12 +767,11 @@ bool HandleNonInstallCmdLineOptions(const InstallerState& installer_state, cmd_line.GetSwitchValueNative(installer::switches::kShowEula); *exit_code = ShowEULADialog(inner_frame); if (installer::EULA_REJECTED != *exit_code) - GoogleUpdateSettings::SetEULAConsent(*installs.packages()[0].get(), true); + GoogleUpdateSettings::SetEULAConsent(installer_state, true); } else if (cmd_line.HasSwitch( installer::switches::kRegisterChromeBrowser)) { const Product* chrome_install = - FindProduct(installs.products(), BrowserDistribution::CHROME_BROWSER); - DCHECK(chrome_install); + installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER); if (chrome_install) { // If --register-chrome-browser option is specified, register all // Chrome protocol/file associations as well as register it as a valid @@ -611,16 +789,13 @@ bool HandleNonInstallCmdLineOptions(const InstallerState& installer_state, *exit_code = ShellUtil::RegisterChromeBrowser( chrome_install->distribution(), chrome_exe, suffix, false); } else { - LOG(ERROR) << "Can't register browser - Chrome distribution not found"; + LOG(DFATAL) << "Can't register browser - Chrome distribution not found"; *exit_code = installer::UNKNOWN_STATUS; } } else if (cmd_line.HasSwitch(installer::switches::kRenameChromeExe)) { // If --rename-chrome-exe is specified, we want to rename the executables // and exit. - const Packages& packages = installs.packages(); - DCHECK_EQ(1U, packages.size()); - for (size_t i = 0; i < packages.size(); ++i) - *exit_code = RenameChromeExecutables(*packages[i].get()); + *exit_code = RenameChromeExecutables(installer_state); } else if (cmd_line.HasSwitch( installer::switches::kRemoveChromeRegistration)) { // This is almost reverse of --register-chrome-browser option above. @@ -635,7 +810,7 @@ bool HandleNonInstallCmdLineOptions(const InstallerState& installer_state, } installer::InstallStatus tmp = installer::UNKNOWN_STATUS; const Product* chrome_install = - FindProduct(installs.products(), BrowserDistribution::CHROME_BROWSER); + installer_state.FindProduct(BrowserDistribution::CHROME_BROWSER); DCHECK(chrome_install); if (chrome_install) { installer::DeleteChromeRegistrationKeys(chrome_install->distribution(), @@ -651,37 +826,41 @@ bool HandleNonInstallCmdLineOptions(const InstallerState& installer_state, if (flavor == -1) { *exit_code = installer::UNKNOWN_STATUS; } else { - const Products& products = installs.products(); + const Products& products = installer_state.products(); for (size_t i = 0; i < products.size(); ++i) { const Product* product = products[i]; BrowserDistribution* browser_dist = product->distribution(); - browser_dist->InactiveUserToastExperiment(flavor, *product); + browser_dist->InactiveUserToastExperiment(flavor, *product, + installer_state.target_path()); } } } else if (cmd_line.HasSwitch(installer::switches::kSystemLevelToast)) { - const Products& products = installs.products(); + const Products& products = installer_state.products(); for (size_t i = 0; i < products.size(); ++i) { const Product* product = products[i]; BrowserDistribution* browser_dist = product->distribution(); // We started as system-level and have been re-launched as user level // to continue with the toast experiment. scoped_ptr<Version> installed_version( - InstallUtil::GetChromeVersion(browser_dist, installs.system_level())); + InstallUtil::GetChromeVersion(browser_dist, + installer_state.system_install())); browser_dist->LaunchUserExperiment(installer::REENTRY_SYS_UPDATE, *installed_version, *product, true); } } else if (cmd_line.HasSwitch( installer::switches::kChromeFrameReadyModeOptIn)) { *exit_code = InstallUtil::GetInstallReturnCode( - installer::ChromeFrameReadyModeOptIn(installer_state, cmd_line)); + installer::ChromeFrameReadyModeOptIn(original_state, installer_state)); } else if (cmd_line.HasSwitch( installer::switches::kChromeFrameReadyModeTempOptOut)) { *exit_code = InstallUtil::GetInstallReturnCode( - installer::ChromeFrameReadyModeTempOptOut(cmd_line)); + installer::ChromeFrameReadyModeTempOptOut(original_state, + installer_state)); } else if (cmd_line.HasSwitch( installer::switches::kChromeFrameReadyModeEndTempOptOut)) { *exit_code = InstallUtil::GetInstallReturnCode( - installer::ChromeFrameReadyModeEndTempOptOut(cmd_line)); + installer::ChromeFrameReadyModeEndTempOptOut(original_state, + installer_state)); } else { handled = false; } @@ -743,54 +922,9 @@ class AutoCom { bool initialized_; }; -bool PopulateInstallations(bool for_uninstall, - const MasterPreferences& prefs, - ProductPackageMapping* installations) { - DCHECK(installations); - bool success = true; - - bool implicit_chrome_install = false; - bool implicit_gcf_install = false; - - // See what products are already installed in multi mode. - // When we do multi installs, we must upgrade all installations in sync since - // they share the binaries. Be careful to not do this when we're uninstalling - // a product. - if (prefs.is_multi_install() && !for_uninstall) { - struct CheckInstall { - bool* installed; - BrowserDistribution::Type type; - } product_checks[] = { - {&implicit_chrome_install, BrowserDistribution::CHROME_BROWSER}, - {&implicit_gcf_install, BrowserDistribution::CHROME_FRAME}, - }; - for (size_t i = 0; i < arraysize(product_checks); ++i) { - BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution( - product_checks[i].type, prefs); - *product_checks[i].installed = installer::IsInstalledAsMulti( - installations->system_level(), dist); - LOG_IF(INFO, *product_checks[i].installed) - << "Product already installed and must be included: " - << dist->GetApplicationName(); - } - } - if (prefs.install_chrome() || implicit_chrome_install) { - VLOG(1) << (for_uninstall ? "Uninstall" : "Install") - << " distribution: Chrome"; - success = installations->AddDistribution( - BrowserDistribution::CHROME_BROWSER, prefs); - } - if (success && (prefs.install_chrome_frame() || implicit_gcf_install)) { - VLOG(1) << (for_uninstall ? "Uninstall" : "Install") - << " distribution: Chrome Frame"; - success = installations->AddDistribution( - BrowserDistribution::CHROME_FRAME, prefs); - } - return success; -} // Returns the Custom information for the client identified by the exe path // passed in. This information is used for crash reporting. @@ -883,28 +1017,12 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, InitializeCrashReporting(system_install)); InstallationState original_state; - original_state.Initialize(prefs); + original_state.Initialize(); InstallerState installer_state; - installer_state.Initialize(prefs, original_state); + installer_state.Initialize(cmd_line, prefs, original_state); const bool is_uninstall = cmd_line.HasSwitch(installer::switches::kUninstall); - ProductPackageMapping installations(prefs.is_multi_install(), system_install); - if (!PopulateInstallations(is_uninstall, 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 = installer::NON_MULTI_INSTALLATION_EXISTS; - int string_id = IDS_INSTALL_NON_MULTI_INSTALLATION_EXISTS_BASE; - if (!prefs.is_multi_install()) { - status = installer::MULTI_INSTALLATION_EXISTS; - string_id = IDS_INSTALL_MULTI_INSTALLATION_EXISTS_BASE; - } - LOG(ERROR) << "Failed to populate installations: " << status; - InstallUtil::WriteInstallerResult(system_install, - installer_state.state_key(), status, string_id, NULL); - return status; - } - // Check to make sure current system is WinXP or later. If not, log // error message and get out. if (!InstallUtil::IsOSSupported()) { @@ -940,8 +1058,8 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, } int exit_code = 0; - if (HandleNonInstallCmdLineOptions(installer_state, cmd_line, installations, - &exit_code)) + if (HandleNonInstallCmdLineOptions(original_state, cmd_line, installer_state, + &exit_code)) return exit_code; if (system_install && !IsUserAnAdmin()) { @@ -965,24 +1083,22 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, } installer::InstallStatus install_status = installer::UNKNOWN_STATUS; - // If --uninstall option is given, uninstall chrome + // If --uninstall option is given, uninstall the identified product(s) if (is_uninstall) { - for (size_t i = 0; i < installations.products().size(); ++i) { + const Products& products = installer_state.products(); + for (size_t i = 0; i < products.size(); ++i) { install_status = UninstallProduct(original_state, installer_state, - cmd_line, *installations.products()[i]); + cmd_line, *products[i]); } } else { // If --uninstall option is not specified, we assume it is install case. - const Packages& packages = installations.packages(); - VLOG(1) << "Installing to " << packages.size() << " target paths"; - for (size_t i = 0; i < packages.size(); ++i) { - install_status = InstallChrome(original_state, installer_state, cmd_line, - *packages[i].get(), prefs); - } + VLOG(1) << "Installing to " << installer_state.target_path().value(); + install_status = InstallProducts(original_state, cmd_line, prefs, + &installer_state); } const Product* cf_install = - FindProduct(installations.products(), BrowserDistribution::CHROME_FRAME); + installer_state.FindProduct(BrowserDistribution::CHROME_FRAME); if (cf_install && !cmd_line.HasSwitch(installer::switches::kForceUninstall)) { @@ -991,7 +1107,7 @@ int WINAPI wWinMain(HINSTANCE instance, HINSTANCE prev_instance, } else if (is_uninstall) { // Only show the message box if Chrome Frame was the only product being // uninstalled. - if (installations.products().size() == 1U) { + if (installer_state.products().size() == 1U) { ::MessageBoxW(NULL, installer::GetLocalizedString( IDS_UNINSTALL_COMPLETE_BASE).c_str(), @@ -1005,17 +1121,11 @@ 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. - // TODO(tommi): Fix this loop when IsMsi has been moved out of the Product - // class. - for (size_t i = 0; i < installations.products().size(); ++i) { - const Product* product = installations.products()[i]; - if (!(product->IsMsi() && 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); - } - } + 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); VLOG(1) << "Installation complete, returning: " << return_code; diff --git a/chrome/installer/setup/setup_unittests.rc b/chrome/installer/setup/setup_unittests.rc new file mode 100644 index 0000000..201a12b --- /dev/null +++ b/chrome/installer/setup/setup_unittests.rc @@ -0,0 +1,65 @@ +// Microsoft Visual C++ generated resource script.
+//
+#include "setup_unittests_resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+LANGUAGE 9, 1
+#pragma code_page(1252)
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE
+BEGIN
+ "setup_unittests_resource.h\0"
+END
+
+2 TEXTINCLUDE
+BEGIN
+ "#include ""afxres.h""\r\n"
+ "\0"
+END
+
+3 TEXTINCLUDE
+BEGIN
+ "\r\n"
+ "\0"
+END
+
+#endif // APSTUDIO_INVOKED
+
+#endif // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+#include "installer_util_strings.rc"
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif // not APSTUDIO_INVOKED
+
diff --git a/chrome/installer/setup/setup_unittests_resource.h b/chrome/installer/setup/setup_unittests_resource.h new file mode 100644 index 0000000..6b3a0ee --- /dev/null +++ b/chrome/installer/setup/setup_unittests_resource.h @@ -0,0 +1,18 @@ +// Copyright (c) 2011 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. + +//{{NO_DEPENDENCIES}} +// Microsoft Visual C++ generated include file. +// Used by setup_unittests.rc + +// Next default values for new objects +// +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +#define _APS_NEXT_RESOURCE_VALUE 101 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif diff --git a/chrome/installer/setup/setup_util.cc b/chrome/installer/setup/setup_util.cc index d982792..7c6ec1c 100644 --- a/chrome/installer/setup/setup_util.cc +++ b/chrome/installer/setup/setup_util.cc @@ -9,7 +9,6 @@ #include "base/file_util.h" #include "base/logging.h" #include "base/string_util.h" -#include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/util_constants.h" #include "courgette/courgette.h" #include "third_party/bspatch/mbspatch.h" diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc index e21cc01..038525b 100644 --- a/chrome/installer/setup/uninstall.cc +++ b/chrome/installer/setup/uninstall.cc @@ -29,7 +29,6 @@ #include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/installer_state.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" @@ -38,6 +37,7 @@ using base::win::RegKey; using installer::InstallStatus; +using installer::MasterPreferences; namespace { @@ -47,10 +47,12 @@ namespace { // installed products and for the multi-installer package. void ProcessGoogleUpdateItems( const installer::InstallationState& original_state, + const installer::InstallerState& installer_state, const installer::Product& product) { - const bool system_level = product.system_level(); + DCHECK(installer_state.is_multi_install()); + const bool system_level = installer_state.system_install(); BrowserDistribution* distribution = product.distribution(); - const HKEY reg_root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + const HKEY reg_root = installer_state.root_key(); const installer::ProductState* product_state = original_state.GetProductState(system_level, distribution->GetType()); DCHECK(product_state != NULL); @@ -58,39 +60,40 @@ void ProcessGoogleUpdateItems( // Remove product's flags from the channel value. channel_info.set_value(product_state->channel().value()); - const bool modified = distribution->SetChannelFlags(false, &channel_info); + const bool modified = product.SetChannelFlags(false, &channel_info); // Apply the new channel value to all other products and to the multi package. if (modified) { - BrowserDistribution::Type other_dist_type = + BrowserDistribution::Type other_dist_types[] = { (distribution->GetType() == BrowserDistribution::CHROME_BROWSER) ? - BrowserDistribution::CHROME_FRAME : - BrowserDistribution::CHROME_BROWSER; - BrowserDistribution* other_dist = - BrowserDistribution::GetSpecificDistribution(other_dist_type, - installer::MasterPreferences::ForCurrentProcess()); + BrowserDistribution::CHROME_FRAME : + BrowserDistribution::CHROME_BROWSER, + BrowserDistribution::CHROME_BINARIES + }; scoped_ptr<WorkItemList> update_list(WorkItem::CreateNoRollbackWorkItemList()); - product_state = original_state.GetProductState(system_level, - other_dist_type); - if (installer::IsInstalledAsMulti(system_level, other_dist) && - !product_state->channel().Equals(channel_info)) { - update_list->AddSetRegValueWorkItem(reg_root, other_dist->GetStateKey(), - google_update::kRegApField, channel_info.value(), true); - } else { - VLOG(1) << other_dist->GetApplicationName() - << "'s ap value is unexpectedly up to date."; - } - - product_state = original_state.GetMultiPackageState(system_level); - DCHECK(product_state != NULL); - if (!product_state->channel().Equals(channel_info)) { - update_list->AddSetRegValueWorkItem(reg_root, - product.package().properties()->GetStateKey(), - google_update::kRegApField, channel_info.value(), true); - } else { - VLOG(1) << "Multi-install package's ap value is unexpectedly up to date."; + for (int i = 0; i < arraysize(other_dist_types); ++i) { + BrowserDistribution::Type other_dist_type = other_dist_types[i]; + product_state = + original_state.GetProductState(system_level, other_dist_type); + // Only modify other products if they're installed and multi. + if (product_state != NULL && + product_state->is_multi_install() && + !product_state->channel().Equals(channel_info)) { + BrowserDistribution* other_dist = + BrowserDistribution::GetSpecificDistribution(other_dist_type); + update_list->AddSetRegValueWorkItem(reg_root, other_dist->GetStateKey(), + google_update::kRegApField, channel_info.value(), true); + } else { + LOG_IF(DFATAL, + product_state != NULL && product_state->is_multi_install()) + << "Channel value for " + << BrowserDistribution::GetSpecificDistribution( + other_dist_type)->GetAppShortCutName() + << " is somehow already set to the desired new value of " + << channel_info.value(); + } } bool success = update_list->Do(); @@ -175,14 +178,16 @@ void CloseChromeFrameHelperProcess() { // It returns true iff: // - Software\Clients\StartMenuInternet\Chromium\"" key has a valid value. // - The value is same as chrome.exe path for the current installation. -bool CurrentUserHasDefaultBrowser(const Product& product) { +bool CurrentUserHasDefaultBrowser(const InstallerState& installer_state, + const Product& product) { std::wstring reg_key(ShellUtil::kRegStartMenuInternet); - reg_key.append(L"\\" + product.distribution()->GetApplicationName() + - ShellUtil::kRegShellOpen); + reg_key.append(1, L'\\') + .append(product.distribution()->GetApplicationName()) + .append(ShellUtil::kRegShellOpen); RegKey key(HKEY_LOCAL_MACHINE, reg_key.c_str(), KEY_READ); std::wstring reg_exe; if (key.ReadValue(L"", ®_exe) == ERROR_SUCCESS && reg_exe.length() > 2) { - FilePath chrome_exe(product.package().path() + FilePath chrome_exe(installer_state.target_path() .Append(installer::kChromeExe)); // The path in the registry will always have quotes. reg_exe = reg_exe.substr(1, reg_exe.length() - 2); @@ -199,14 +204,15 @@ bool CurrentUserHasDefaultBrowser(const Product& product) { // We try to remove the standard desktop shortcut but if that fails we try // 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) { +void DeleteChromeShortcuts(const InstallerState& installer_state, + const Product& product) { if (!product.is_chrome()) { VLOG(1) << __FUNCTION__ " called for non-CHROME distribution"; return; } FilePath shortcut_path; - if (product.system_level()) { + if (installer_state.system_install()) { PathService::Get(base::DIR_COMMON_START_MENU, &shortcut_path); if (!ShellUtil::RemoveChromeDesktopShortcut(product.distribution(), ShellUtil::CURRENT_USER | ShellUtil::SYSTEM_LEVEL, false)) { @@ -332,11 +338,11 @@ DeleteResult DeleteLocalState(const Product& product) { return result; } -bool MoveSetupOutOfInstallFolder(const Package& package, +bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, const FilePath& setup_path, const Version& installed_version) { bool ret = false; - FilePath setup_exe(package.GetInstallerDirectory(installed_version) + FilePath setup_exe(installer_state.GetInstallerDirectory(installed_version) .Append(setup_path.BaseName())); FilePath temp_file; if (!file_util::CreateTemporaryFile(&temp_file)) { @@ -349,33 +355,33 @@ bool MoveSetupOutOfInstallFolder(const Package& package, return ret; } -DeleteResult DeleteFilesAndFolders(const Package& package, +DeleteResult DeleteFilesAndFolders(const InstallerState& installer_state, const Version& installed_version) { - VLOG(1) << "DeleteFilesAndFolders: " << package.path().value(); - if (package.path().empty()) { + VLOG(1) << "DeleteFilesAndFolders: " << installer_state.target_path().value(); + if (installer_state.target_path().empty()) { LOG(ERROR) << "Could not get installation destination path."; return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. } DeleteResult result = DELETE_SUCCEEDED; - VLOG(1) << "Deleting install path " << package.path().value(); - if (!file_util::Delete(package.path(), true)) { + VLOG(1) << "Deleting install path " << installer_state.target_path().value(); + if (!file_util::Delete(installer_state.target_path(), true)) { LOG(ERROR) << "Failed to delete folder (1st try): " - << package.path().value(); - if (FindProduct(package.products(), - BrowserDistribution::CHROME_FRAME)) { + << installer_state.target_path().value(); + if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { // We don't try killing Chrome processes for Chrome Frame builds since // that is unlikely to help. Instead, schedule files for deletion and // return a value that will trigger a reboot prompt. - ScheduleDirectoryForDeletion(package.path().value().c_str()); + ScheduleDirectoryForDeletion( + installer_state.target_path().value().c_str()); result = DELETE_REQUIRES_REBOOT; } else { // Try closing any running chrome processes and deleting files once again. CloseAllChromeProcesses(); - if (!file_util::Delete(package.path(), true)) { + if (!file_util::Delete(installer_state.target_path(), true)) { LOG(ERROR) << "Failed to delete folder (2nd try): " - << package.path().value(); + << installer_state.target_path().value(); result = DELETE_FAILED; } } @@ -385,11 +391,11 @@ DeleteResult DeleteFilesAndFolders(const Package& package, // If we need a reboot to continue, schedule the parent directories for // deletion unconditionally. If they are not empty, the session manager // will not delete them on reboot. - ScheduleParentAndGrandparentForDeletion(package.path()); + ScheduleParentAndGrandparentForDeletion(installer_state.target_path()); } else { // Now check and delete if the parent directories are empty // For example Google\Chrome or Chromium - DeleteEmptyParentDir(package.path()); + DeleteEmptyParentDir(installer_state.target_path()); } return result; } @@ -397,7 +403,9 @@ DeleteResult DeleteFilesAndFolders(const Package& package, // This method checks if Chrome is currently running or if the user has // cancelled the uninstall operation by clicking Cancel on the confirmation // box that Chrome pops up. -InstallStatus IsChromeActiveOrUserCancelled(const Product& product) { +InstallStatus IsChromeActiveOrUserCancelled( + const InstallerState& installer_state, + const Product& product) { int32 exit_code = ResultCodes::NORMAL_EXIT; CommandLine options(CommandLine::NO_PROGRAM); options.AppendSwitch(installer::switches::kUninstall); @@ -411,7 +419,8 @@ InstallStatus IsChromeActiveOrUserCancelled(const Product& product) { // give this method some brains and not kill chrome.exe launched // by us, we will not uninstall if we get this return code). VLOG(1) << "Launching Chrome to do uninstall tasks."; - if (product.LaunchChromeAndWait(options, &exit_code)) { + if (product.LaunchChromeAndWait(installer_state.target_path(), options, + &exit_code)) { VLOG(1) << "chrome.exe launched for uninstall confirmation returned: " << exit_code; if ((exit_code == ResultCodes::UNINSTALL_CHROME_ALIVE) || @@ -428,7 +437,8 @@ InstallStatus IsChromeActiveOrUserCancelled(const Product& product) { return installer::UNINSTALL_CONFIRMED; } -bool ShouldDeleteProfile(const CommandLine& cmd_line, InstallStatus status, +bool ShouldDeleteProfile(const InstallerState& installer_state, + const CommandLine& cmd_line, InstallStatus status, const Product& product) { bool should_delete = false; @@ -436,7 +446,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.is_chrome() && !product.IsMsi()) { + if (!product.is_chrome() && !installer_state.is_msi()) { should_delete = true; } else { should_delete = @@ -542,16 +552,16 @@ const wchar_t kChromeExtProgId[] = L"ChromiumExt"; } } -bool ProcessChromeFrameWorkItems(const FilePath& setup_path, - const Product& product, - const ProductState* product_state) { +bool ProcessChromeFrameWorkItems(const InstallationState& original_state, + const InstallerState& installer_state, + const FilePath& setup_path, + const Product& product) { if (!product.is_chrome_frame()) return false; - DCHECK(product_state != NULL); scoped_ptr<WorkItemList> item_list(WorkItem::CreateWorkItemList()); - AddChromeFrameWorkItems(false, setup_path, product_state->version(), product, - item_list.get()); + AddChromeFrameWorkItems(original_state, installer_state, setup_path, + Version(), product, item_list.get()); return item_list->Do(); } @@ -581,7 +591,7 @@ InstallStatus UninstallProduct(const InstallationState& original_state, CloseAllChromeProcesses(); } else if (is_chrome) { // no --force-uninstall so lets show some UI dialog boxes. - status = IsChromeActiveOrUserCancelled(product); + status = IsChromeActiveOrUserCancelled(installer_state, product); if (status != installer::UNINSTALL_CONFIRMED && status != installer::UNINSTALL_DELETE_PROFILE) return status; @@ -590,7 +600,8 @@ InstallStatus UninstallProduct(const InstallationState& original_state, // another uninstaller (silent) in elevated mode to do HKLM cleanup. // And continue uninstalling in the current process also to do HKCU cleanup. if (remove_all && - (!suffix.empty() || CurrentUserHasDefaultBrowser(product)) && + (!suffix.empty() || + CurrentUserHasDefaultBrowser(installer_state, product)) && !::IsUserAnAdmin() && base::win::GetVersion() >= base::win::VERSION_VISTA && !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) { @@ -615,11 +626,10 @@ InstallStatus UninstallProduct(const InstallationState& original_state, // in case of errors. // First delete shortcuts from Start->Programs, Desktop & Quick Launch. - DeleteChromeShortcuts(product); + DeleteChromeShortcuts(installer_state, product); // Delete the registry keys (Uninstall key and Version key). - HKEY reg_root = product.system_level() ? HKEY_LOCAL_MACHINE : - HKEY_CURRENT_USER; + HKEY reg_root = installer_state.root_key(); RegKey key(reg_root, L"", KEY_ALL_ACCESS); // Note that we must retrieve the distribution-specific data before deleting @@ -634,31 +644,34 @@ InstallStatus UninstallProduct(const InstallationState& original_state, // there). This is due to a Google Update behaviour where an uninstall and a // rapid reinstall might result in stale values from the old ClientState key // being picked up on reinstall. - product.SetMsiMarker(false); + product.SetMsiMarker(installer_state.system_install(), false); // Remove all Chrome registration keys. InstallStatus ret = installer::UNKNOWN_STATUS; DeleteChromeRegistrationKeys(product.distribution(), reg_root, suffix, ret); - // Get the state of the installed product (if any) - const ProductState* product_state = - original_state.GetProductState(installer_state.system_install(), - browser_dist->GetType()); - - if (!is_chrome) - ProcessChromeFrameWorkItems(setup_path, product, product_state); + if (!is_chrome) { + ProcessChromeFrameWorkItems(original_state, installer_state, setup_path, + product); + } - if (product.package().multi_install()) - ProcessGoogleUpdateItems(original_state, product); + if (installer_state.is_multi_install()) + ProcessGoogleUpdateItems(original_state, installer_state, 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() && - (!suffix.empty() || CurrentUserHasDefaultBrowser(product))) { + if (remove_all && !installer_state.system_install() && + (!suffix.empty() || CurrentUserHasDefaultBrowser(installer_state, + product))) { DeleteChromeRegistrationKeys(product.distribution(), HKEY_LOCAL_MACHINE, suffix, ret); } + // Get the state of the installed product (if any) + const ProductState* product_state = + original_state.GetProductState(installer_state.system_install(), + browser_dist->GetType()); + // Delete shared registry keys as well (these require admin rights) if // remove_all option is specified. if (remove_all) { @@ -675,8 +688,9 @@ InstallStatus UninstallProduct(const InstallationState& original_state, // Unregister any dll servers that we may have registered for this // product. if (product_state != NULL) { - std::vector<FilePath> com_dll_list(browser_dist->GetComDllList()); - FilePath dll_folder = product.package().path().Append( + std::vector<FilePath> com_dll_list; + product.AddComDllList(&com_dll_list); + FilePath dll_folder = installer_state.target_path().Append( UTF8ToWide(product_state->version().GetString())); scoped_ptr<WorkItemList> unreg_work_item_list( @@ -685,7 +699,7 @@ InstallStatus UninstallProduct(const InstallationState& original_state, AddRegisterComDllWorkItems(dll_folder, com_dll_list, - product.system_level(), + installer_state.system_install(), false, // Unregister true, // May fail unreg_work_item_list.get()); @@ -704,25 +718,35 @@ InstallStatus UninstallProduct(const InstallationState& original_state, // Finally delete all the files from Chrome folder after moving setup.exe // and the user's Local State to a temp location. - bool delete_profile = ShouldDeleteProfile(cmd_line, status, product); + bool delete_profile = ShouldDeleteProfile(installer_state, cmd_line, status, + product); ret = installer::UNINSTALL_SUCCESSFUL; // 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 (product.package().multi_install()) { - can_delete_files = - (product.package().GetMultiInstallDependencyCount() == 0); + bool can_delete_files = true; + if (installer_state.is_multi_install()) { + BrowserDistribution::Type types[] = { + BrowserDistribution::CHROME_BROWSER, + BrowserDistribution::CHROME_FRAME + }; + ProductState prod_state; + for (int i = 0; i < arraysize(types); ++i) { + if (prod_state.Initialize(installer_state.system_install(), types[i]) && + prod_state.is_multi_install()) { + can_delete_files = false; + break; + } + } 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()); + if (can_delete_files) { + BrowserDistribution* multi_dist = + installer_state.multi_package_binaries_distribution(); + InstallUtil::DeleteRegistryKey(key, multi_dist->GetVersionKey()); } - } else { - can_delete_files = true; } FilePath backup_state_file(BackupLocalStateFile( @@ -733,9 +757,9 @@ InstallStatus UninstallProduct(const InstallationState& original_state, // 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, + MoveSetupOutOfInstallFolder(installer_state, setup_path, product_state->version()); - delete_result = DeleteFilesAndFolders(product.package(), + delete_result = DeleteFilesAndFolders(installer_state, product_state->version()); } @@ -763,4 +787,3 @@ InstallStatus UninstallProduct(const InstallationState& original_state, } } // namespace installer - diff --git a/chrome/installer/util/browser_distribution.cc b/chrome/installer/util/browser_distribution.cc index 4387b51..be452c5 100644 --- a/chrome/installer/util/browser_distribution.cc +++ b/chrome/installer/util/browser_distribution.cc @@ -17,7 +17,9 @@ #include "base/win/registry.h" #include "chrome/common/env_vars.h" #include "chrome/installer/util/chrome_frame_distribution.h" +#include "chrome/installer/util/chromium_binaries_distribution.h" #include "chrome/installer/util/google_chrome_distribution.h" +#include "chrome/installer/util/google_chrome_binaries_distribution.h" #include "chrome/installer/util/google_chrome_sxs_distribution.h" #include "chrome/installer/util/install_util.h" #include "chrome/installer/util/l10n_string_util.h" @@ -31,6 +33,7 @@ namespace { // The BrowserDistribution objects are never freed. BrowserDistribution* g_browser_distribution = NULL; BrowserDistribution* g_chrome_frame_distribution = NULL; +BrowserDistribution* g_binaries_distribution = NULL; // Returns true if currently running in npchrome_frame.dll bool IsChromeFrameModule() { @@ -59,17 +62,19 @@ BrowserDistribution::Type GetCurrentDistributionType() { } // end namespace -BrowserDistribution::BrowserDistribution( - const installer::MasterPreferences& prefs) - : type_(BrowserDistribution::CHROME_BROWSER) { +BrowserDistribution::BrowserDistribution() + : type_(CHROME_BROWSER) { +} + +BrowserDistribution::BrowserDistribution(Type type) + : type_(type) { } template<class DistributionClass> BrowserDistribution* BrowserDistribution::GetOrCreateBrowserDistribution( - const installer::MasterPreferences& prefs, BrowserDistribution** dist) { if (!*dist) { - DistributionClass* temp = new DistributionClass(prefs); + DistributionClass* temp = new DistributionClass(); if (base::subtle::NoBarrier_CompareAndSwap( reinterpret_cast<base::subtle::AtomicWord*>(dist), NULL, reinterpret_cast<base::subtle::AtomicWord>(temp)) != NULL) @@ -80,33 +85,43 @@ BrowserDistribution* BrowserDistribution::GetOrCreateBrowserDistribution( } BrowserDistribution* BrowserDistribution::GetDistribution() { - const installer::MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); - return GetSpecificDistribution(GetCurrentDistributionType(), prefs); + return GetSpecificDistribution(GetCurrentDistributionType()); } // static BrowserDistribution* BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::Type type, - const installer::MasterPreferences& prefs) { + BrowserDistribution::Type type) { BrowserDistribution* dist = NULL; - if (type == CHROME_FRAME) { - dist = GetOrCreateBrowserDistribution<ChromeFrameDistribution>( - prefs, &g_chrome_frame_distribution); - } else { - DCHECK_EQ(CHROME_BROWSER, type); + switch (type) { + case CHROME_BROWSER: +#if defined(GOOGLE_CHROME_BUILD) + if (InstallUtil::IsChromeSxSProcess()) { + dist = GetOrCreateBrowserDistribution<GoogleChromeSxSDistribution>( + &g_browser_distribution); + } else { + dist = GetOrCreateBrowserDistribution<GoogleChromeDistribution>( + &g_browser_distribution); + } +#else + dist = GetOrCreateBrowserDistribution<BrowserDistribution>( + &g_browser_distribution); +#endif + break; + + case CHROME_FRAME: + dist = GetOrCreateBrowserDistribution<ChromeFrameDistribution>( + &g_chrome_frame_distribution); + break; + + default: + DCHECK_EQ(CHROME_BINARIES, type); #if defined(GOOGLE_CHROME_BUILD) - if (InstallUtil::IsChromeSxSProcess()) { - dist = GetOrCreateBrowserDistribution<GoogleChromeSxSDistribution>( - prefs, &g_browser_distribution); - } else { - dist = GetOrCreateBrowserDistribution<GoogleChromeDistribution>( - prefs, &g_browser_distribution); - } + dist = GetOrCreateBrowserDistribution<GoogleChromeBinariesDistribution>( + &g_binaries_distribution); #else - dist = GetOrCreateBrowserDistribution<BrowserDistribution>( - prefs, &g_browser_distribution); + dist = GetOrCreateBrowserDistribution<ChromiumBinariesDistribution>( + &g_binaries_distribution); #endif } @@ -212,31 +227,6 @@ void BrowserDistribution::LaunchUserExperiment( void BrowserDistribution::InactiveUserToastExperiment(int flavor, - const installer::Product& installation) { -} - -std::vector<FilePath> BrowserDistribution::GetKeyFiles() { - std::vector<FilePath> key_files; - key_files.push_back(FilePath(installer::kChromeDll)); - return key_files; -} - -std::vector<FilePath> BrowserDistribution::GetComDllList() { - return std::vector<FilePath>(); -} - -void BrowserDistribution::AppendUninstallCommandLineFlags( - CommandLine* cmd_line) { - DCHECK(cmd_line); - cmd_line->AppendSwitch(installer::switches::kChrome); -} - -bool BrowserDistribution::ShouldCreateUninstallEntry() { - return true; -} - -bool BrowserDistribution::SetChannelFlags( - bool set, - installer::ChannelInfo* channel_info) { - return false; + const installer::Product& installation, + const FilePath& application_path) { } diff --git a/chrome/installer/util/browser_distribution.h b/chrome/installer/util/browser_distribution.h index 1f6a1c9..3fb1ded 100644 --- a/chrome/installer/util/browser_distribution.h +++ b/chrome/installer/util/browser_distribution.h @@ -35,12 +35,12 @@ class BrowserDistribution { enum Type { CHROME_BROWSER, CHROME_FRAME, + CHROME_BINARIES, }; static BrowserDistribution* GetDistribution(); - static BrowserDistribution* GetSpecificDistribution( - Type type, const installer::MasterPreferences& prefs); + static BrowserDistribution* GetSpecificDistribution(Type type); Type GetType() const { return type_; } @@ -104,45 +104,21 @@ class BrowserDistribution { // The user has qualified for the inactive user toast experiment and this // function just performs it. virtual void InactiveUserToastExperiment(int flavor, - const installer::Product& installation); - - // A key-file is a file such as a DLL on Windows that is expected to be - // in use when the product is being used. For example "chrome.dll" for - // Chrome. Before attempting to delete an installation directory during - // an uninstallation, the uninstaller will check if any one of a potential - // set of key files is in use and if they are, abort the delete operation. - // Only if none of the key files are in use, can the folder be deleted. - // Note that this function does not return a full path to the key file(s), - // only (a) file name(s). - virtual std::vector<FilePath> GetKeyFiles(); - - // Returns the list of Com Dlls that this product cares about having - // registered and unregistered. The list may be empty. - virtual std::vector<FilePath> GetComDllList(); - - // Given a command line, appends the set of uninstall flags the uninstaller - // 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(); - - // Adds or removes product-specific flags in |channel_info|. Returns true if - // |channel_info| is modified. - virtual bool SetChannelFlags(bool set, installer::ChannelInfo* channel_info); + const installer::Product& installation, + const FilePath& application_path); protected: - explicit BrowserDistribution(const installer::MasterPreferences& prefs); + explicit BrowserDistribution(Type type); template<class DistributionClass> static BrowserDistribution* GetOrCreateBrowserDistribution( - const installer::MasterPreferences& prefs, BrowserDistribution** dist); - Type type_; + const Type type_; private: + BrowserDistribution(); + DISALLOW_COPY_AND_ASSIGN(BrowserDistribution); }; diff --git a/chrome/installer/util/browser_distribution_unittest.cc b/chrome/installer/util/browser_distribution_unittest.cc index f35ad3f..61c9652 100644 --- a/chrome/installer/util/browser_distribution_unittest.cc +++ b/chrome/installer/util/browser_distribution_unittest.cc @@ -5,7 +5,6 @@ // Unit tests for BrowserDistribution class. #include "chrome/installer/util/browser_distribution.h" -#include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/shell_util.h" #include "testing/gtest/include/gtest/gtest.h" @@ -25,21 +24,14 @@ class BrowserDistributionTest : public testing::Test { // The distribution strings should not be empty. The unit tests are not linking // with the chrome resources so we cannot test official build. TEST(BrowserDistributionTest, StringsTest) { - struct browser_test_type { - BrowserDistribution::Type type; - bool has_com_dlls; - } browser_tests[] = { - { BrowserDistribution::CHROME_BROWSER, false }, - { BrowserDistribution::CHROME_FRAME, true }, + BrowserDistribution::Type browser_tests[] = { + BrowserDistribution::CHROME_BROWSER, + BrowserDistribution::CHROME_FRAME, }; - const installer::MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); - for (int i = 0; i < arraysize(browser_tests); ++i) { BrowserDistribution* dist = - BrowserDistribution::GetSpecificDistribution(browser_tests[i].type, - prefs); + BrowserDistribution::GetSpecificDistribution(browser_tests[i]); ASSERT_TRUE(dist != NULL); std::wstring name = dist->GetApplicationName(); EXPECT_FALSE(name.empty()); @@ -47,12 +39,6 @@ TEST(BrowserDistributionTest, StringsTest) { EXPECT_FALSE(desc.empty()); std::wstring alt_name = dist->GetAlternateApplicationName(); EXPECT_FALSE(alt_name.empty()); - std::vector<FilePath> com_dlls(dist->GetComDllList()); - if (browser_tests[i].has_com_dlls) { - EXPECT_FALSE(com_dlls.empty()); - } else { - EXPECT_TRUE(com_dlls.empty()); - } } } diff --git a/chrome/installer/util/channel_info.cc b/chrome/installer/util/channel_info.cc index 4abcb70..98f54d8 100644 --- a/chrome/installer/util/channel_info.cc +++ b/chrome/installer/util/channel_info.cc @@ -153,6 +153,18 @@ bool ChannelInfo::GetChannelName(std::wstring* channel_name) const { return false; } +bool ChannelInfo::EqualsBaseOf(const ChannelInfo& other) const { + std::wstring::size_type this_base_end; + std::wstring::size_type other_base_end; + + if (!FindModifier(MOD_MULTI_INSTALL, value_, &this_base_end)) + this_base_end = FindInsertionPoint(MOD_MULTI_INSTALL, value_); + if (!FindModifier(MOD_MULTI_INSTALL, other.value_, &other_base_end)) + other_base_end = FindInsertionPoint(MOD_MULTI_INSTALL, other.value_); + return value_.compare(0, this_base_end, + other.value_.c_str(), other_base_end) == 0; +} + bool ChannelInfo::IsCeee() const { return HasModifier(MOD_CEEE, value_); } diff --git a/chrome/installer/util/channel_info.h b/chrome/installer/util/channel_info.h index 7954e1c..a5b895d 100644 --- a/chrome/installer/util/channel_info.h +++ b/chrome/installer/util/channel_info.h @@ -41,6 +41,10 @@ class ChannelInfo { // determined. bool GetChannelName(std::wstring* channel_name) const; + // Returns true if this object and |other| have a common base (that which + // remains when all modifiers and suffixes are omitted). + bool EqualsBaseOf(const ChannelInfo& other) const; + // Returns true if the -CEEE modifier is present in the value. bool IsCeee() const; diff --git a/chrome/installer/util/channel_info_unittest.cc b/chrome/installer/util/channel_info_unittest.cc index febc33a..5c03040 100644 --- a/chrome/installer/util/channel_info_unittest.cc +++ b/chrome/installer/util/channel_info_unittest.cc @@ -2,6 +2,9 @@ // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. +#include <utility> + +#include "base/basictypes.h" #include "chrome/installer/util/channel_info.h" #include "testing/gtest/include/gtest/gtest.h" @@ -158,3 +161,36 @@ TEST(ChannelInfoTest, Combinations) { ci.set_value(L"2.0-beta-chromeframe-chrome"); EXPECT_TRUE(ci.IsChrome()); } + +TEST(ChannelInfoTest, EqualsBaseOf) { + installer::ChannelInfo ci1; + installer::ChannelInfo ci2; + + std::pair<std::wstring, std::wstring> trues[] = { + std::make_pair(std::wstring(L""), std::wstring(L"")), + std::make_pair(std::wstring(L"2.0-beta"), std::wstring(L"2.0-beta")), + std::make_pair(std::wstring(L"-full"), std::wstring(L"-full")), + std::make_pair(std::wstring(L""), std::wstring(L"-multi")) + }; + for (int i = 0; i < arraysize(trues); ++i) { + std::pair<std::wstring, std::wstring>& the_pair = trues[i]; + ci1.set_value(the_pair.first); + ci2.set_value(the_pair.second); + EXPECT_TRUE(ci1.EqualsBaseOf(ci2)) << the_pair.first << " " + << the_pair.second; + } + + std::pair<std::wstring, std::wstring> falses[] = { + std::make_pair(std::wstring(L""), std::wstring(L"2.0-beta")), + std::make_pair(std::wstring(L"2.0-gamma"), std::wstring(L"2.0-beta")), + std::make_pair(std::wstring(L"spam-full"), std::wstring(L"-full")), + std::make_pair(std::wstring(L"multi"), std::wstring(L"-multi")) + }; + for (int i = 0; i < arraysize(falses); ++i) { + std::pair<std::wstring, std::wstring>& the_pair = falses[i]; + ci1.set_value(the_pair.first); + ci2.set_value(the_pair.second); + EXPECT_FALSE(ci1.EqualsBaseOf(ci2)) << the_pair.first << " " + << the_pair.second; + } +} diff --git a/chrome/installer/util/chrome_browser_operations.cc b/chrome/installer/util/chrome_browser_operations.cc new file mode 100644 index 0000000..c4acd5c --- /dev/null +++ b/chrome/installer/util/chrome_browser_operations.cc @@ -0,0 +1,82 @@ +// Copyright (c) 2011 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/util/chrome_browser_operations.h" + +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/channel_info.h" +#include "chrome/installer/util/helper.h" +#include "chrome/installer/util/master_preferences.h" +#include "chrome/installer/util/master_preferences_constants.h" +#include "chrome/installer/util/util_constants.h" + +namespace installer { + +void ChromeBrowserOperations::ReadOptions( + const MasterPreferences& prefs, + std::set<std::wstring>* options) const { + DCHECK(options); + + bool pref_value; + + if (prefs.GetBool(master_preferences::kMultiInstall, &pref_value) && + pref_value) { + options->insert(kOptionMultiInstall); + } +} + +void ChromeBrowserOperations::ReadOptions( + const CommandLine& uninstall_command, + std::set<std::wstring>* options) const { + DCHECK(options); + + if (uninstall_command.HasSwitch(switches::kMultiInstall)) + options->insert(kOptionMultiInstall); +} + +void ChromeBrowserOperations::AddKeyFiles( + const std::set<std::wstring>& options, + std::vector<FilePath>* key_files) const { + DCHECK(key_files); + key_files->push_back(FilePath(installer::kChromeDll)); +} + +void ChromeBrowserOperations::AddComDllList( + const std::set<std::wstring>& options, + std::vector<FilePath>* com_dll_list) const { +} + +void ChromeBrowserOperations::AppendProductFlags( + const std::set<std::wstring>& options, + CommandLine* uninstall_command) const { + DCHECK(uninstall_command); + + if (options.find(kOptionMultiInstall) != options.end()) { + if (!uninstall_command->HasSwitch(switches::kMultiInstall)) + uninstall_command->AppendSwitch(switches::kMultiInstall); + uninstall_command->AppendSwitch(switches::kChrome); + } +} + +bool ChromeBrowserOperations::SetChannelFlags( + const std::set<std::wstring>& options, + bool set, + ChannelInfo* channel_info) const { +#if defined(GOOGLE_CHROME_BUILD) + DCHECK(channel_info); + return channel_info->SetChrome(set); +#else + return false; +#endif +} + +bool ChromeBrowserOperations::ShouldCreateUninstallEntry( + const std::set<std::wstring>& options) const { + return true; +} + +} // namespace installer diff --git a/chrome/installer/util/chrome_browser_operations.h b/chrome/installer/util/chrome_browser_operations.h new file mode 100644 index 0000000..5628b7d --- /dev/null +++ b/chrome/installer/util/chrome_browser_operations.h @@ -0,0 +1,54 @@ +// Copyright (c) 2011 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_UTIL_CHROME_BROWSER_OPERATIONS_H_ +#define CHROME_INSTALLER_UTIL_CHROME_BROWSER_OPERATIONS_H_ +#pragma once + +#include <set> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/installer/util/product_operations.h" + +namespace installer { + +// Operations specific to Chrome; see ProductOperations for general info. +class ChromeBrowserOperations : public ProductOperations { + public: + ChromeBrowserOperations() {} + + virtual void ReadOptions(const MasterPreferences& prefs, + std::set<std::wstring>* options) const OVERRIDE; + + virtual void ReadOptions(const CommandLine& uninstall_command, + std::set<std::wstring>* options) const OVERRIDE; + + virtual void AddKeyFiles(const std::set<std::wstring>& options, + std::vector<FilePath>* key_files) const OVERRIDE; + + virtual void AddComDllList( + const std::set<std::wstring>& options, + std::vector<FilePath>* com_dll_list) const OVERRIDE; + + virtual void AppendProductFlags( + const std::set<std::wstring>& options, + CommandLine* uninstall_command) const OVERRIDE; + + virtual bool SetChannelFlags(const std::set<std::wstring>& options, + bool set, + ChannelInfo* channel_info) const OVERRIDE; + + virtual bool ShouldCreateUninstallEntry( + const std::set<std::wstring>& options) const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(ChromeBrowserOperations); +}; + +} // namespace installer + +#endif // CHROME_INSTALLER_UTIL_CHROME_BROWSER_OPERATIONS_H_ diff --git a/chrome/installer/util/chrome_browser_sxs_operations.cc b/chrome/installer/util/chrome_browser_sxs_operations.cc new file mode 100644 index 0000000..6c7fd81 --- /dev/null +++ b/chrome/installer/util/chrome_browser_sxs_operations.cc @@ -0,0 +1,22 @@ +// Copyright (c) 2011 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/util/chrome_browser_sxs_operations.h" + +#include "base/command_line.h" +#include "base/logging.h" +#include "chrome/installer/util/util_constants.h" + +namespace installer { + +void ChromeBrowserSxSOperations::AppendProductFlags( + const std::set<std::wstring>& options, + CommandLine* uninstall_command) const { + DCHECK(uninstall_command); + + uninstall_command->AppendSwitch(switches::kChromeSxS); + ChromeBrowserOperations::AppendProductFlags(options, uninstall_command); +} + +} // namespace installer diff --git a/chrome/installer/util/chrome_browser_sxs_operations.h b/chrome/installer/util/chrome_browser_sxs_operations.h new file mode 100644 index 0000000..de6101e --- /dev/null +++ b/chrome/installer/util/chrome_browser_sxs_operations.h @@ -0,0 +1,33 @@ +// Copyright (c) 2011 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_UTIL_CHROME_BROWSER_SXS_OPERATIONS_H_ +#define CHROME_INSTALLER_UTIL_CHROME_BROWSER_SXS_OPERATIONS_H_ +#pragma once + +#include <set> +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "chrome/installer/util/chrome_browser_operations.h" + +namespace installer { + +// Operations specific to Chrome SxS; see ProductOperations for general info. +class ChromeBrowserSxSOperations : public ChromeBrowserOperations { + public: + ChromeBrowserSxSOperations() {} + + virtual void AppendProductFlags( + const std::set<std::wstring>& options, + CommandLine* uninstall_command) const OVERRIDE; + + private: + DISALLOW_COPY_AND_ASSIGN(ChromeBrowserSxSOperations); +}; + +} // namespace installer + +#endif // CHROME_INSTALLER_UTIL_CHROME_BROWSER_SXS_OPERATIONS_H_ diff --git a/chrome/installer/util/chrome_frame_distribution.cc b/chrome/installer/util/chrome_frame_distribution.cc index 2507f1d..86a1abc 100644 --- a/chrome/installer/util/chrome_frame_distribution.cc +++ b/chrome/installer/util/chrome_frame_distribution.cc @@ -18,8 +18,6 @@ #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" -#include "chrome/installer/util/master_preferences_constants.h" #include "installer_util_strings.h" // NOLINT @@ -27,38 +25,8 @@ namespace { const wchar_t kChromeFrameGuid[] = L"{8BA986DA-5100-405E-AA35-86F34A02ACBF}"; } -ChromeFrameDistribution::ChromeFrameDistribution( - const installer::MasterPreferences& prefs) - : BrowserDistribution(prefs), ceee_(prefs.install_ceee()), - ready_mode_(false) { - type_ = BrowserDistribution::CHROME_FRAME; - prefs.GetBool(installer::master_preferences::kChromeFrameReadyMode, - &ready_mode_); - - bool system_install = false; - prefs.GetBool(installer::master_preferences::kSystemLevel, &system_install); - - // See if Chrome Frame is already installed. If so, we must make sure that - // the ceee and ready mode flags match. - CommandLine uninstall(CommandLine::NO_PROGRAM); - if (installer::GetUninstallSwitches(system_install, this, &uninstall)) { - if (!ceee_ && uninstall.HasSwitch(installer::switches::kCeee)) { - LOG(INFO) << "CEEE is not specified on the command line but CEEE is " - "already installed. Implicitly enabling CEEE."; - ceee_ = true; - } - - // If the user has already opted in to CF, we shouldn't set the ready-mode - // flag. If we don't do this, we might have two entries in the Add/Remove - // Programs list that can uninstall GCF. - if (ready_mode_) { - if (!uninstall.HasSwitch(installer::switches::kChromeFrameReadyMode)) { - LOG(INFO) << "Ready mode was specified on the command line but GCF " - "is already fully installed. Ignoring command line."; - ready_mode_ = false; - } - } - } +ChromeFrameDistribution::ChromeFrameDistribution() + : BrowserDistribution(CHROME_FRAME) { } std::wstring ChromeFrameDistribution::GetAppGuid() { @@ -146,61 +114,3 @@ void ChromeFrameDistribution::UpdateInstallStatus(bool system_install, InstallUtil::GetInstallReturnCode(install_status), kChromeFrameGuid); #endif } - -std::vector<FilePath> ChromeFrameDistribution::GetKeyFiles() { - std::vector<FilePath> key_files; - key_files.push_back(FilePath(installer::kChromeFrameDll)); - if (ceee_) { - key_files.push_back(FilePath(installer::kCeeeIeDll)); - key_files.push_back(FilePath(installer::kCeeeBrokerExe)); - } - return key_files; -} - -std::vector<FilePath> ChromeFrameDistribution::GetComDllList() { - std::vector<FilePath> dll_list; - dll_list.push_back(FilePath(installer::kChromeFrameDll)); - if (ceee_) { - dll_list.push_back(FilePath(installer::kCeeeInstallHelperDll)); - dll_list.push_back(FilePath(installer::kCeeeIeDll)); - } - return dll_list; -} - -void ChromeFrameDistribution::AppendUninstallCommandLineFlags( - CommandLine* cmd_line) { - DCHECK(cmd_line); - cmd_line->AppendSwitch(installer::switches::kChromeFrame); - - 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_; -} - -bool ChromeFrameDistribution::SetChannelFlags( - bool set, - installer::ChannelInfo* channel_info) { -#if defined(GOOGLE_CHROME_BUILD) - DCHECK(channel_info); - bool modified = channel_info->SetChromeFrame(set); - - // Always remove the options if we're called to remove flags. - if (!set || ceee_) - modified |= channel_info->SetCeee(set); - - if (!set || ready_mode_) - modified |= channel_info->SetReadyMode(set); - - return modified; -#else - return false; -#endif -} diff --git a/chrome/installer/util/chrome_frame_distribution.h b/chrome/installer/util/chrome_frame_distribution.h index f34cc86..043dc68 100644 --- a/chrome/installer/util/chrome_frame_distribution.h +++ b/chrome/installer/util/chrome_frame_distribution.h @@ -55,29 +55,11 @@ class ChromeFrameDistribution : public BrowserDistribution { bool incremental_install, bool multi_install, installer::InstallStatus install_status); - virtual std::vector<FilePath> GetKeyFiles(); - - virtual std::vector<FilePath> GetComDllList(); - - virtual void AppendUninstallCommandLineFlags(CommandLine* cmd_line); - - virtual bool ShouldCreateUninstallEntry(); - - virtual bool SetChannelFlags(bool set, installer::ChannelInfo* channel_info); - protected: friend class BrowserDistribution; // Disallow construction from non-friends. - explicit ChromeFrameDistribution( - const installer::MasterPreferences& prefs); - - // 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_; + ChromeFrameDistribution(); }; #endif // CHROME_INSTALLER_UTIL_CHROME_FRAME_DISTRIBUTION_H_ diff --git a/chrome/installer/util/chrome_frame_operations.cc b/chrome/installer/util/chrome_frame_operations.cc new file mode 100644 index 0000000..4ab5b2a --- /dev/null +++ b/chrome/installer/util/chrome_frame_operations.cc @@ -0,0 +1,148 @@ +// Copyright (c) 2011 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/util/chrome_frame_operations.h" + +#include "base/command_line.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/channel_info.h" +#include "chrome/installer/util/helper.h" +#include "chrome/installer/util/master_preferences.h" +#include "chrome/installer/util/master_preferences_constants.h" +#include "chrome/installer/util/util_constants.h" + +namespace installer { + +// Remove ready-mode if not multi-install. +void ChromeFrameOperations::NormalizeOptions( + std::set<std::wstring>* options) const { + std::set<std::wstring>::iterator ready_mode(options->find(kOptionReadyMode)); + if (ready_mode != options->end() && + options->find(kOptionMultiInstall) == options->end()) { + LOG(WARNING) << "--ready-mode option does not apply when --multi-install " + "is not also specified; ignoring."; + options->erase(ready_mode); + } +} + +void ChromeFrameOperations::ReadOptions( + const MasterPreferences& prefs, + std::set<std::wstring>* options) const { + DCHECK(options); + + static const struct PrefToOption { + const char* pref_name; + const wchar_t* option_name; + } map[] = { + { master_preferences::kCeee, kOptionCeee }, + { master_preferences::kChromeFrameReadyMode, kOptionReadyMode }, + { master_preferences::kMultiInstall, kOptionMultiInstall } + }; + + bool pref_value; + + for (const PrefToOption* scan = &map[0], *end = &map[arraysize(map)]; + scan != end; ++scan) { + if (prefs.GetBool(scan->pref_name, &pref_value) && pref_value) + options->insert(scan->option_name); + } + + NormalizeOptions(options); +} + +void ChromeFrameOperations::ReadOptions( + const CommandLine& uninstall_command, + std::set<std::wstring>* options) const { + DCHECK(options); + + static const struct FlagToOption { + const char* flag_name; + const wchar_t* option_name; + } map[] = { + { switches::kCeee, kOptionCeee }, + { switches::kChromeFrameReadyMode, kOptionReadyMode }, + { switches::kMultiInstall, kOptionMultiInstall } + }; + + for (const FlagToOption* scan = &map[0], *end = &map[arraysize(map)]; + scan != end; ++scan) { + if (uninstall_command.HasSwitch(scan->flag_name)) + options->insert(scan->option_name); + } + + NormalizeOptions(options); +} + +void ChromeFrameOperations::AddKeyFiles( + const std::set<std::wstring>& options, + std::vector<FilePath>* key_files) const { + DCHECK(key_files); + key_files->push_back(FilePath(installer::kChromeFrameDll)); + if (options.find(kOptionCeee) != options.end()) { + key_files->push_back(FilePath(installer::kCeeeIeDll)); + key_files->push_back(FilePath(installer::kCeeeBrokerExe)); + } +} + +void ChromeFrameOperations::AddComDllList( + const std::set<std::wstring>& options, + std::vector<FilePath>* com_dll_list) const { + DCHECK(com_dll_list); + std::vector<FilePath> dll_list; + com_dll_list->push_back(FilePath(installer::kChromeFrameDll)); + if (options.find(kOptionCeee) != options.end()) { + com_dll_list->push_back(FilePath(installer::kCeeeInstallHelperDll)); + com_dll_list->push_back(FilePath(installer::kCeeeIeDll)); + } +} + +void ChromeFrameOperations::AppendProductFlags( + const std::set<std::wstring>& options, + CommandLine* uninstall_command) const { + DCHECK(uninstall_command); + uninstall_command->AppendSwitch(switches::kChromeFrame); + + if (options.find(kOptionCeee) != options.end()) + uninstall_command->AppendSwitch(switches::kCeee); + + if (options.find(kOptionMultiInstall) != options.end()) { + if (!uninstall_command->HasSwitch(switches::kMultiInstall)) + uninstall_command->AppendSwitch(switches::kMultiInstall); + + // ready-mode is only supported in multi-installs of Chrome Frame. + if (options.find(kOptionReadyMode) != options.end()) + uninstall_command->AppendSwitch(switches::kChromeFrameReadyMode); + } +} + +bool ChromeFrameOperations::SetChannelFlags( + const std::set<std::wstring>& options, + bool set, + ChannelInfo* channel_info) const { +#if defined(GOOGLE_CHROME_BUILD) + DCHECK(channel_info); + bool modified = channel_info->SetChromeFrame(set); + + // Always remove the options if we're called to remove flags or if the + // corresponding option isn't set. + modified |= channel_info->SetCeee( + set && options.find(kOptionCeee) != options.end()); + + modified |= channel_info->SetReadyMode( + set && options.find(kOptionReadyMode) != options.end()); + + return modified; +#else + return false; +#endif +} + +bool ChromeFrameOperations::ShouldCreateUninstallEntry( + const std::set<std::wstring>& options) const { + return options.find(kOptionReadyMode) == options.end(); +} + +} // namespace installer diff --git a/chrome/installer/util/chrome_frame_operations.h b/chrome/installer/util/chrome_frame_operations.h new file mode 100644 index 0000000..267eef6 --- /dev/null +++ b/chrome/installer/util/chrome_frame_operations.h @@ -0,0 +1,58 @@ +// Copyright (c) 2011 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_UTIL_CHROME_FRAME_OPERATIONS_H_ +#define CHROME_INSTALLER_UTIL_CHROME_FRAME_OPERATIONS_H_ +#pragma once + +#include <set> +#include <string> +#include <vector> + +#include "base/basictypes.h" +#include "base/file_path.h" +#include "base/compiler_specific.h" +#include "chrome/installer/util/product_operations.h" + +namespace installer { + +// Operations specific to Chrome Frame; see ProductOperations for general info. +class ChromeFrameOperations : public ProductOperations { + public: + ChromeFrameOperations() {} + + virtual void ReadOptions(const MasterPreferences& prefs, + std::set<std::wstring>* options) const OVERRIDE; + + virtual void ReadOptions(const CommandLine& uninstall_command, + std::set<std::wstring>* options) const OVERRIDE; + + virtual void AddKeyFiles(const std::set<std::wstring>& options, + std::vector<FilePath>* key_files) const OVERRIDE; + + virtual void AddComDllList( + const std::set<std::wstring>& options, + std::vector<FilePath>* com_dll_list) const OVERRIDE; + + virtual void AppendProductFlags( + const std::set<std::wstring>& options, + CommandLine* uninstall_command) const OVERRIDE; + + virtual bool SetChannelFlags(const std::set<std::wstring>& options, + bool set, + ChannelInfo* channel_info) const OVERRIDE; + + virtual bool ShouldCreateUninstallEntry( + const std::set<std::wstring>& options) const OVERRIDE; + + protected: + void NormalizeOptions(std::set<std::wstring>* options) const; + + private: + DISALLOW_COPY_AND_ASSIGN(ChromeFrameOperations); +}; + +} // namespace installer + +#endif // CHROME_INSTALLER_UTIL_CHROME_FRAME_OPERATIONS_H_ diff --git a/chrome/installer/util/chromium_binaries_distribution.cc b/chrome/installer/util/chromium_binaries_distribution.cc new file mode 100644 index 0000000..8e39ffb --- /dev/null +++ b/chrome/installer/util/chromium_binaries_distribution.cc @@ -0,0 +1,105 @@ +// Copyright (c) 2011 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. +// +// This file declares a class that contains various method related to branding. + +#include "chrome/installer/util/google_chrome_binaries_distribution.h" + +#include "base/logging.h" + +namespace { + +const wchar_t kChromiumBinariesName[] = L"Chromium Binaries"; + +} // namespace + +ChromiumBinariesDistribution::ChromiumBinariesDistribution() + : BrowserDistribution(CHROME_BINARIES), + browser_distribution_( + BrowserDistribution::GetSpecificDistribution(CHROME_BROWSER)) { +} + +std::wstring ChromiumBinariesDistribution::GetAppGuid() { + return std::wstring(); +} + +std::wstring ChromiumBinariesDistribution::GetApplicationName() { + NOTREACHED() << "GetApplicationName unsupported"; + return std::wstring(); +} + +std::wstring ChromiumBinariesDistribution::GetAppShortCutName() { + return kChromiumBinariesName; +} + +std::wstring ChromiumBinariesDistribution::GetAlternateApplicationName() { + NOTREACHED() << "GetApplicationName unsupported"; + return std::wstring(); +} + +std::wstring ChromiumBinariesDistribution::GetBrowserAppId() { + NOTREACHED() << "GetApplicationName unsupported"; + return std::wstring(); +} + +std::wstring ChromiumBinariesDistribution::GetInstallSubDir() { + return browser_distribution_->GetInstallSubDir(); +} + +std::wstring ChromiumBinariesDistribution::GetPublisherName() { + NOTREACHED() << "GetApplicationName unsupported"; + return std::wstring(); +} + +std::wstring ChromiumBinariesDistribution::GetAppDescription() { + NOTREACHED() << "GetApplicationName unsupported"; + return std::wstring(); +} + +std::wstring ChromiumBinariesDistribution::GetLongAppDescription() { + NOTREACHED() << "GetApplicationName unsupported"; + return std::wstring(); +} + +std::string ChromiumBinariesDistribution::GetSafeBrowsingName() { + NOTREACHED() << "GetApplicationName unsupported"; + return std::string(); +} + +std::wstring ChromiumBinariesDistribution::GetStateKey() { + return std::wstring(L"Software\\").append(kChromiumBinariesName); +} + +std::wstring ChromiumBinariesDistribution::GetStateMediumKey() { + return std::wstring(L"Software\\").append(kChromiumBinariesName); +} + +std::wstring ChromiumBinariesDistribution::GetUninstallLinkName() { + NOTREACHED() << "GetApplicationName unsupported"; + return std::wstring(); +} + +std::wstring ChromiumBinariesDistribution::GetUninstallRegPath() { + NOTREACHED() << "GetApplicationName unsupported"; + return std::wstring(); +} + +std::wstring ChromiumBinariesDistribution::GetVersionKey() { + return std::wstring(L"Software\\").append(kChromiumBinariesName); +} + +bool ChromiumBinariesDistribution::CanSetAsDefault() { + NOTREACHED() << "GetApplicationName unsupported"; + return false; +} + +int ChromiumBinariesDistribution::GetIconIndex() { + NOTREACHED() << "GetApplicationName unsupported"; + return 0; +} + +bool ChromiumBinariesDistribution::GetChromeChannel(std::wstring* channel) { + NOTREACHED() << "GetApplicationName unsupported"; + return false; +} diff --git a/chrome/installer/util/chromium_binaries_distribution.h b/chrome/installer/util/chromium_binaries_distribution.h new file mode 100644 index 0000000..6ee0c4f --- /dev/null +++ b/chrome/installer/util/chromium_binaries_distribution.h @@ -0,0 +1,64 @@ +// Copyright (c) 2011 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. +// +// This file declares a class that contains various method related to branding. + +#ifndef CHROME_INSTALLER_UTIL_CHROMIUM_BINARIES_DISTRIBUTION_H_ +#define CHROME_INSTALLER_UTIL_CHROMIUM_BINARIES_DISTRIBUTION_H_ +#pragma once + +#include <string> + +#include "chrome/installer/util/browser_distribution.h" + +class ChromiumBinariesDistribution : public BrowserDistribution { + public: + virtual std::wstring GetAppGuid(); + + virtual std::wstring GetApplicationName(); + + virtual std::wstring GetAppShortCutName(); + + virtual std::wstring GetAlternateApplicationName(); + + virtual std::wstring GetBrowserAppId(); + + virtual std::wstring GetInstallSubDir(); + + virtual std::wstring GetPublisherName(); + + virtual std::wstring GetAppDescription(); + + virtual std::wstring GetLongAppDescription(); + + virtual std::string GetSafeBrowsingName(); + + virtual std::wstring GetStateKey(); + + virtual std::wstring GetStateMediumKey(); + + virtual std::wstring GetUninstallLinkName(); + + virtual std::wstring GetUninstallRegPath(); + + virtual std::wstring GetVersionKey(); + + virtual bool CanSetAsDefault(); + + virtual int GetIconIndex(); + + virtual bool GetChromeChannel(std::wstring* channel); + + protected: + friend class BrowserDistribution; + + ChromiumBinariesDistribution(); + + BrowserDistribution* browser_distribution_; + + private: + DISALLOW_COPY_AND_ASSIGN(ChromiumBinariesDistribution); +}; + +#endif // CHROME_INSTALLER_UTIL_CHROMIUM_BINARIES_DISTRIBUTION_H_ diff --git a/chrome/installer/util/google_chrome_binaries_distribution.cc b/chrome/installer/util/google_chrome_binaries_distribution.cc new file mode 100644 index 0000000..00229bf --- /dev/null +++ b/chrome/installer/util/google_chrome_binaries_distribution.cc @@ -0,0 +1,56 @@ +// Copyright (c) 2011 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. +// +// This file declares a class that contains various method related to branding. + +#include "chrome/installer/util/google_chrome_binaries_distribution.h" + +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/google_update_settings.h" +#include "chrome/installer/util/install_util.h" + +namespace { + +const wchar_t kChromeBinariesGuid[] = L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; +const wchar_t kChromeBinariesName[] = L"Google Chrome binaries"; + +} // namespace + +GoogleChromeBinariesDistribution::GoogleChromeBinariesDistribution() + : ChromiumBinariesDistribution() { +} + +std::wstring GoogleChromeBinariesDistribution::GetAppGuid() { + return kChromeBinariesGuid; +} + +std::wstring GoogleChromeBinariesDistribution::GetAppShortCutName() { + return kChromeBinariesName; +} + +std::wstring GoogleChromeBinariesDistribution::GetStateKey() { + return std::wstring(google_update::kRegPathClientState) + .append(1, L'\\') + .append(kChromeBinariesGuid); +} + +std::wstring GoogleChromeBinariesDistribution::GetStateMediumKey() { + return std::wstring(google_update::kRegPathClientStateMedium) + .append(1, L'\\') + .append(kChromeBinariesGuid); +} + +std::wstring GoogleChromeBinariesDistribution::GetVersionKey() { + return std::wstring(google_update::kRegPathClients) + .append(1, L'\\') + .append(kChromeBinariesGuid); +} + +void GoogleChromeBinariesDistribution::UpdateInstallStatus(bool system_install, + bool incremental_install, bool multi_install, + installer::InstallStatus install_status) { + GoogleUpdateSettings::UpdateInstallStatus(system_install, + incremental_install, multi_install, + InstallUtil::GetInstallReturnCode(install_status), kChromeBinariesGuid); +} diff --git a/chrome/installer/util/google_chrome_binaries_distribution.h b/chrome/installer/util/google_chrome_binaries_distribution.h new file mode 100644 index 0000000..3ddbf34 --- /dev/null +++ b/chrome/installer/util/google_chrome_binaries_distribution.h @@ -0,0 +1,38 @@ +// Copyright (c) 2011 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. +// +// This file declares a class that contains various method related to branding. + +#ifndef CHROME_INSTALLER_UTIL_GOOGLE_CHROME_BINARIES_DISTRIBUTION_H_ +#define CHROME_INSTALLER_UTIL_GOOGLE_CHROME_BINARIES_DISTRIBUTION_H_ +#pragma once + +#include "chrome/installer/util/chromium_binaries_distribution.h" + +class GoogleChromeBinariesDistribution : public ChromiumBinariesDistribution { + public: + virtual std::wstring GetAppGuid(); + + virtual std::wstring GetAppShortCutName(); + + virtual std::wstring GetStateKey(); + + virtual std::wstring GetStateMediumKey(); + + virtual std::wstring GetVersionKey(); + + virtual void UpdateInstallStatus(bool system_install, + bool incremental_install, bool multi_install, + installer::InstallStatus install_status); + + protected: + friend class BrowserDistribution; + + GoogleChromeBinariesDistribution(); + + private: + DISALLOW_COPY_AND_ASSIGN(GoogleChromeBinariesDistribution); +}; + +#endif // CHROME_INSTALLER_UTIL_GOOGLE_CHROME_BINARIES_DISTRIBUTION_H_ diff --git a/chrome/installer/util/google_chrome_distribution.cc b/chrome/installer/util/google_chrome_distribution.cc index e0ab343..300ee1d 100644 --- a/chrome/installer/util/google_chrome_distribution.cc +++ b/chrome/installer/util/google_chrome_distribution.cc @@ -253,9 +253,9 @@ bool RelaunchSetupAsConsoleUser(const std::string& flag) { } // namespace -GoogleChromeDistribution::GoogleChromeDistribution( - const installer::MasterPreferences& prefs) - : BrowserDistribution(prefs), product_guid_(kChromeGuid) { +GoogleChromeDistribution::GoogleChromeDistribution() + : BrowserDistribution(CHROME_BROWSER), + product_guid_(kChromeGuid) { } // The functions below are not used by the 64-bit Windows binary - @@ -633,7 +633,8 @@ void GoogleChromeDistribution::LaunchUserExperiment( // User qualifies for the experiment. Launch chrome with --try-chrome=flavor. void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, - const installer::Product& installation) { + const installer::Product& installation, + const FilePath& application_path) { bool has_welcome_url = (flavor == 0); // Possibly add a url to launch depending on the experiment flavor. CommandLine options(CommandLine::NO_PROGRAM); @@ -651,7 +652,7 @@ void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, } // Launch chrome now. It will show the toast UI. int32 exit_code = 0; - if (!installation.LaunchChromeAndWait(options, &exit_code)) + if (!installation.LaunchChromeAndWait(application_path, options, &exit_code)) return; // The chrome process has exited, figure out what happened. @@ -688,10 +689,3 @@ void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, base::LaunchApp(cmd, false, false, NULL); } #endif - -bool GoogleChromeDistribution::SetChannelFlags( - bool set, - installer::ChannelInfo* channel_info) { - DCHECK(channel_info); - return channel_info->SetChrome(set); -} diff --git a/chrome/installer/util/google_chrome_distribution.h b/chrome/installer/util/google_chrome_distribution.h index 038d27b..9610518 100644 --- a/chrome/installer/util/google_chrome_distribution.h +++ b/chrome/installer/util/google_chrome_distribution.h @@ -79,18 +79,16 @@ class GoogleChromeDistribution : public BrowserDistribution { // toast experiment. It will use chrome to show the UI and it will record the // outcome in the registry. virtual void InactiveUserToastExperiment(int flavor, - const installer::Product& installation); + const installer::Product& installation, + const FilePath& application_path); const std::wstring& product_guid() { return product_guid_; } - virtual bool SetChannelFlags(bool set, installer::ChannelInfo* channel_info); - protected: void set_product_guid(const std::wstring& guid) { product_guid_ = guid; } // Disallow construction from others. - explicit GoogleChromeDistribution( - const installer::MasterPreferences& prefs); + GoogleChromeDistribution(); private: friend class BrowserDistribution; diff --git a/chrome/installer/util/google_chrome_distribution_dummy.cc b/chrome/installer/util/google_chrome_distribution_dummy.cc index 1b515d9..9b022be 100644 --- a/chrome/installer/util/google_chrome_distribution_dummy.cc +++ b/chrome/installer/util/google_chrome_distribution_dummy.cc @@ -15,9 +15,8 @@ #include "base/file_path.h" #include "base/logging.h" -GoogleChromeDistribution::GoogleChromeDistribution( - const installer::MasterPreferences& prefs) - : BrowserDistribution(prefs) { +GoogleChromeDistribution::GoogleChromeDistribution() + : BrowserDistribution(CHROME_BROWSER) { } void GoogleChromeDistribution::DoPostUninstallOperations( @@ -114,7 +113,8 @@ void GoogleChromeDistribution::LaunchUserExperiment( } void GoogleChromeDistribution::InactiveUserToastExperiment(int flavor, - const installer::Product& installation) { + const installer::Product& installation, + const FilePath& application_path) { NOTREACHED(); } @@ -135,10 +135,3 @@ bool GoogleChromeDistribution::BuildUninstallMetricsString( NOTREACHED(); return false; } - -bool GoogleChromeDistribution::SetChannelFlags( - bool set, - installer::ChannelInfo* channel_info) { - NOTREACHED(); - return false; -} diff --git a/chrome/installer/util/google_chrome_distribution_unittest.cc b/chrome/installer/util/google_chrome_distribution_unittest.cc index 1222ff3..ed566ea 100644 --- a/chrome/installer/util/google_chrome_distribution_unittest.cc +++ b/chrome/installer/util/google_chrome_distribution_unittest.cc @@ -11,7 +11,6 @@ #include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/google_chrome_distribution.h" -#include "chrome/installer/util/master_preferences.h" #include "testing/gtest/include/gtest/gtest.h" #if defined(GOOGLE_CHROME_BUILD) @@ -52,12 +51,9 @@ TEST(GoogleChromeDistTest, TestExtractUninstallMetrics) { ASSERT_TRUE(root.get()); std::wstring uninstall_metrics_string; - const installer::MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); - GoogleChromeDistribution* dist = static_cast<GoogleChromeDistribution*>( BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs)); + BrowserDistribution::CHROME_BROWSER)); EXPECT_TRUE( dist->ExtractUninstallMetrics(*static_cast<DictionaryValue*>(root.get()), diff --git a/chrome/installer/util/google_chrome_sxs_distribution.cc b/chrome/installer/util/google_chrome_sxs_distribution.cc index 9461a52..9ea35f1 100644 --- a/chrome/installer/util/google_chrome_sxs_distribution.cc +++ b/chrome/installer/util/google_chrome_sxs_distribution.cc @@ -9,7 +9,7 @@ #include "base/command_line.h" #include "base/logging.h" -#include "installer_util_strings.h" +#include "installer_util_strings.h" // NOLINT namespace { @@ -20,9 +20,8 @@ const int kSxSIconIndex = 4; } // namespace -GoogleChromeSxSDistribution::GoogleChromeSxSDistribution( - const installer::MasterPreferences& prefs) - : GoogleChromeDistribution(prefs) { +GoogleChromeSxSDistribution::GoogleChromeSxSDistribution() + : GoogleChromeDistribution() { GoogleChromeDistribution::set_product_guid(kChromeSxSGuid); } @@ -62,9 +61,3 @@ bool GoogleChromeSxSDistribution::GetChromeChannel(std::wstring* channel) { std::wstring GoogleChromeSxSDistribution::ChannelName() { return kChannelName; } - -void GoogleChromeSxSDistribution::AppendUninstallCommandLineFlags( - CommandLine* cmd_line) { - DCHECK(cmd_line); - cmd_line->AppendSwitch(installer::switches::kChromeSxS); -} diff --git a/chrome/installer/util/google_chrome_sxs_distribution.h b/chrome/installer/util/google_chrome_sxs_distribution.h index 7088c56..10df80a 100644 --- a/chrome/installer/util/google_chrome_sxs_distribution.h +++ b/chrome/installer/util/google_chrome_sxs_distribution.h @@ -32,13 +32,11 @@ class GoogleChromeSxSDistribution : public GoogleChromeDistribution { virtual bool GetChromeChannel(std::wstring* channel); // returns the channel name for GoogleChromeSxSDistribution static std::wstring ChannelName(); - virtual void AppendUninstallCommandLineFlags(CommandLine* cmd_line); private: friend class BrowserDistribution; // Disallow construction from non-friends. - explicit GoogleChromeSxSDistribution( - const installer::MasterPreferences& prefs); + GoogleChromeSxSDistribution(); }; #endif // CHROME_INSTALLER_UTIL_GOOGLE_CHROME_SXS_DISTRIBUTION_H_ diff --git a/chrome/installer/util/google_update_settings.cc b/chrome/installer/util/google_update_settings.cc index 44ee0f0..76d13d2 100644 --- a/chrome/installer/util/google_update_settings.cc +++ b/chrome/installer/util/google_update_settings.cc @@ -17,11 +17,11 @@ #include "chrome/installer/util/channel_info.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/install_util.h" -#include "chrome/installer/util/package.h" -#include "chrome/installer/util/package_properties.h" +#include "chrome/installer/util/installer_state.h" #include "chrome/installer/util/product.h" using base::win::RegKey; +using installer::InstallerState; namespace { @@ -126,33 +126,43 @@ bool GoogleUpdateSettings::SetMetricsId(const std::wstring& metrics_id) { } bool GoogleUpdateSettings::SetEULAConsent( - const installer::Package& package, + const InstallerState& installer_state, bool consented) { // If this is a multi install, Google Update will have put eulaaccepted=0 into // the ClientState key of the multi-installer. Conduct a brief search for // this value and store the consent in the corresponding location. - HKEY root = package.system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - std::wstring reg_path = package.properties()->GetStateMediumKey(); - EulaSearchResult status = HasEULASetting( - root, package.properties()->GetStateKey(), !consented); + HKEY root = installer_state.root_key(); + EulaSearchResult status = NO_SETTING; + std::wstring reg_path; + std::wstring fallback_reg_path; + + if (installer_state.package_type() == InstallerState::MULTI_PACKAGE) { + BrowserDistribution* binaries_dist = + installer_state.multi_package_binaries_distribution(); + fallback_reg_path = reg_path = binaries_dist->GetStateMediumKey(); + status = HasEULASetting(root, binaries_dist->GetStateKey(), !consented); + } if (status != FOUND_SAME_SETTING) { EulaSearchResult new_status = NO_SETTING; - installer::Products::const_iterator scan = package.products().begin(); - installer::Products::const_iterator end = package.products().end(); + installer::Products::const_iterator scan = + installer_state.products().begin(); + installer::Products::const_iterator end = + installer_state.products().end(); for (; status != FOUND_SAME_SETTING && scan != end; ++scan) { - const installer::Product& product = *(scan->get()); - new_status = HasEULASetting(root, product.distribution()->GetStateKey(), + if (fallback_reg_path.empty()) + fallback_reg_path = (*scan)->distribution()->GetStateMediumKey(); + new_status = HasEULASetting(root, (*scan)->distribution()->GetStateKey(), !consented); if (new_status > status) { status = new_status; - reg_path = product.distribution()->GetStateMediumKey(); + reg_path = (*scan)->distribution()->GetStateMediumKey(); } } if (status == NO_SETTING) { LOG(WARNING) - << "eulaaccepted value not found; setting consent on package"; - reg_path = package.properties()->GetStateMediumKey(); + << "eulaaccepted value not found; setting consent in key " + << fallback_reg_path; + reg_path = fallback_reg_path; } } RegKey key(HKEY_LOCAL_MACHINE, reg_path.c_str(), KEY_SET_VALUE); diff --git a/chrome/installer/util/google_update_settings.h b/chrome/installer/util/google_update_settings.h index 36e020c..12b5fa5 100644 --- a/chrome/installer/util/google_update_settings.h +++ b/chrome/installer/util/google_update_settings.h @@ -12,7 +12,7 @@ namespace installer { class ChannelInfo; -class Package; +class InstallerState; } // This class provides accessors to the Google Update 'ClientState' information @@ -38,7 +38,7 @@ class GoogleUpdateSettings { // Sets the machine-wide EULA consented flag required on OEM installs. // Returns false if the setting could not be recorded. - static bool SetEULAConsent(const installer::Package& package, + static bool SetEULAConsent(const installer::InstallerState& installer_state, bool consented); // Returns the last time chrome was run in days. It uses a recorded value diff --git a/chrome/installer/util/google_update_settings_unittest.cc b/chrome/installer/util/google_update_settings_unittest.cc index 96a6dae..2234192 100644 --- a/chrome/installer/util/google_update_settings_unittest.cc +++ b/chrome/installer/util/google_update_settings_unittest.cc @@ -15,9 +15,9 @@ #include "chrome/installer/util/channel_info.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/google_update_settings.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/package.h" -#include "chrome/installer/util/package_properties.h" #include "chrome/installer/util/product.h" #include "chrome/installer/util/work_item_list.h" #include "testing/gtest/include/gtest/gtest.h" @@ -404,28 +404,32 @@ TEST_F(GoogleUpdateSettingsTest, UpdateInstallStatusTest) { } TEST_F(GoogleUpdateSettingsTest, SetEULAConsent) { - const bool multi_install = false; + const bool multi_install = true; const bool system_level = true; - HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - const installer::MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); - installer::ChromePackageProperties properties; - BrowserDistribution* distribution = - BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs); - scoped_refptr<installer::Package> package( - new installer::Package(multi_install, system_level, FilePath(), - &properties)); - scoped_refptr<installer::Product> product( - new installer::Product(distribution, package.get())); + CommandLine cmd_line = CommandLine::FromString( + std::wstring(L"setup.exe") + + (multi_install ? L" --multi-install --chrome" : L"") + + (system_level ? L" --system-level" : L"")); + installer::MasterPreferences prefs(cmd_line); + installer::InstallationState machine_state; + machine_state.Initialize(); + installer::InstallerState installer_state; + installer_state.Initialize(cmd_line, prefs, machine_state); + HKEY root = installer_state.root_key(); + RegKey key; DWORD value; + BrowserDistribution* binaries = + installer_state.multi_package_binaries_distribution(); + EXPECT_EQ(BrowserDistribution::CHROME_BINARIES, binaries->GetType()); + BrowserDistribution* chrome = + installer_state.products()[0]->distribution(); + EXPECT_EQ(BrowserDistribution::CHROME_BROWSER, chrome->GetType()); // By default, eulaconsent ends up on the package. - EXPECT_TRUE(GoogleUpdateSettings::SetEULAConsent(*package.get(), true)); + EXPECT_TRUE(GoogleUpdateSettings::SetEULAConsent(installer_state, true)); EXPECT_EQ(ERROR_SUCCESS, - key.Open(HKEY_LOCAL_MACHINE, properties.GetStateMediumKey().c_str(), + key.Open(HKEY_LOCAL_MACHINE, binaries->GetStateMediumKey().c_str(), KEY_QUERY_VALUE | KEY_SET_VALUE)); EXPECT_EQ(ERROR_SUCCESS, key.ReadValueDW(google_update::kRegEULAAceptedField, &value)); @@ -435,14 +439,14 @@ TEST_F(GoogleUpdateSettingsTest, SetEULAConsent) { // But it will end up on the product if needed EXPECT_EQ(ERROR_SUCCESS, - key.Create(HKEY_LOCAL_MACHINE, distribution->GetStateKey().c_str(), + key.Create(HKEY_LOCAL_MACHINE, chrome->GetStateKey().c_str(), KEY_SET_VALUE)); EXPECT_EQ(ERROR_SUCCESS, key.WriteValue(google_update::kRegEULAAceptedField, static_cast<DWORD>(0))); - EXPECT_TRUE(GoogleUpdateSettings::SetEULAConsent(*package.get(), true)); + EXPECT_TRUE(GoogleUpdateSettings::SetEULAConsent(installer_state, true)); EXPECT_EQ(ERROR_SUCCESS, - key.Open(HKEY_LOCAL_MACHINE, distribution->GetStateMediumKey().c_str(), + key.Open(HKEY_LOCAL_MACHINE, chrome->GetStateMediumKey().c_str(), KEY_QUERY_VALUE | KEY_SET_VALUE)); EXPECT_EQ(ERROR_SUCCESS, key.ReadValueDW(google_update::kRegEULAAceptedField, &value)); diff --git a/chrome/installer/util/helper.cc b/chrome/installer/util/helper.cc index 6cddf25..82f8a36 100644 --- a/chrome/installer/util/helper.cc +++ b/chrome/installer/util/helper.cc @@ -4,17 +4,13 @@ #include "chrome/installer/util/helper.h" -#include "base/command_line.h" -#include "base/file_path.h" #include "base/logging.h" +#include "base/file_path.h" #include "base/path_service.h" -#include "base/win/registry.h" #include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/install_util.h" -#include "chrome/installer/util/master_preferences.h" -#include "chrome/installer/util/package_properties.h" - -using base::win::RegKey; +#include "chrome/installer/util/util_constants.h" namespace { @@ -40,101 +36,32 @@ FilePath GetChromeInstallBasePath(bool system, namespace installer { -bool IsInstalledAsMulti(bool system_install, BrowserDistribution* dist) { - bool installed_as_multi = false; - CommandLine cmd(CommandLine::NO_PROGRAM); - if (GetUninstallSwitches(system_install, dist, &cmd)) - installed_as_multi = cmd.HasSwitch(installer::switches::kMultiInstall); - return installed_as_multi; -} - -bool GetUninstallSwitches(bool system_install, BrowserDistribution* dist, - CommandLine* cmd_line_switches) { - scoped_ptr<Version> installed(InstallUtil::GetChromeVersion(dist, - system_install)); - if (installed.get()) { - 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"foo.exe "); - *cmd_line_switches = CommandLine::FromString(args); - } else { - LOG(ERROR) << "No uninstallation arguments for " - << dist->GetApplicationName(); - installed.reset(); - } - } else { - LOG(ERROR) << "Product looks to be installed but we can't access the " - "state key: " << dist->GetApplicationName(); - installed.reset(); - } - } - - return installed.get() != NULL; -} - FilePath GetChromeInstallPath(bool system_install, BrowserDistribution* dist) { - return GetChromeInstallBasePath(system_install, dist, - installer::kInstallBinaryDir); + return GetChromeInstallBasePath(system_install, dist, kInstallBinaryDir); } 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<Version> 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. +BrowserDistribution* GetBinariesDistribution(bool system_install) { + BrowserDistribution* dist = BrowserDistribution::GetDistribution(); + ProductState state; - // 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(); + // If we're part of a multi-install, we need to poll using the multi-installer + // package's app guid rather than the browser's or Chrome Frame's app guid. + // If we can't read the app's state from the registry, assume it isn't + // multi-installed. + if (state.Initialize(system_install, dist) && state.is_multi_install()) { + return BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES); + } else { + return dist; } - - // 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); } std::wstring GetAppGuidForUpdates(bool system_install) { - BrowserDistribution* dist = BrowserDistribution::GetDistribution(); - - // If we're part of a multi-install, we need to poll using the multi-installer - // package's app guid rather than the browser's or Chrome Frame's app guid. - return IsInstalledAsMulti(system_install, dist) ? - ActivePackageProperties().GetAppGuid() : - dist->GetAppGuid(); + return GetBinariesDistribution(system_install)->GetAppGuid(); } } // namespace installer. diff --git a/chrome/installer/util/helper.h b/chrome/installer/util/helper.h index bcee4c6..fa996b6 100644 --- a/chrome/installer/util/helper.h +++ b/chrome/installer/util/helper.h @@ -16,17 +16,6 @@ class FilePath; namespace installer { -// Checks if a distribution is currently installed as part of a multi-install. -bool IsInstalledAsMulti(bool system_install, BrowserDistribution* dist); - -// Retrieves the command line switches for uninstalling the distribution. -// Note that the returned CommandLine object does not include a "program". -// Only the switches should be used. -// Returns true if the product is installed and the uninstall switches -// were successfully retrieved, otherwise false. -bool GetUninstallSwitches(bool system_install, BrowserDistribution* dist, - CommandLine* cmd_line_switches); - // 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 @@ -40,18 +29,10 @@ 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); +// Returns the distribution corresponding to the current process's binaries. +// In the case of a multi-install product, this will be the CHROME_BINARIES +// distribution. +BrowserDistribution* GetBinariesDistribution(bool system_install); // Returns the app guid under which the current process receives updates from // Google Update. diff --git a/chrome/installer/util/helper_unittest.cc b/chrome/installer/util/helper_unittest.cc deleted file mode 100644 index 48b2476..0000000 --- a/chrome/installer/util/helper_unittest.cc +++ /dev/null @@ -1,204 +0,0 @@ -// Copyright (c) 2006-2008 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 <windows.h> - -#include <fstream> - -#include "base/base_paths.h" -#include "base/file_util.h" -#include "base/path_service.h" -#include "base/process_util.h" -#include "base/string_util.h" -#include "base/version.h" -#include "chrome/installer/util/helper.h" -#include "chrome/installer/util/package.h" -#include "chrome/installer/util/package_properties.h" -#include "chrome/installer/util/work_item.h" -#include "testing/gtest/include/gtest/gtest.h" - -using installer::ChromePackageProperties; -using installer::Package; - -namespace { - class SetupHelperTest : public testing::Test { - protected: - virtual void SetUp() { - // Name a subdirectory of the user temp directory. - ASSERT_TRUE(PathService::Get(base::DIR_TEMP, &test_dir_)); - test_dir_ = test_dir_.AppendASCII("SetupHelperTest"); - - // Create a fresh, empty copy of this test directory. - file_util::Delete(test_dir_, true); - file_util::CreateDirectoryW(test_dir_); - ASSERT_TRUE(file_util::PathExists(test_dir_)); - } - - virtual void TearDown() { - logging::CloseLogFile(); - // Clean up test directory - ASSERT_TRUE(file_util::Delete(test_dir_, true)); - ASSERT_FALSE(file_util::PathExists(test_dir_)); - } - - // the path to temporary directory used to contain the test operations - FilePath test_dir_; - }; - - // Simple function to dump some text into a new file. - void CreateTextFile(const std::wstring& filename, - const std::wstring& contents) { - std::ofstream file; - file.open(filename.c_str()); - ASSERT_TRUE(file.is_open()); - file << contents; - file.close(); - } - - wchar_t text_content_1[] = L"delete me"; - wchar_t text_content_2[] = L"delete me as well"; -}; - -// Delete version directories. Everything lower than the given version -// should be deleted. -TEST_F(SetupHelperTest, Delete) { - // Create a Chrome dir - FilePath chrome_dir(test_dir_); - chrome_dir = chrome_dir.AppendASCII("chrome"); - file_util::CreateDirectory(chrome_dir); - ASSERT_TRUE(file_util::PathExists(chrome_dir)); - - FilePath chrome_dir_1(chrome_dir); - chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0"); - file_util::CreateDirectory(chrome_dir_1); - ASSERT_TRUE(file_util::PathExists(chrome_dir_1)); - - FilePath chrome_dir_2(chrome_dir); - chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0"); - file_util::CreateDirectory(chrome_dir_2); - ASSERT_TRUE(file_util::PathExists(chrome_dir_2)); - - FilePath chrome_dir_3(chrome_dir); - chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0"); - file_util::CreateDirectory(chrome_dir_3); - ASSERT_TRUE(file_util::PathExists(chrome_dir_3)); - - FilePath chrome_dir_4(chrome_dir); - chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0"); - file_util::CreateDirectory(chrome_dir_4); - ASSERT_TRUE(file_util::PathExists(chrome_dir_4)); - - FilePath chrome_dll_1(chrome_dir_1); - chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll"); - CreateTextFile(chrome_dll_1.value(), text_content_1); - ASSERT_TRUE(file_util::PathExists(chrome_dll_1)); - - FilePath chrome_dll_2(chrome_dir_2); - chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll"); - CreateTextFile(chrome_dll_2.value(), text_content_1); - ASSERT_TRUE(file_util::PathExists(chrome_dll_2)); - - FilePath chrome_dll_3(chrome_dir_3); - chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll"); - CreateTextFile(chrome_dll_3.value(), text_content_1); - ASSERT_TRUE(file_util::PathExists(chrome_dll_3)); - - FilePath chrome_dll_4(chrome_dir_4); - chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll"); - CreateTextFile(chrome_dll_4.value(), text_content_1); - ASSERT_TRUE(file_util::PathExists(chrome_dll_4)); - - scoped_ptr<Version> latest_version(Version::GetVersionFromString("1.0.4.0")); - ChromePackageProperties properties; - scoped_refptr<Package> package(new Package(false, true, chrome_dir, - &properties)); - package->RemoveOldVersionDirectories(*latest_version.get()); - - // old versions should be gone - EXPECT_FALSE(file_util::PathExists(chrome_dir_1)); - EXPECT_FALSE(file_util::PathExists(chrome_dir_2)); - EXPECT_FALSE(file_util::PathExists(chrome_dir_3)); - // the latest version should stay - EXPECT_TRUE(file_util::PathExists(chrome_dll_4)); -} - -// Delete older version directories, keeping the one in used intact. -TEST_F(SetupHelperTest, DeleteInUsed) { - // Create a Chrome dir - FilePath chrome_dir(test_dir_); - chrome_dir = chrome_dir.AppendASCII("chrome"); - file_util::CreateDirectory(chrome_dir); - ASSERT_TRUE(file_util::PathExists(chrome_dir)); - - FilePath chrome_dir_1(chrome_dir); - chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0"); - file_util::CreateDirectory(chrome_dir_1); - ASSERT_TRUE(file_util::PathExists(chrome_dir_1)); - - FilePath chrome_dir_2(chrome_dir); - chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0"); - file_util::CreateDirectory(chrome_dir_2); - ASSERT_TRUE(file_util::PathExists(chrome_dir_2)); - - FilePath chrome_dir_3(chrome_dir); - chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0"); - file_util::CreateDirectory(chrome_dir_3); - ASSERT_TRUE(file_util::PathExists(chrome_dir_3)); - - FilePath chrome_dir_4(chrome_dir); - chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0"); - file_util::CreateDirectory(chrome_dir_4); - ASSERT_TRUE(file_util::PathExists(chrome_dir_4)); - - FilePath chrome_dll_1(chrome_dir_1); - chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll"); - CreateTextFile(chrome_dll_1.value(), text_content_1); - ASSERT_TRUE(file_util::PathExists(chrome_dll_1)); - - FilePath chrome_dll_2(chrome_dir_2); - chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll"); - CreateTextFile(chrome_dll_2.value(), text_content_1); - ASSERT_TRUE(file_util::PathExists(chrome_dll_2)); - - // Open the file to make it in use. - std::ofstream file; - file.open(chrome_dll_2.value().c_str()); - - FilePath chrome_othera_2(chrome_dir_2); - chrome_othera_2 = chrome_othera_2.AppendASCII("othera.dll"); - CreateTextFile(chrome_othera_2.value(), text_content_2); - ASSERT_TRUE(file_util::PathExists(chrome_othera_2)); - - FilePath chrome_otherb_2(chrome_dir_2); - chrome_otherb_2 = chrome_otherb_2.AppendASCII("otherb.dll"); - CreateTextFile(chrome_otherb_2.value(), text_content_2); - ASSERT_TRUE(file_util::PathExists(chrome_otherb_2)); - - FilePath chrome_dll_3(chrome_dir_3); - chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll"); - CreateTextFile(chrome_dll_3.value(), text_content_1); - ASSERT_TRUE(file_util::PathExists(chrome_dll_3)); - - FilePath chrome_dll_4(chrome_dir_4); - chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll"); - CreateTextFile(chrome_dll_4.value(), text_content_1); - ASSERT_TRUE(file_util::PathExists(chrome_dll_4)); - - scoped_ptr<Version> latest_version(Version::GetVersionFromString("1.0.4.0")); - ChromePackageProperties properties; - scoped_refptr<Package> install_path(new Package(false, true, chrome_dir, - &properties)); - install_path->RemoveOldVersionDirectories(*latest_version.get()); - - // old versions not in used should be gone - EXPECT_FALSE(file_util::PathExists(chrome_dir_1)); - EXPECT_FALSE(file_util::PathExists(chrome_dir_3)); - // every thing under in used version should stay - EXPECT_TRUE(file_util::PathExists(chrome_dir_2)); - EXPECT_TRUE(file_util::PathExists(chrome_dll_2)); - EXPECT_TRUE(file_util::PathExists(chrome_othera_2)); - EXPECT_TRUE(file_util::PathExists(chrome_otherb_2)); - // the latest version should stay - EXPECT_TRUE(file_util::PathExists(chrome_dll_4)); -} diff --git a/chrome/installer/util/install_util.cc b/chrome/installer/util/install_util.cc index 0e598dd..48af659 100644 --- a/chrome/installer/util/install_util.cc +++ b/chrome/installer/util/install_util.cc @@ -12,24 +12,24 @@ #include <algorithm> +#include "base/command_line.h" #include "base/file_util.h" #include "base/logging.h" #include "base/path_service.h" #include "base/scoped_ptr.h" #include "base/string_util.h" #include "base/values.h" +#include "base/version.h" #include "base/win/registry.h" #include "base/win/windows_version.h" #include "chrome/common/json_value_serializer.h" #include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/l10n_string_util.h" -#include "chrome/installer/util/master_preferences_constants.h" #include "chrome/installer/util/util_constants.h" #include "chrome/installer/util/work_item_list.h" using base::win::RegKey; -using installer::MasterPreferences; bool InstallUtil::ExecuteExeAsAdmin(const CommandLine& cmd, DWORD* exit_code) { FilePath::StringType program(cmd.GetProgram().value()); @@ -240,3 +240,25 @@ int InstallUtil::GetInstallReturnCode(installer::InstallStatus status) { return status; } } + +// static +void InstallUtil::MakeUninstallCommand(const std::wstring& exe_path, + const std::wstring& arguments, + CommandLine* command_line) { + const bool no_program = exe_path.empty(); + + // Return a bunch of nothingness. + if (no_program && arguments.empty()) { + *command_line = CommandLine(CommandLine::NO_PROGRAM); + } else { + // Form a full command line string. + std::wstring command; + command.append(1, L'"') + .append(no_program ? L"" : exe_path) + .append(L"\" ") + .append(arguments); + + // If we have a program name, return this complete command line. + *command_line = CommandLine::FromString(command); + } +} diff --git a/chrome/installer/util/install_util.h b/chrome/installer/util/install_util.h index f2ba551..1912c09 100644 --- a/chrome/installer/util/install_util.h +++ b/chrome/installer/util/install_util.h @@ -15,13 +15,12 @@ #include <string> #include "base/basictypes.h" -#include "base/command_line.h" -#include "base/version.h" -#include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/util_constants.h" -class WorkItemList; class BrowserDistribution; +class CommandLine; +class Version; +class WorkItemList; namespace base { namespace win { @@ -107,6 +106,11 @@ class InstallUtil { // Returns zero on install success, or an InstallStatus value otherwise. static int GetInstallReturnCode(installer::InstallStatus install_status); + // Composes |exe_path| and |arguments| into |command_line|. + static void MakeUninstallCommand(const std::wstring& exe_path, + const std::wstring& arguments, + CommandLine* command_line); + private: DISALLOW_COPY_AND_ASSIGN(InstallUtil); }; diff --git a/chrome/installer/util/install_util_unittest.cc b/chrome/installer/util/install_util_unittest.cc index b0e05ba..a5cb86f 100644 --- a/chrome/installer/util/install_util_unittest.cc +++ b/chrome/installer/util/install_util_unittest.cc @@ -3,6 +3,7 @@ // found in the LICENSE file. #include <string> +#include <utility> #include "base/command_line.h" #include "base/win/registry.h" @@ -34,17 +35,17 @@ TEST_F(InstallUtilTest, InstallerResult) { // check results for a fresh install of single Chrome { TempRegKeyOverride override(root, L"root_inst_res"); - const MasterPreferences prefs( - CommandLine::FromString(L"setup.exe --system-level")); + CommandLine cmd_line = CommandLine::FromString(L"setup.exe --system-level"); + const MasterPreferences prefs(cmd_line); InstallationState machine_state; - machine_state.Initialize(prefs); + machine_state.Initialize(); InstallerState state; - state.Initialize(prefs, machine_state); + state.Initialize(cmd_line, prefs, machine_state); InstallUtil::WriteInstallerResult(system_level, state.state_key(), installer::FIRST_INSTALL_SUCCESS, 0, &launch_cmd); BrowserDistribution* distribution = BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs); + BrowserDistribution::CHROME_BROWSER); EXPECT_EQ(ERROR_SUCCESS, key.Open(root, distribution->GetStateKey().c_str(), KEY_READ)); EXPECT_EQ(ERROR_SUCCESS, @@ -56,18 +57,18 @@ TEST_F(InstallUtilTest, InstallerResult) { // check results for a fresh install of multi Chrome { TempRegKeyOverride override(root, L"root_inst_res"); - const MasterPreferences prefs( - CommandLine::FromString( - L"setup.exe --system-level --multi-install --chrome")); + CommandLine cmd_line = CommandLine::FromString( + L"setup.exe --system-level --multi-install --chrome"); + const MasterPreferences prefs(cmd_line); InstallationState machine_state; - machine_state.Initialize(prefs); + machine_state.Initialize(); InstallerState state; - state.Initialize(prefs, machine_state); + state.Initialize(cmd_line, prefs, machine_state); InstallUtil::WriteInstallerResult(system_level, state.state_key(), installer::FIRST_INSTALL_SUCCESS, 0, &launch_cmd); BrowserDistribution* distribution = BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs); + BrowserDistribution::CHROME_BROWSER); EXPECT_EQ(ERROR_SUCCESS, key.Open(root, distribution->GetStateKey().c_str(), KEY_READ)); EXPECT_EQ(ERROR_SUCCESS, @@ -77,3 +78,27 @@ TEST_F(InstallUtilTest, InstallerResult) { } TempRegKeyOverride::DeleteAllTempKeys(); } + +TEST_F(InstallUtilTest, MakeUninstallCommand) { + CommandLine command_line(CommandLine::NO_PROGRAM); + + std::pair<std::wstring, std::wstring> params[] = { + std::make_pair(std::wstring(L""), std::wstring(L"")), + std::make_pair(std::wstring(L""), std::wstring(L"--do-something --silly")), + std::make_pair(std::wstring(L"spam.exe"), std::wstring(L"")), + std::make_pair(std::wstring(L"spam.exe"), + std::wstring(L"--do-something --silly")), + }; + for (int i = 0; i < arraysize(params); ++i) { + std::pair<std::wstring, std::wstring>& param = params[i]; + InstallUtil::MakeUninstallCommand(param.first, param.second, &command_line); + EXPECT_EQ(param.first, command_line.GetProgram().value()); + if (param.second.empty()) { + EXPECT_EQ(0U, command_line.GetSwitchCount()); + } else { + EXPECT_EQ(2U, command_line.GetSwitchCount()); + EXPECT_TRUE(command_line.HasSwitch("do-something")); + EXPECT_TRUE(command_line.HasSwitch("silly")); + } + } +} diff --git a/chrome/installer/util/installation_state.cc b/chrome/installer/util/installation_state.cc index c0ab82d..f766195 100644 --- a/chrome/installer/util/installation_state.cc +++ b/chrome/installer/util/installation_state.cc @@ -9,33 +9,82 @@ #include "base/version.h" #include "base/win/registry.h" #include "chrome/installer/util/google_update_constants.h" -#include "chrome/installer/util/package_properties.h" +#include "chrome/installer/util/install_util.h" namespace installer { -ProductState::ProductState() { +ProductState::ProductState() + : uninstall_command_(CommandLine::NO_PROGRAM), + msi_(false), + multi_install_(false) { } -void ProductState::Initialize(bool system_install, - const std::wstring& version_key, - const std::wstring& state_key) { +bool ProductState::Initialize(bool system_install, + BrowserDistribution::Type type) { + return Initialize(system_install, + BrowserDistribution::GetSpecificDistribution(type)); +} + +bool ProductState::Initialize(bool system_install, + BrowserDistribution* distribution) { + const std::wstring version_key(distribution->GetVersionKey()); + const std::wstring state_key(distribution->GetStateKey()); const HKEY root_key = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; base::win::RegKey key(root_key, version_key.c_str(), KEY_QUERY_VALUE); std::wstring version_str; - if (key.ReadValue(google_update::kRegVersionField, &version_str) - == ERROR_SUCCESS) { + if (key.ReadValue(google_update::kRegVersionField, + &version_str) == ERROR_SUCCESS) { version_.reset(Version::GetVersionFromString(WideToASCII(version_str))); if (version_.get() != NULL) { - // The product is installed. Check for the channel value (absent if not - // installed/managed by Google Update). - if ((key.Open(root_key, state_key.c_str(), KEY_QUERY_VALUE) != - ERROR_SUCCESS) || !channel_.Initialize(key)) { - channel_.set_value(std::wstring()); + // The product is installed. + if (key.ReadValue(google_update::kRegOldVersionField, + &version_str) == ERROR_SUCCESS) { + old_version_.reset( + Version::GetVersionFromString(WideToASCII(version_str))); + } else { + old_version_.reset(); + } + if (key.ReadValue(google_update::kRegRenameCmdField, + &rename_cmd_) != ERROR_SUCCESS) + rename_cmd_.clear(); + // Read from the ClientState key. + channel_.set_value(std::wstring()); + uninstall_command_ = CommandLine(CommandLine::NO_PROGRAM); + msi_ = false; + multi_install_ = false; + if (key.Open(root_key, state_key.c_str(), + KEY_QUERY_VALUE) == ERROR_SUCCESS) { + std::wstring setup_path; + std::wstring uninstall_arguments; + // "ap" will be absent if not managed by Google Update. + channel_.Initialize(key); + // "UninstallString" will be absent for the multi-installer package. + key.ReadValue(kUninstallStringField, &setup_path); + // "UninstallArguments" will be absent for the multi-installer package. + key.ReadValue(kUninstallArgumentsField, &uninstall_arguments); + InstallUtil::MakeUninstallCommand(setup_path, uninstall_arguments, + &uninstall_command_); + // "msi" may be absent, 0 or 1 + DWORD dw_value = 0; + msi_ = (key.ReadValueDW(google_update::kRegMSIField, + &dw_value) == ERROR_SUCCESS) && (dw_value != 0); + // Multi-install is implied or is derived from the command-line. + if (distribution->GetType() == BrowserDistribution::CHROME_BINARIES) { + multi_install_ = true; + } else { + multi_install_ = uninstall_command_.HasSwitch( + switches::kMultiInstall); + } } } } else { version_.reset(); } + return version_.get() != NULL; +} + +FilePath ProductState::GetSetupPath() const { + return uninstall_command_.GetProgram(); } const Version& ProductState::version() const { @@ -43,12 +92,17 @@ const Version& ProductState::version() const { return *version_; } -void ProductState::CopyFrom(const ProductState& other) { +ProductState& ProductState::CopyFrom(const ProductState& other) { channel_.set_value(other.channel_.value()); - if (other.version_.get() == NULL) - version_.reset(); - else - version_.reset(other.version_->Clone()); + version_.reset(other.version_.get() == NULL ? NULL : other.version_->Clone()); + old_version_.reset( + other.old_version_.get() == NULL ? NULL : other.old_version_->Clone()); + rename_cmd_ = other.rename_cmd_; + uninstall_command_ = other.uninstall_command_; + msi_ = other.msi_; + multi_install_ = other.multi_install_; + + return *this; } InstallationState::InstallationState() { @@ -60,53 +114,31 @@ int InstallationState::IndexFromDistType(BrowserDistribution::Type type) { unexpected_chrome_browser_distribution_value_); COMPILE_ASSERT(BrowserDistribution::CHROME_FRAME == CHROME_FRAME_INDEX, unexpected_chrome_frame_distribution_value_); + COMPILE_ASSERT(BrowserDistribution::CHROME_BINARIES == CHROME_BINARIES_INDEX, + unexpected_chrome_frame_distribution_value_); DCHECK(type == BrowserDistribution::CHROME_BROWSER || - type == BrowserDistribution::CHROME_FRAME); + type == BrowserDistribution::CHROME_FRAME || + type == BrowserDistribution::CHROME_BINARIES); return type; } -void InstallationState::Initialize(const MasterPreferences& prefs) { +void InstallationState::Initialize() { BrowserDistribution* distribution; distribution = BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs); - InitializeProduct(false, distribution, &user_products_[CHROME_BROWSER_INDEX]); - InitializeProduct(true, distribution, - &system_products_[CHROME_BROWSER_INDEX]); + BrowserDistribution::CHROME_BROWSER); + user_products_[CHROME_BROWSER_INDEX].Initialize(false, distribution); + system_products_[CHROME_BROWSER_INDEX].Initialize(true, distribution); distribution = BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_FRAME, prefs); - InitializeProduct(false, distribution, &user_products_[CHROME_FRAME_INDEX]); - InitializeProduct(true, distribution, &system_products_[CHROME_FRAME_INDEX]); - - ActivePackageProperties package_properties; - InitializeMultiPackage(false, package_properties, - &user_products_[MULTI_PACKAGE_INDEX]); - InitializeMultiPackage(true, package_properties, - &system_products_[MULTI_PACKAGE_INDEX]); -} + BrowserDistribution::CHROME_FRAME); + user_products_[CHROME_FRAME_INDEX].Initialize(false, distribution); + system_products_[CHROME_FRAME_INDEX].Initialize(true, distribution); -// static -void InstallationState::InitializeProduct(bool system_install, - BrowserDistribution* distribution, - ProductState* product) { - product->Initialize(system_install, distribution->GetVersionKey(), - distribution->GetStateKey()); -} - -// static -void InstallationState::InitializeMultiPackage(bool system_install, - PackageProperties& properties, - ProductState* product) { - product->Initialize(system_install, properties.GetVersionKey(), - properties.GetStateKey()); -} - -const ProductState* InstallationState::GetMultiPackageState( - bool system_install) const { - const ProductState& product_state = - (system_install ? system_products_ : user_products_)[MULTI_PACKAGE_INDEX]; - return product_state.version_.get() == NULL ? NULL : &product_state; + distribution = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES); + user_products_[CHROME_BINARIES_INDEX].Initialize(false, distribution); + system_products_[CHROME_BINARIES_INDEX].Initialize(true, distribution); } const ProductState* InstallationState::GetProductState( diff --git a/chrome/installer/util/installation_state.h b/chrome/installer/util/installation_state.h index 3657c62..817e0af 100644 --- a/chrome/installer/util/installation_state.h +++ b/chrome/installer/util/installation_state.h @@ -1,4 +1,4 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. +// Copyright (c) 2011 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. @@ -9,6 +9,8 @@ #include <string> #include "base/basictypes.h" +#include "base/command_line.h" +#include "base/file_path.h" #include "base/scoped_ptr.h" #include "chrome/installer/util/browser_distribution.h" #include "chrome/installer/util/channel_info.h" @@ -19,44 +21,75 @@ namespace installer { class InstallationState; class MasterPreferences; -class PackageProperties; +// A representation of a product's state on the machine based on the contents +// of the Windows registry. +// TODO(grt): Pull this out into its own file. class ProductState { public: ProductState(); - void Initialize(bool system_install, - const std::wstring& version_key, - const std::wstring& state_key); + // Returns true if the product is installed (i.e., the product's Clients key + // exists and has a "pv" value); false otherwise. + bool Initialize(bool system_install, + BrowserDistribution::Type type); + bool Initialize(bool system_install, + BrowserDistribution* distribution); + + // Returns the product's channel info (i.e., the Google Update "ap" value). const ChannelInfo& channel() const { return channel_; } - ChannelInfo& channel() { return channel_; } + // Returns the path to the product's "setup.exe"; may be empty. + FilePath GetSetupPath() const; + + // Returns the product's version. This method may only be called on an + // instance that has been initialized for an installed product. const Version& version() const; - // Takes ownership of |version|. - void set_version(Version* version) { version_.reset(version); } - void CopyFrom(const ProductState& other); + // Returns the current version of the product if a new version is awaiting + // update; may be NULL. Ownership of a returned value is not passed to the + // caller. + const Version* old_version() const { return old_version_.get(); } - private: - friend class InstallationState; + // Returns the command to be used to update to the new version that is + // awaiting update; may be empty. + const std::wstring& rename_cmd() const { return rename_cmd_; } + + // True if the "msi" value in the ClientState key is present and non-zero. + bool is_msi() const { return msi_; } + + // The command to uninstall the product; may be empty. + const CommandLine& uninstall_command() const { return uninstall_command_; } + + // True if |uninstall_command| contains --multi-install. + bool is_multi_install() const { return multi_install_; } + + // Returns this object a la operator=(). + ProductState& CopyFrom(const ProductState& other); + protected: ChannelInfo channel_; scoped_ptr<Version> version_; + scoped_ptr<Version> old_version_; + std::wstring rename_cmd_; + CommandLine uninstall_command_; + bool msi_; + bool multi_install_; + + private: + friend class InstallationState; + DISALLOW_COPY_AND_ASSIGN(ProductState); }; // class ProductState // Encapsulates the state of all products on the system. +// TODO(grt): Rename this to MachineState and put it in its own file. class InstallationState { public: InstallationState(); - // Initializes |this| with the machine's current state. - void Initialize(const MasterPreferences& prefs); - - // Returns the state of the multi-installer package or NULL if no - // multi-install products are installed. - // Caller does NOT assume ownership of returned pointer. - const ProductState* GetMultiPackageState(bool system_install) const; + // Initializes this object with the machine's current state. + void Initialize(); // Returns the state of a product or NULL if not installed. // Caller does NOT assume ownership of returned pointer. @@ -67,17 +100,11 @@ class InstallationState { enum { CHROME_BROWSER_INDEX, CHROME_FRAME_INDEX, - MULTI_PACKAGE_INDEX, + CHROME_BINARIES_INDEX, NUM_PRODUCTS }; static int IndexFromDistType(BrowserDistribution::Type type); - static void InitializeProduct(bool system_install, - BrowserDistribution* distribution, - ProductState* product); - static void InitializeMultiPackage(bool system_install, - PackageProperties& properties, - ProductState* product); ProductState user_products_[NUM_PRODUCTS]; ProductState system_products_[NUM_PRODUCTS]; diff --git a/chrome/installer/util/installer_state.cc b/chrome/installer/util/installer_state.cc index 69d2841..9137b29 100644 --- a/chrome/installer/util/installer_state.cc +++ b/chrome/installer/util/installer_state.cc @@ -4,11 +4,23 @@ #include "chrome/installer/util/installer_state.h" +#include <algorithm> +#include <functional> +#include <utility> + +#include "base/command_line.h" +#include "base/file_util.h" #include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "chrome/installer/util/delete_tree_work_item.h" +#include "chrome/installer/util/helper.h" #include "chrome/installer/util/installation_state.h" #include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/master_preferences_constants.h" -#include "chrome/installer/util/package_properties.h" +#include "chrome/installer/util/product.h" +#include "chrome/installer/util/work_item.h" namespace installer { @@ -16,7 +28,8 @@ bool InstallerState::IsMultiInstallUpdate(const MasterPreferences& prefs, const InstallationState& machine_state) { // First, is the package present? const ProductState* package = - machine_state.GetMultiPackageState(system_install_); + machine_state.GetProductState(level_ == SYSTEM_LEVEL, + BrowserDistribution::CHROME_BINARIES); if (package == NULL) { // The multi-install package has not been installed, so it certainly isn't // being updated. @@ -33,7 +46,7 @@ bool InstallerState::IsMultiInstallUpdate(const MasterPreferences& prefs, for (const BrowserDistribution::Type* scan = &types[0], *end = &types[num_types]; scan != end; ++scan) { const ProductState* product = - machine_state.GetProductState(system_install_, *scan); + machine_state.GetProductState(level_ == SYSTEM_LEVEL, *scan); if (product == NULL) { VLOG(2) << "It seems that distribution type " << *scan << " is being installed for the first time."; @@ -51,42 +64,382 @@ bool InstallerState::IsMultiInstallUpdate(const MasterPreferences& prefs, return true; } -InstallerState::InstallerState() : operation_(UNINITIALIZED) { +InstallerState::InstallerState() + : operation_(UNINITIALIZED), + multi_package_distribution_(NULL), + level_(UNKNOWN_LEVEL), + package_type_(UNKNOWN_PACKAGE_TYPE), + root_key_(NULL), + msi_(false), + verbose_logging_(false) { +} + +InstallerState::InstallerState(Level level) + : operation_(UNINITIALIZED), + multi_package_distribution_(NULL), + level_(UNKNOWN_LEVEL), + package_type_(UNKNOWN_PACKAGE_TYPE), + root_key_(NULL), + msi_(false), + verbose_logging_(false) { + // Use set_level() so that root_key_ is updated properly. + set_level(level); } -void InstallerState::Initialize(const MasterPreferences& prefs, +void InstallerState::Initialize(const CommandLine& command_line, + const MasterPreferences& prefs, const InstallationState& machine_state) { - if (!prefs.GetBool(installer::master_preferences::kSystemLevel, - &system_install_)) - system_install_ = false; + bool pref_bool; + if (!prefs.GetBool(master_preferences::kSystemLevel, &pref_bool)) + pref_bool = false; + set_level(pref_bool ? SYSTEM_LEVEL : USER_LEVEL); + + if (!prefs.GetBool(master_preferences::kVerboseLogging, &verbose_logging_)) + verbose_logging_ = false; + + if (!prefs.GetBool(master_preferences::kMultiInstall, &pref_bool)) + pref_bool = false; + set_package_type(pref_bool ? MULTI_PACKAGE : SINGLE_PACKAGE); + + if (!prefs.GetBool(master_preferences::kMsi, &msi_)) + msi_ = false; + + const bool is_uninstall = command_line.HasSwitch(switches::kUninstall); + + if (prefs.install_chrome()) { + Product* p = + AddProductFromPreferences(BrowserDistribution::CHROME_BROWSER, prefs, + machine_state); + VLOG(1) << (is_uninstall ? "Uninstall" : "Install") + << " distribution: " << p->distribution()->GetApplicationName(); + } + if (prefs.install_chrome_frame()) { + Product* p = + AddProductFromPreferences(BrowserDistribution::CHROME_FRAME, prefs, + machine_state); + VLOG(1) << (is_uninstall ? "Uninstall" : "Install") + << " distribution: " << p->distribution()->GetApplicationName(); + } BrowserDistribution* operand = NULL; - if (!prefs.is_multi_install()) { + if (is_uninstall) { + operation_ = UNINSTALL; + } else if (!prefs.is_multi_install()) { // For a single-install, the current browser dist is the operand. operand = BrowserDistribution::GetDistribution(); operation_ = SINGLE_INSTALL_OR_UPDATE; } else if (IsMultiInstallUpdate(prefs, machine_state)) { // Updates driven by Google Update take place under the multi-installer's // app guid. - installer::ActivePackageProperties package_properties; + operand = multi_package_distribution_; operation_ = MULTI_UPDATE; - state_key_ = package_properties.GetStateKey(); } else { // Initial and over installs will always take place under one of the // product app guids. Chrome Frame's will be used if only Chrome Frame // is being installed. In all other cases, Chrome's is used. + operation_ = MULTI_INSTALL; + } + + if (operand == NULL) { operand = BrowserDistribution::GetSpecificDistribution( prefs.install_chrome() ? BrowserDistribution::CHROME_BROWSER : - BrowserDistribution::CHROME_FRAME, - prefs); - operation_ = MULTI_INSTALL; + BrowserDistribution::CHROME_FRAME); } - if (operand != NULL) { - state_key_ = operand->GetStateKey(); + state_key_ = operand->GetStateKey(); +} + +void InstallerState::set_level(Level level) { + level_ = level; + switch (level) { + case USER_LEVEL: + root_key_ = HKEY_CURRENT_USER; + break; + case SYSTEM_LEVEL: + root_key_ = HKEY_LOCAL_MACHINE; + break; + default: + DCHECK(level == UNKNOWN_LEVEL); + level_ = UNKNOWN_LEVEL; + root_key_ = NULL; + break; + } +} + +void InstallerState::set_package_type(PackageType type) { + package_type_ = type; + switch (type) { + case SINGLE_PACKAGE: + multi_package_distribution_ = NULL; + break; + case MULTI_PACKAGE: + multi_package_distribution_ = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES); + break; + default: + DCHECK(type == UNKNOWN_PACKAGE_TYPE); + package_type_ = UNKNOWN_PACKAGE_TYPE; + multi_package_distribution_ = NULL; + break; + } +} + +// Returns the Chrome binaries directory for multi-install or |dist|'s directory +// otherwise. +FilePath InstallerState::GetDefaultProductInstallPath( + BrowserDistribution* dist) const { + DCHECK(dist); + DCHECK(package_type_ != UNKNOWN_PACKAGE_TYPE); + + if (package_type_ == SINGLE_PACKAGE) { + return GetChromeInstallPath(system_install(), dist); + } else { + return GetChromeInstallPath(system_install(), + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES)); + } +} + +// Evaluates a product's eligibility for participation in this operation. +// We never expect these checks to fail, hence they all terminate the process in +// debug builds. See the log messages for details. +bool InstallerState::CanAddProduct(const Product& product, + const FilePath* product_dir) const { + switch (package_type_) { + case SINGLE_PACKAGE: + if (!products_.empty()) { + LOG(DFATAL) << "Cannot process more than one single-install product."; + return false; + } + break; + case MULTI_PACKAGE: + if (!product.HasOption(kOptionMultiInstall)) { + LOG(DFATAL) << "Cannot process a single-install product with a " + "multi-install state."; + return false; + } + if (FindProduct(product.distribution()->GetType()) != NULL) { + LOG(DFATAL) << "Cannot process more than one product of the same type."; + return false; + } + if (!target_path_.empty()) { + FilePath default_dir; + if (product_dir == NULL) + default_dir = GetDefaultProductInstallPath(product.distribution()); + if (!FilePath::CompareEqualIgnoreCase( + (product_dir == NULL ? default_dir : *product_dir).value(), + target_path_.value())) { + LOG(DFATAL) << "Cannot process products in different directories."; + return false; + } + } + break; + default: + DCHECK_EQ(UNKNOWN_PACKAGE_TYPE, package_type_); + break; + } + return true; +} + +// Adds |product|, installed in |product_dir| to this object's collection. If +// |product_dir| is NULL, the product's default install location is used. +// Returns NULL if |product| is incompatible with this object. Otherwise, +// returns a pointer to the product (ownership is held by this object). +Product* InstallerState::AddProductInDirectory(const FilePath* product_dir, + scoped_ptr<Product>* product) { + DCHECK(product != NULL); + DCHECK(product->get() != NULL); + const Product& the_product = *product->get(); + + if (!CanAddProduct(the_product, product_dir)) + return NULL; + + if (package_type_ == UNKNOWN_PACKAGE_TYPE) { + set_package_type(the_product.HasOption(kOptionMultiInstall) ? + MULTI_PACKAGE : SINGLE_PACKAGE); + } + + if (target_path_.empty()) { + if (product_dir == NULL) + target_path_ = GetDefaultProductInstallPath(the_product.distribution()); + else + target_path_ = *product_dir; + } + + if (state_key_.empty()) + state_key_ = the_product.distribution()->GetStateKey(); + + products_.push_back(product->release()); + return products_[products_->size() - 1]; +} + +Product* InstallerState::AddProduct(scoped_ptr<Product>* product) { + return AddProductInDirectory(NULL, product); +} + +// Adds a product of type |distribution_type| constructed on the basis of +// |prefs|, setting this object's msi flag if the product is represented in +// |machine_state| and is msi-installed. Returns the product that was added, +// or NULL if |state| is incompatible with this object. Ownership is not passed +// to the caller. +Product* InstallerState::AddProductFromPreferences( + BrowserDistribution::Type distribution_type, + const MasterPreferences& prefs, + const InstallationState& machine_state) { + scoped_ptr<Product> product_ptr( + new Product(BrowserDistribution::GetSpecificDistribution( + distribution_type))); + product_ptr->InitializeFromPreferences(prefs); + + Product* product = AddProductInDirectory(NULL, &product_ptr); + + if (product != NULL && !msi_) { + const ProductState* product_state = machine_state.GetProductState( + system_install(), distribution_type); + if (product_state != NULL) + msi_ = product_state->is_msi(); + } + + return product; +} + +Product* InstallerState::AddProductFromState( + BrowserDistribution::Type type, + const ProductState& state) { + scoped_ptr<Product> product_ptr( + new Product(BrowserDistribution::GetSpecificDistribution(type))); + product_ptr->InitializeFromUninstallCommand(state.uninstall_command()); + + // Strip off <version>/Installer/setup.exe; see GetInstallerDirectory(). + FilePath product_dir = state.GetSetupPath().DirName().DirName().DirName(); + + Product* product = AddProductInDirectory(&product_dir, &product_ptr); + + if (product != NULL) + msi_ |= state.is_msi(); + + return product; +} + +bool InstallerState::system_install() const { + DCHECK(level_ == USER_LEVEL || level_ == SYSTEM_LEVEL); + return level_ == SYSTEM_LEVEL; +} + +bool InstallerState::is_multi_install() const { + DCHECK(package_type_ == SINGLE_PACKAGE || package_type_ == MULTI_PACKAGE); + return package_type_ != SINGLE_PACKAGE; +} + +bool InstallerState::RemoveProduct(const Product* product) { + ScopedVector<Product>::iterator it = + std::find(products_.begin(), products_.end(), product); + if (it != products_.end()) { + products_->erase(it); + return true; + } + return false; +} + +const Product* InstallerState::FindProduct( + BrowserDistribution::Type distribution_type) const { + for (Products::const_iterator scan = products_.begin(), end = products_.end(); + scan != end; ++scan) { + if ((*scan)->is_type(distribution_type)) + return *scan; + } + return NULL; +} + +Version* InstallerState::GetCurrentVersion( + const InstallationState& machine_state) const { + DCHECK(!products_.empty()); + scoped_ptr<Version> current_version; + const BrowserDistribution::Type prod_type = (package_type_ == MULTI_PACKAGE) ? + BrowserDistribution::CHROME_BINARIES : + products_[0]->distribution()->GetType(); + const ProductState* product_state = + machine_state.GetProductState(level_ == SYSTEM_LEVEL, prod_type); + + if (product_state != NULL) { + const Version* version = NULL; + + // Be aware that there might be a pending "new_chrome.exe" already in the + // installation path. If so, we use old_version, which holds the version of + // "chrome.exe" itself. + if (file_util::PathExists(target_path().Append(kChromeNewExe))) + version = product_state->old_version(); + + if (version == NULL) + version = &product_state->version(); + + current_version.reset(version->Clone()); + } + + return current_version.release(); +} + +FilePath InstallerState::GetInstallerDirectory(const Version& version) const { + return target_path().Append(ASCIIToWide(version.GetString())) + .Append(kInstallerDir); +} + +void InstallerState::RemoveOldVersionDirectories( + const Version& latest_version) const { + file_util::FileEnumerator version_enum(target_path(), false, + file_util::FileEnumerator::DIRECTORIES); + scoped_ptr<Version> version; + std::vector<FilePath> key_files; + + // We try to delete all directories whose versions are lower than + // latest_version. + FilePath next_version = version_enum.Next(); + while (!next_version.empty()) { + file_util::FileEnumerator::FindInfo find_data = {0}; + version_enum.GetFindInfo(&find_data); + VLOG(1) << "directory found: " << find_data.cFileName; + version.reset(Version::GetVersionFromString( + WideToASCII(find_data.cFileName))); + if (version.get() && latest_version.CompareTo(*version) > 0) { + key_files.clear(); + std::for_each(products_.begin(), products_.end(), + std::bind2nd(std::mem_fun(&Product::AddKeyFiles), + &key_files)); + const std::vector<FilePath>::iterator end = key_files.end(); + for (std::vector<FilePath>::iterator scan = key_files.begin(); + scan != end; ++scan) { + *scan = next_version.Append(*scan); + } + + VLOG(1) << "Deleting directory: " << next_version.value(); + + scoped_ptr<WorkItem> item( + WorkItem::CreateDeleteTreeWorkItem(next_version, key_files)); + if (!item->Do()) + item->Rollback(); + } + + next_version = version_enum.Next(); + } +} + +void InstallerState::AddComDllList(std::vector<FilePath>* com_dll_list) const { + std::for_each(products_.begin(), products_.end(), + std::bind2nd(std::mem_fun(&Product::AddComDllList), + com_dll_list)); +} + +bool InstallerState::SetChannelFlags(bool set, + ChannelInfo* channel_info) const { + bool modified = false; + for (Products::const_iterator scan = products_.begin(), end = products_.end(); + scan != end; ++scan) { + modified |= (*scan)->SetChannelFlags(set, channel_info); } + return modified; } } // namespace installer diff --git a/chrome/installer/util/installer_state.h b/chrome/installer/util/installer_state.h index 0bc2a5f..0b4ea0b 100644 --- a/chrome/installer/util/installer_state.h +++ b/chrome/installer/util/installer_state.h @@ -7,47 +7,183 @@ #pragma once #include <string> +#include <vector> #include "base/basictypes.h" +#include "base/file_path.h" +#include "base/logging.h" +#include "base/scoped_ptr.h" +#include "base/scoped_vector.h" #include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/product.h" + +#if defined(OS_WIN) +#include <windows.h> // NOLINT +#endif + +class CommandLine; +class Version; namespace installer { +class ChannelInfo; class InstallationState; class MasterPreferences; +class ProductState; + +typedef std::vector<Product*> Products; + // Encapsulates the state of the current installation operation. Only valid -// for installs and upgrades (not for uninstalls or non-install commands) +// for installs and upgrades (not for uninstalls or non-install commands). +// TODO(grt): Rename to InstallerEngine/Conductor or somesuch? class InstallerState { public: + enum Level { + UNKNOWN_LEVEL, + USER_LEVEL, + SYSTEM_LEVEL + }; + + enum PackageType { + UNKNOWN_PACKAGE_TYPE, + SINGLE_PACKAGE, + MULTI_PACKAGE + }; + enum Operation { UNINITIALIZED, SINGLE_INSTALL_OR_UPDATE, MULTI_INSTALL, MULTI_UPDATE, + UNINSTALL }; + // Constructs an uninitialized instance; see Initialize(). InstallerState(); - // Initializes |this| based on the current operation. - void Initialize(const MasterPreferences& prefs, + // Constructs an initialized but empty instance. + explicit InstallerState(Level level); + + // Initializes this object based on the current operation. + void Initialize(const CommandLine& command_line, + const MasterPreferences& prefs, const InstallationState& machine_state); - // true if system-level, false if user-level. - bool system_install() const { return system_install_; } + // Adds a product constructed on the basis of |state|, setting this object's + // msi flag if |state| is msi-installed. Returns the product that was added, + // or NULL if |state| is incompatible with this object. Ownership is not + // passed to the caller. + Product* AddProductFromState(BrowserDistribution::Type type, + const ProductState& state); + + // Returns the product that was added, or NULL if |product| is incompatible + // with this object. Ownership of |product| is taken by this object, while + // ownership of the return value is not passed to the caller. + Product* AddProduct(scoped_ptr<Product>* product); + + // Removes |product| from the set of products to be operated on. The object + // pointed to by |product| is freed. Returns false if |product| is not + // present in the set. + bool RemoveProduct(const Product* product); + + // The level (user or system) of this operation. + Level level() const { return level_; } + // The package type (single or multi) of this operation. + PackageType package_type() const { return package_type_; } + + // An identifier of this operation. Operation operation() const { return operation_; } + // A convenience method returning level() == SYSTEM_LEVEL. + // TODO(grt): Eradicate the bool in favor of the enum. + bool system_install() const; + + // A convenience method returning package_type() == MULTI_PACKAGE. + // TODO(grt): Eradicate the bool in favor of the enum. + bool is_multi_install() const; + + // The full path to the place where the operand resides. + const FilePath& target_path() const { return target_path_; } + + // True if the "msi" preference is set or if a product with the "msi" state + // flag is set is to be operated on. + bool is_msi() const { return msi_; } + + // True if the --verbose-logging command-line flag is set or if the + // verbose_logging master preferences option is true. + bool verbose_logging() const { return verbose_logging_; } + +#if defined(OS_WIN) + HKEY root_key() const { return root_key_; } +#endif + // The ClientState key by which we interact with Google Update. const std::wstring& state_key() const { return state_key_; } + // Returns the BrowserDistribution instance corresponding to the binaries for + // this run if we're operating on a multi-package product. + BrowserDistribution* multi_package_binaries_distribution() const { + DCHECK(package_type_ == MULTI_PACKAGE); + DCHECK(multi_package_distribution_ != NULL); + return multi_package_distribution_; + } + + const Products& products() const { return products_.get(); } + + // Returns the product of the desired type, or NULL if none found. + const Product* FindProduct(BrowserDistribution::Type distribution_type) const; + + // Returns the currently installed version in |target_path|, or NULL if no + // products are installed. Ownership is passed to the caller. + Version* GetCurrentVersion(const InstallationState& machine_state) const; + + // Returns the path to the installer under Chrome version folder + // (for example <target_path>\Google\Chrome\Application\<Version>\Installer) + FilePath GetInstallerDirectory(const Version& version) const; + + // Try to delete all directories whose versions are lower than + // |latest_version|. + void RemoveOldVersionDirectories(const Version& latest_version) const; + + // Adds to |com_dll_list| the list of COM DLLs that are to be registered + // and/or unregistered. The list may be empty. + void AddComDllList(std::vector<FilePath>* com_dll_list) const; + + bool SetChannelFlags(bool set, ChannelInfo* channel_info) const; + protected: + FilePath GetDefaultProductInstallPath(BrowserDistribution* dist) const; + bool CanAddProduct(const Product& product, const FilePath* product_dir) const; + Product* AddProductInDirectory(const FilePath* product_dir, + scoped_ptr<Product>* product); + Product* AddProductFromPreferences( + BrowserDistribution::Type distribution_type, + const MasterPreferences& prefs, + const InstallationState& machine_state); bool IsMultiInstallUpdate(const MasterPreferences& prefs, const InstallationState& machine_state); + // Sets this object's level and updates the root_key_ accordingly. + void set_level(Level level); + + // Sets this object's package type and updates the multi_package_distribution_ + // accordingly. + void set_package_type(PackageType type); + Operation operation_; + FilePath target_path_; std::wstring state_key_; - bool system_install_; + ScopedVector<Product> products_; + BrowserDistribution* multi_package_distribution_; + Level level_; + PackageType package_type_; +#if defined(OS_WIN) + HKEY root_key_; +#endif + bool msi_; + bool verbose_logging_; private: DISALLOW_COPY_AND_ASSIGN(InstallerState); diff --git a/chrome/installer/util/installer_state_unittest.cc b/chrome/installer/util/installer_state_unittest.cc new file mode 100644 index 0000000..25efc07 --- /dev/null +++ b/chrome/installer/util/installer_state_unittest.cc @@ -0,0 +1,327 @@ +// Copyright (c) 2011 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 <windows.h> + +#include <fstream> + +#include "base/base_paths.h" +#include "base/command_line.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/process_util.h" +#include "base/string_util.h" +#include "base/utf_string_conversions.h" +#include "base/version.h" +#include "base/win/registry.h" +#include "base/win/scoped_handle.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/helper.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/product_unittest.h" +#include "chrome/installer/util/work_item.h" +#include "testing/gtest/include/gtest/gtest.h" + +class InstallerStateTest : public TestWithTempDirAndDeleteTempOverrideKeys { + protected: +}; + +// An installer state on which we can tweak the target path. +class MockInstallerState : public installer::InstallerState { + public: + MockInstallerState() : InstallerState() { } + void set_target_path(const FilePath& target_path) { + target_path_ = target_path; + } +}; + +// Simple function to dump some text into a new file. +void CreateTextFile(const std::wstring& filename, + const std::wstring& contents) { + std::ofstream file; + file.open(filename.c_str()); + ASSERT_TRUE(file.is_open()); + file << contents; + file.close(); +} + +void BuildSingleChromeState(const FilePath& target_dir, + MockInstallerState* installer_state) { + CommandLine cmd_line = CommandLine::FromString(L"setup.exe"); + installer::MasterPreferences prefs(cmd_line); + installer::InstallationState machine_state; + machine_state.Initialize(); + installer_state->Initialize(cmd_line, prefs, machine_state); + installer_state->set_target_path(target_dir); + EXPECT_TRUE(installer_state->FindProduct(BrowserDistribution::CHROME_BROWSER) + != NULL); + EXPECT_TRUE(installer_state->FindProduct(BrowserDistribution::CHROME_FRAME) + == NULL); +} + +wchar_t text_content_1[] = L"delete me"; +wchar_t text_content_2[] = L"delete me as well"; + +// Delete version directories. Everything lower than the given version +// should be deleted. +TEST_F(InstallerStateTest, Delete) { + // TODO(grt): move common stuff into the test fixture. + // Create a Chrome dir + FilePath chrome_dir(test_dir_.path()); + chrome_dir = chrome_dir.AppendASCII("chrome"); + file_util::CreateDirectory(chrome_dir); + ASSERT_TRUE(file_util::PathExists(chrome_dir)); + + FilePath chrome_dir_1(chrome_dir); + chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0"); + file_util::CreateDirectory(chrome_dir_1); + ASSERT_TRUE(file_util::PathExists(chrome_dir_1)); + + FilePath chrome_dir_2(chrome_dir); + chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0"); + file_util::CreateDirectory(chrome_dir_2); + ASSERT_TRUE(file_util::PathExists(chrome_dir_2)); + + FilePath chrome_dir_3(chrome_dir); + chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0"); + file_util::CreateDirectory(chrome_dir_3); + ASSERT_TRUE(file_util::PathExists(chrome_dir_3)); + + FilePath chrome_dir_4(chrome_dir); + chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0"); + file_util::CreateDirectory(chrome_dir_4); + ASSERT_TRUE(file_util::PathExists(chrome_dir_4)); + + FilePath chrome_dll_1(chrome_dir_1); + chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll"); + CreateTextFile(chrome_dll_1.value(), text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_1)); + + FilePath chrome_dll_2(chrome_dir_2); + chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll"); + CreateTextFile(chrome_dll_2.value(), text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_2)); + + FilePath chrome_dll_3(chrome_dir_3); + chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll"); + CreateTextFile(chrome_dll_3.value(), text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_3)); + + FilePath chrome_dll_4(chrome_dir_4); + chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll"); + CreateTextFile(chrome_dll_4.value(), text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_4)); + + MockInstallerState installer_state; + BuildSingleChromeState(chrome_dir, &installer_state); + scoped_ptr<Version> latest_version(Version::GetVersionFromString("1.0.4.0")); + installer_state.RemoveOldVersionDirectories(*latest_version.get()); + + // old versions should be gone + EXPECT_FALSE(file_util::PathExists(chrome_dir_1)); + EXPECT_FALSE(file_util::PathExists(chrome_dir_2)); + EXPECT_FALSE(file_util::PathExists(chrome_dir_3)); + // the latest version should stay + EXPECT_TRUE(file_util::PathExists(chrome_dll_4)); +} + +// Delete older version directories, keeping the one in used intact. +TEST_F(InstallerStateTest, DeleteInUsed) { + // Create a Chrome dir + FilePath chrome_dir(test_dir_.path()); + chrome_dir = chrome_dir.AppendASCII("chrome"); + file_util::CreateDirectory(chrome_dir); + ASSERT_TRUE(file_util::PathExists(chrome_dir)); + + FilePath chrome_dir_1(chrome_dir); + chrome_dir_1 = chrome_dir_1.AppendASCII("1.0.1.0"); + file_util::CreateDirectory(chrome_dir_1); + ASSERT_TRUE(file_util::PathExists(chrome_dir_1)); + + FilePath chrome_dir_2(chrome_dir); + chrome_dir_2 = chrome_dir_2.AppendASCII("1.0.2.0"); + file_util::CreateDirectory(chrome_dir_2); + ASSERT_TRUE(file_util::PathExists(chrome_dir_2)); + + FilePath chrome_dir_3(chrome_dir); + chrome_dir_3 = chrome_dir_3.AppendASCII("1.0.3.0"); + file_util::CreateDirectory(chrome_dir_3); + ASSERT_TRUE(file_util::PathExists(chrome_dir_3)); + + FilePath chrome_dir_4(chrome_dir); + chrome_dir_4 = chrome_dir_4.AppendASCII("1.0.4.0"); + file_util::CreateDirectory(chrome_dir_4); + ASSERT_TRUE(file_util::PathExists(chrome_dir_4)); + + FilePath chrome_dll_1(chrome_dir_1); + chrome_dll_1 = chrome_dll_1.AppendASCII("chrome.dll"); + CreateTextFile(chrome_dll_1.value(), text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_1)); + + FilePath chrome_dll_2(chrome_dir_2); + chrome_dll_2 = chrome_dll_2.AppendASCII("chrome.dll"); + CreateTextFile(chrome_dll_2.value(), text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_2)); + + // Open the file to make it in use. + std::ofstream file; + file.open(chrome_dll_2.value().c_str()); + + FilePath chrome_othera_2(chrome_dir_2); + chrome_othera_2 = chrome_othera_2.AppendASCII("othera.dll"); + CreateTextFile(chrome_othera_2.value(), text_content_2); + ASSERT_TRUE(file_util::PathExists(chrome_othera_2)); + + FilePath chrome_otherb_2(chrome_dir_2); + chrome_otherb_2 = chrome_otherb_2.AppendASCII("otherb.dll"); + CreateTextFile(chrome_otherb_2.value(), text_content_2); + ASSERT_TRUE(file_util::PathExists(chrome_otherb_2)); + + FilePath chrome_dll_3(chrome_dir_3); + chrome_dll_3 = chrome_dll_3.AppendASCII("chrome.dll"); + CreateTextFile(chrome_dll_3.value(), text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_3)); + + FilePath chrome_dll_4(chrome_dir_4); + chrome_dll_4 = chrome_dll_4.AppendASCII("chrome.dll"); + CreateTextFile(chrome_dll_4.value(), text_content_1); + ASSERT_TRUE(file_util::PathExists(chrome_dll_4)); + + MockInstallerState installer_state; + BuildSingleChromeState(chrome_dir, &installer_state); + scoped_ptr<Version> latest_version(Version::GetVersionFromString("1.0.4.0")); + installer_state.RemoveOldVersionDirectories(*latest_version.get()); + + // old versions not in used should be gone + EXPECT_FALSE(file_util::PathExists(chrome_dir_1)); + EXPECT_FALSE(file_util::PathExists(chrome_dir_3)); + // every thing under in used version should stay + EXPECT_TRUE(file_util::PathExists(chrome_dir_2)); + EXPECT_TRUE(file_util::PathExists(chrome_dll_2)); + EXPECT_TRUE(file_util::PathExists(chrome_othera_2)); + EXPECT_TRUE(file_util::PathExists(chrome_otherb_2)); + // the latest version should stay + EXPECT_TRUE(file_util::PathExists(chrome_dll_4)); +} + +// Tests a few basic things of the Package class. Makes sure that the path +// operations are correct +TEST_F(InstallerStateTest, Basic) { + const bool multi_install = false; + const bool system_level = true; + CommandLine cmd_line = CommandLine::FromString( + std::wstring(L"setup.exe") + + (multi_install ? L" --multi-install --chrome" : L"") + + (system_level ? L" --system-level" : L"")); + installer::MasterPreferences prefs(cmd_line); + installer::InstallationState machine_state; + machine_state.Initialize(); + MockInstallerState installer_state; + installer_state.Initialize(cmd_line, prefs, machine_state); + installer_state.set_target_path(test_dir_.path()); + EXPECT_EQ(test_dir_.path().value(), installer_state.target_path().value()); + EXPECT_EQ(1U, installer_state.products().size()); + + const char kOldVersion[] = "1.2.3.4"; + const char kNewVersion[] = "2.3.4.5"; + + scoped_ptr<Version> new_version(Version::GetVersionFromString(kNewVersion)); + scoped_ptr<Version> old_version(Version::GetVersionFromString(kOldVersion)); + ASSERT_TRUE(new_version.get() != NULL); + ASSERT_TRUE(old_version.get() != NULL); + + FilePath installer_dir( + installer_state.GetInstallerDirectory(*new_version.get())); + EXPECT_FALSE(installer_dir.empty()); + + FilePath new_version_dir(installer_state.target_path().Append( + UTF8ToWide(new_version->GetString()))); + FilePath old_version_dir(installer_state.target_path().Append( + UTF8ToWide(old_version->GetString()))); + + EXPECT_FALSE(file_util::PathExists(new_version_dir)); + EXPECT_FALSE(file_util::PathExists(old_version_dir)); + + EXPECT_FALSE(file_util::PathExists(installer_dir)); + file_util::CreateDirectory(installer_dir); + EXPECT_TRUE(file_util::PathExists(new_version_dir)); + + file_util::CreateDirectory(old_version_dir); + EXPECT_TRUE(file_util::PathExists(old_version_dir)); + + // Create a fake chrome.dll key file in the old version directory. This + // should prevent the old version directory from getting deleted. + FilePath old_chrome_dll(old_version_dir.Append(installer::kChromeDll)); + EXPECT_FALSE(file_util::PathExists(old_chrome_dll)); + + // Hold on to the file exclusively to prevent the directory from + // being deleted. + base::win::ScopedHandle file( + ::CreateFile(old_chrome_dll.value().c_str(), GENERIC_READ, + 0, NULL, OPEN_ALWAYS, 0, NULL)); + EXPECT_TRUE(file.IsValid()); + EXPECT_TRUE(file_util::PathExists(old_chrome_dll)); + + installer_state.RemoveOldVersionDirectories(*new_version.get()); + // The old directory should still exist. + EXPECT_TRUE(file_util::PathExists(old_version_dir)); + EXPECT_TRUE(file_util::PathExists(new_version_dir)); + + // Now close the file handle to make it possible to delete our key file. + file.Close(); + + installer_state.RemoveOldVersionDirectories(*new_version.get()); + // The new directory should still exist. + EXPECT_TRUE(file_util::PathExists(new_version_dir)); + + // Now, the old directory and key file should be gone. + EXPECT_FALSE(file_util::PathExists(old_chrome_dll)); + EXPECT_FALSE(file_util::PathExists(old_version_dir)); +} + +TEST_F(InstallerStateTest, WithProduct) { + const bool multi_install = false; + const bool system_level = true; + CommandLine cmd_line = CommandLine::FromString( + std::wstring(L"setup.exe") + + (multi_install ? L" --multi-install --chrome" : L"") + + (system_level ? L" --system-level" : L"")); + installer::MasterPreferences prefs(cmd_line); + installer::InstallationState machine_state; + machine_state.Initialize(); + MockInstallerState installer_state; + installer_state.Initialize(cmd_line, prefs, machine_state); + installer_state.set_target_path(test_dir_.path()); + EXPECT_EQ(1U, installer_state.products().size()); + EXPECT_EQ(system_level, installer_state.system_install()); + + const char kCurrentVersion[] = "1.2.3.4"; + scoped_ptr<Version> current_version( + Version::GetVersionFromString(kCurrentVersion)); + + HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + EXPECT_EQ(root, installer_state.root_key()); + { + TempRegKeyOverride override(root, L"root_pit"); + BrowserDistribution* dist = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER); + base::win::RegKey chrome_key(root, dist->GetVersionKey().c_str(), + KEY_ALL_ACCESS); + EXPECT_TRUE(chrome_key.Valid()); + if (chrome_key.Valid()) { + chrome_key.WriteValue(google_update::kRegVersionField, + UTF8ToWide(current_version->GetString()).c_str()); + machine_state.Initialize(); + // TODO(tommi): Also test for when there exists a new_chrome.exe. + scoped_ptr<Version> found_version(installer_state.GetCurrentVersion( + machine_state)); + EXPECT_TRUE(found_version.get() != NULL); + if (found_version.get()) { + EXPECT_TRUE(current_version->Equals(*found_version)); + } + } + } +} diff --git a/chrome/installer/util/package.cc b/chrome/installer/util/package.cc deleted file mode 100644 index 3122cef..0000000 --- a/chrome/installer/util/package.cc +++ /dev/null @@ -1,188 +0,0 @@ -// Copyright (c) 2010 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/util/package.h" - -#include "base/file_util.h" -#include "base/logging.h" -#include "base/string_util.h" -#include "base/utf_string_conversions.h" -#include "base/win/registry.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/master_preferences.h" -#include "chrome/installer/util/package_properties.h" -#include "chrome/installer/util/product.h" -#include "chrome/installer/util/util_constants.h" -#include "chrome/installer/util/work_item_list.h" - -using base::win::RegKey; -using installer::MasterPreferences; - -namespace installer { - -Package::Package(bool multi_install, bool system_level, const FilePath& path, - PackageProperties* properties) - : multi_install_(multi_install), - system_level_(system_level), - path_(path), - properties_(properties) { - DCHECK(properties_); -} - -Package::~Package() { -} - -const FilePath& Package::path() const { - return path_; -} - -const Products& Package::products() const { - return products_; -} - -PackageProperties* Package::properties() const { - return properties_; -} - -bool Package::IsEqual(const FilePath& path) const { - return FilePath::CompareEqualIgnoreCase(path_.value(), path.value()); -} - -void Package::AssociateProduct(const Product* product) { -#ifndef NDEBUG - for (size_t i = 0; i < products_.size(); ++i) { - DCHECK_NE(product->distribution()->GetType(), - products_[i]->distribution()->GetType()); - } -#endif - products_.push_back(product); -} - -bool Package::multi_install() const { - return multi_install_; -} - -bool Package::system_level() const { - return system_level_; -} - -FilePath Package::GetInstallerDirectory( - const Version& version) const { - return path_.Append(UTF8ToWide(version.GetString())) - .Append(installer::kInstallerDir); -} - -Version* Package::GetCurrentVersion() const { - scoped_ptr<Version> current_version; - // Be aware that there might be a pending "new_chrome.exe" already in the - // installation path. - FilePath new_chrome_exe(path_.Append(installer::kChromeNewExe)); - bool new_chrome_exists = file_util::PathExists(new_chrome_exe); - - HKEY root = system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - for (size_t i = 0; i < products_.size(); ++i) { - const Product* product = products_[i]; - RegKey chrome_key(root, product->distribution()->GetVersionKey().c_str(), - KEY_READ); - std::wstring version; - if (new_chrome_exists) - chrome_key.ReadValue(google_update::kRegOldVersionField, &version); - - if (version.empty()) - chrome_key.ReadValue(google_update::kRegVersionField, &version); - - if (!version.empty()) { - scoped_ptr<Version> this_version(Version::GetVersionFromString( - WideToASCII(version))); - if (this_version.get()) { - if (!current_version.get() || - (current_version->CompareTo(*this_version) > 0)) { - current_version.reset(this_version.release()); - } else if (current_version.get()) { - DCHECK_EQ(current_version->GetString(), this_version->GetString()) - << "found distributions of different versions in the same " - "installation folder!"; - } - } - } - } - - return current_version.release(); -} - -void Package::RemoveOldVersionDirectories( - const Version& latest_version) const { - file_util::FileEnumerator version_enum(path_, false, - file_util::FileEnumerator::DIRECTORIES); - scoped_ptr<Version> version; - - // We try to delete all directories whose versions are lower than - // latest_version. - FilePath next_version = version_enum.Next(); - while (!next_version.empty()) { - file_util::FileEnumerator::FindInfo find_data = {0}; - version_enum.GetFindInfo(&find_data); - VLOG(1) << "directory found: " << find_data.cFileName; - version.reset(Version::GetVersionFromString( - WideToASCII(find_data.cFileName))); - if (version.get() && (latest_version.CompareTo(*version) > 0)) { - std::vector<FilePath> key_files; - for (Products::const_iterator it = products_.begin(); - it != products_.end(); ++it) { - BrowserDistribution* dist = it->get()->distribution(); - std::vector<FilePath> dist_key_files(dist->GetKeyFiles()); - std::vector<FilePath>::const_iterator key_file_iter( - dist_key_files.begin()); - for (; key_file_iter != dist_key_files.end(); ++key_file_iter) { - key_files.push_back(next_version.Append(*key_file_iter)); - } - } - - VLOG(1) << "Deleting directory: " << next_version.value(); - - scoped_ptr<DeleteTreeWorkItem> item( - WorkItem::CreateDeleteTreeWorkItem(next_version, key_files)); - if (!item->Do()) - item->Rollback(); - } - - next_version = version_enum.Next(); - } -} - -size_t Package::GetMultiInstallDependencyCount() const { - BrowserDistribution::Type product_types[] = { - BrowserDistribution::CHROME_BROWSER, - BrowserDistribution::CHROME_FRAME, - }; - - const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); - HKEY root_key = system_level_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - - size_t ret = 0; - - for (int i = 0; i < arraysize(product_types); ++i) { - BrowserDistribution* dist = - BrowserDistribution::GetSpecificDistribution(product_types[i], prefs); - // First see if the product is installed by checking its version key. - // If the key doesn't exist, the product isn't installed. - RegKey version_key(root_key, dist->GetVersionKey().c_str(), KEY_READ); - if (!version_key.Valid()) { - VLOG(1) << "Product not installed: " << dist->GetApplicationName(); - } else { - if (installer::IsInstalledAsMulti(system_level_, dist)) { - VLOG(1) << "Product dependency: " << dist->GetApplicationName(); - ++ret; - } - } - } - - return ret; -} - -} // namespace installer - diff --git a/chrome/installer/util/package.h b/chrome/installer/util/package.h deleted file mode 100644 index d523fc2..0000000 --- a/chrome/installer/util/package.h +++ /dev/null @@ -1,90 +0,0 @@ -// Copyright (c) 2010 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_UTIL_PACKAGE_H_ -#define CHROME_INSTALLER_UTIL_PACKAGE_H_ -#pragma once - -#include <vector> - -#include "base/file_path.h" -#include "base/ref_counted.h" - -class CommandLine; -class Version; - -namespace installer { - -enum InstallStatus; -class Product; -class PackageProperties; - -typedef std::vector<scoped_refptr<const Product> > Products; - -// Represents a physical installation. An instance of this class is associated -// with one or more Product instances. Product objects can share a Package but -// not vice versa. -class Package : public base::RefCounted<Package> { - public: - Package(bool multi_install, bool system_level, const FilePath& path, - PackageProperties* properties); - - // Returns the path of the installation folder. - const FilePath& path() const; - - const Products& products() const; - - PackageProperties* properties() const; - - bool multi_install() const; - - bool system_level() const; - - bool IsEqual(const FilePath& path) const; - - void AssociateProduct(const Product* product); - - // Get path to the installer under Chrome version folder - // (for example <path>\Google\Chrome\Application\<Version>\Installer) - FilePath GetInstallerDirectory(const Version& version) const; - - // Figure out the oldest currently installed version for this package - // Returns NULL if none is found. Caller is responsible for freeing - // the returned Version object if valid. - // The function DCHECKs if it finds that not all products in this - // folder have the same current version. - Version* GetCurrentVersion() const; - - // Tries to remove all previous version directories (after a new Chrome - // update). If an instance of Chrome with older version is still running - // on the system, its corresponding version directory will be left intact. - // (The version directory is subject for removal again during next update.) - // - // latest_version: the latest version of Chrome installed. - void RemoveOldVersionDirectories(const Version& latest_version) const; - - // Returns how many installed products depend on the binaries currently - // in the installation path. - // Note: The function counts only products that are installed as part of - // a multi install installation and only products that have the same - // system_level() value. - size_t GetMultiInstallDependencyCount() const; - - protected: - bool multi_install_; - bool system_level_; - FilePath path_; - Products products_; - PackageProperties* properties_; // Weak reference. - - private: - friend class base::RefCounted<Package>; - ~Package(); - - DISALLOW_COPY_AND_ASSIGN(Package); -}; - -} // namespace installer - -#endif // CHROME_INSTALLER_UTIL_PACKAGE_H_ diff --git a/chrome/installer/util/package_properties.cc b/chrome/installer/util/package_properties.cc deleted file mode 100644 index e33c2f9..0000000 --- a/chrome/installer/util/package_properties.cc +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright (c) 2011 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/util/package_properties.h" - -#include "base/basictypes.h" -#include "base/string_util.h" -#include "chrome/installer/util/browser_distribution.h" -#include "chrome/installer/util/google_update_constants.h" -#include "chrome/installer/util/google_update_settings.h" -#include "chrome/installer/util/install_util.h" -#include "chrome/installer/util/util_constants.h" - -namespace { - -const wchar_t kChromePackageGuid[] = - L"{4DC8B4CA-1BDA-483e-B5FA-D3C12E15B62D}"; - -std::wstring GetKeyForGuid(const wchar_t* base_key, const wchar_t* guid) { - std::wstring key(base_key); - key.append(L"\\"); - key.append(guid); - return key; -} - -} // end namespace - -namespace installer { - -const char PackageProperties::kPackageProductName[] = "Chrome binaries"; - -PackagePropertiesImpl::PackagePropertiesImpl( - const wchar_t* guid, - const std::wstring& state_key, - const std::wstring& state_medium_key, - const std::wstring& version_key) - : guid_(guid), state_key_(state_key), state_medium_key_(state_medium_key), - version_key_(version_key) { -} - -PackagePropertiesImpl::~PackagePropertiesImpl() { -} - -const std::wstring& PackagePropertiesImpl::GetAppGuid() { - return guid_; -} - -const std::wstring& PackagePropertiesImpl::GetStateKey() { - return state_key_; -} - -const std::wstring& PackagePropertiesImpl::GetStateMediumKey() { - return state_medium_key_; -} - -const std::wstring& PackagePropertiesImpl::GetVersionKey() { - return version_key_; -} - -void PackagePropertiesImpl::UpdateInstallStatus(bool system_level, - bool incremental_install, - bool multi_install, - InstallStatus status) { - if (ReceivesUpdates()) { - GoogleUpdateSettings::UpdateInstallStatus(system_level, - incremental_install, multi_install, - InstallUtil::GetInstallReturnCode(status), guid_); - } -} - -ChromiumPackageProperties::ChromiumPackageProperties() - : PackagePropertiesImpl(L"", L"Software\\Chromium", L"Software\\Chromium", - L"Software\\Chromium") { -} - -ChromiumPackageProperties::~ChromiumPackageProperties() { -} - -ChromePackageProperties::ChromePackageProperties() - : PackagePropertiesImpl( - kChromePackageGuid, - GetKeyForGuid(google_update::kRegPathClientState, - kChromePackageGuid), - GetKeyForGuid(google_update::kRegPathClientStateMedium, - kChromePackageGuid), - GetKeyForGuid(google_update::kRegPathClients, - kChromePackageGuid)) { -} - -ChromePackageProperties::~ChromePackageProperties() { -} - -} // namespace installer diff --git a/chrome/installer/util/package_properties.h b/chrome/installer/util/package_properties.h deleted file mode 100644 index 5fd5e5f5..0000000 --- a/chrome/installer/util/package_properties.h +++ /dev/null @@ -1,109 +0,0 @@ -// Copyright (c) 2011 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_UTIL_PACKAGE_PROPERTIES_H_ -#define CHROME_INSTALLER_UTIL_PACKAGE_PROPERTIES_H_ -#pragma once - -#include <windows.h> - -#include <string> - -#include "base/basictypes.h" - -namespace installer { -enum InstallStatus; -}; - -namespace installer { - -// Pure virtual interface that exposes properties of a package installation. -// A package represents a set of binaries on disk that can be shared by two or -// more products. Also see the Package class for further details. -// PackageProperties is comparable to the BrowserDistribution class but the -// difference is that the BrowserDistribution class represents a product -// installation whereas PackageProperties represents a package -// (horizontal vs vertical). -class PackageProperties { - public: - PackageProperties() {} - virtual ~PackageProperties() {} - - static const char kPackageProductName[]; - - // Returns true iff this package will be updated by Google Update. - virtual bool ReceivesUpdates() const = 0; - - // Equivalent to BrowserDistribution::GetAppGuid() - virtual const std::wstring& GetAppGuid() = 0; - virtual const std::wstring& GetStateKey() = 0; - virtual const std::wstring& GetStateMediumKey() = 0; - virtual const std::wstring& GetVersionKey() = 0; - virtual void UpdateInstallStatus(bool system_level, bool incremental_install, - bool multi_install, installer::InstallStatus status) = 0; - - private: - DISALLOW_COPY_AND_ASSIGN(PackageProperties); -}; // class PackageProperties - -class PackagePropertiesImpl : public PackageProperties { - public: - explicit PackagePropertiesImpl(const wchar_t* guid, - const std::wstring& state_key, - const std::wstring& state_medium_key, - const std::wstring& version_key); - virtual ~PackagePropertiesImpl(); - - virtual const std::wstring& GetAppGuid(); - virtual const std::wstring& GetStateKey(); - virtual const std::wstring& GetStateMediumKey(); - virtual const std::wstring& GetVersionKey(); - virtual void UpdateInstallStatus(bool system_level, bool incremental_install, - bool multi_install, installer::InstallStatus status); - - protected: - std::wstring guid_; - std::wstring state_key_; - std::wstring state_medium_key_; - std::wstring version_key_; - - private: - DISALLOW_COPY_AND_ASSIGN(PackagePropertiesImpl); -}; // class PackagePropertiesImpl - -class ChromiumPackageProperties : public PackagePropertiesImpl { - public: - ChromiumPackageProperties(); - virtual ~ChromiumPackageProperties(); - - virtual bool ReceivesUpdates() const { - return false; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ChromiumPackageProperties); -}; // class ChromiumPackageProperties - -class ChromePackageProperties : public PackagePropertiesImpl { - public: - ChromePackageProperties(); - virtual ~ChromePackageProperties(); - - virtual bool ReceivesUpdates() const { - return true; - } - - private: - DISALLOW_COPY_AND_ASSIGN(ChromePackageProperties); -}; // class ChromePackageProperties - -#if defined(GOOGLE_CHROME_BUILD) -typedef ChromePackageProperties ActivePackageProperties; -#else -typedef ChromiumPackageProperties ActivePackageProperties; -#endif - -} // namespace installer - -#endif // CHROME_INSTALLER_UTIL_PACKAGE_PROPERTIES_H_ diff --git a/chrome/installer/util/package_properties_unittest.cc b/chrome/installer/util/package_properties_unittest.cc deleted file mode 100644 index 000cc72..0000000 --- a/chrome/installer/util/package_properties_unittest.cc +++ /dev/null @@ -1,40 +0,0 @@ -// Copyright (c) 2010 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 "base/logging.h" -#include "base/win/registry.h" -#include "chrome/installer/util/google_update_constants.h" -#include "chrome/installer/util/package_properties.h" -#include "chrome/installer/util/product_unittest.h" -#include "chrome/installer/util/util_constants.h" -#include "testing/gtest/include/gtest/gtest.h" - -using base::win::RegKey; -using installer::PackageProperties; -using installer::ChromePackageProperties; -using installer::ChromiumPackageProperties; - -class PackagePropertiesTest : public testing::Test { - protected: -}; - -TEST_F(PackagePropertiesTest, Basic) { - TempRegKeyOverride::DeleteAllTempKeys(); - ChromePackageProperties chrome_props; - ChromiumPackageProperties chromium_props; - PackageProperties* props[] = { &chrome_props, &chromium_props }; - for (size_t i = 0; i < arraysize(props); ++i) { - std::wstring state_key(props[i]->GetStateKey()); - EXPECT_FALSE(state_key.empty()); - std::wstring version_key(props[i]->GetVersionKey()); - EXPECT_FALSE(version_key.empty()); - if (!props[i]->ReceivesUpdates()) { - TempRegKeyOverride override(HKEY_CURRENT_USER, L"props"); - RegKey key; - EXPECT_EQ(ERROR_SUCCESS, - key.Create(HKEY_CURRENT_USER, state_key.c_str(), KEY_ALL_ACCESS)); - } - TempRegKeyOverride::DeleteAllTempKeys(); - } -} diff --git a/chrome/installer/util/package_unittest.cc b/chrome/installer/util/package_unittest.cc deleted file mode 100644 index ea696a7..0000000 --- a/chrome/installer/util/package_unittest.cc +++ /dev/null @@ -1,190 +0,0 @@ -// Copyright (c) 2011 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 "base/command_line.h" -#include "base/logging.h" -#include "base/utf_string_conversions.h" -#include "base/win/scoped_handle.h" -#include "chrome/installer/util/browser_distribution.h" -#include "chrome/installer/util/google_update_constants.h" -#include "chrome/installer/util/master_preferences.h" -#include "chrome/installer/util/package.h" -#include "chrome/installer/util/package_properties.h" -#include "chrome/installer/util/product.h" -#include "chrome/installer/util/product_unittest.h" -#include "chrome/installer/util/util_constants.h" - -using base::win::RegKey; -using installer::ChromePackageProperties; -using installer::ChromiumPackageProperties; -using installer::Package; -using installer::Product; -using installer::MasterPreferences; - -class PackageTest : public TestWithTempDirAndDeleteTempOverrideKeys { - protected: -}; - -// Tests a few basic things of the Package class. Makes sure that the path -// operations are correct -TEST_F(PackageTest, Basic) { - const bool multi_install = false; - const bool system_level = true; - ChromiumPackageProperties properties; - scoped_refptr<Package> package(new Package(multi_install, system_level, - test_dir_.path(), &properties)); - EXPECT_EQ(test_dir_.path().value(), package->path().value()); - EXPECT_TRUE(package->IsEqual(test_dir_.path())); - EXPECT_EQ(0U, package->products().size()); - - const char kOldVersion[] = "1.2.3.4"; - const char kNewVersion[] = "2.3.4.5"; - - scoped_ptr<Version> new_version(Version::GetVersionFromString(kNewVersion)); - scoped_ptr<Version> old_version(Version::GetVersionFromString(kOldVersion)); - ASSERT_TRUE(new_version.get() != NULL); - ASSERT_TRUE(old_version.get() != NULL); - - FilePath installer_dir(package->GetInstallerDirectory(*new_version.get())); - EXPECT_FALSE(installer_dir.empty()); - - FilePath new_version_dir(package->path().Append( - UTF8ToWide(new_version->GetString()))); - FilePath old_version_dir(package->path().Append( - UTF8ToWide(old_version->GetString()))); - - EXPECT_FALSE(file_util::PathExists(new_version_dir)); - EXPECT_FALSE(file_util::PathExists(old_version_dir)); - - EXPECT_FALSE(file_util::PathExists(installer_dir)); - file_util::CreateDirectory(installer_dir); - EXPECT_TRUE(file_util::PathExists(new_version_dir)); - - file_util::CreateDirectory(old_version_dir); - EXPECT_TRUE(file_util::PathExists(old_version_dir)); - - // Create a fake chrome.dll key file in the old version directory. This - // should prevent the old version directory from getting deleted. - FilePath old_chrome_dll(old_version_dir.Append(installer::kChromeDll)); - EXPECT_FALSE(file_util::PathExists(old_chrome_dll)); - - // Hold on to the file exclusively to prevent the directory from - // being deleted. - base::win::ScopedHandle file( - ::CreateFile(old_chrome_dll.value().c_str(), GENERIC_READ, - 0, NULL, OPEN_ALWAYS, 0, NULL)); - EXPECT_TRUE(file.IsValid()); - EXPECT_TRUE(file_util::PathExists(old_chrome_dll)); - - package->RemoveOldVersionDirectories(*new_version.get()); - // The old directory should still exist. - EXPECT_TRUE(file_util::PathExists(old_version_dir)); - EXPECT_TRUE(file_util::PathExists(new_version_dir)); - - // Now close the file handle to make it possible to delete our key file. - file.Close(); - - package->RemoveOldVersionDirectories(*new_version.get()); - // The new directory should still exist. - EXPECT_TRUE(file_util::PathExists(new_version_dir)); - - // Now, the old directory and key file should be gone. - EXPECT_FALSE(file_util::PathExists(old_chrome_dll)); - EXPECT_FALSE(file_util::PathExists(old_version_dir)); -} - -TEST_F(PackageTest, WithProduct) { - const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); - - // TODO(tommi): We should mock this and use our mocked distribution. - const bool multi_install = false; - const bool system_level = true; - BrowserDistribution* distribution = - BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs); - ChromePackageProperties properties; - scoped_refptr<Package> package(new Package(multi_install, system_level, - test_dir_.path(), &properties)); - scoped_refptr<Product> product(new Product(distribution, package.get())); - EXPECT_EQ(1U, package->products().size()); - EXPECT_EQ(system_level, package->system_level()); - - const char kCurrentVersion[] = "1.2.3.4"; - scoped_ptr<Version> current_version( - Version::GetVersionFromString(kCurrentVersion)); - - HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - { - TempRegKeyOverride override(root, L"root_pit"); - RegKey chrome_key(root, distribution->GetVersionKey().c_str(), - KEY_ALL_ACCESS); - EXPECT_TRUE(chrome_key.Valid()); - if (chrome_key.Valid()) { - chrome_key.WriteValue(google_update::kRegVersionField, - UTF8ToWide(current_version->GetString()).c_str()); - // TODO(tommi): Also test for when there exists a new_chrome.exe. - scoped_ptr<Version> found_version(package->GetCurrentVersion()); - EXPECT_TRUE(found_version.get() != NULL); - if (found_version.get()) { - EXPECT_TRUE(current_version->Equals(*found_version)); - } - } - } -} - -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()) == ERROR_SUCCESS); -} - -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()) == - ERROR_SUCCESS); -} -} // end namespace - -TEST_F(PackageTest, Dependency) { - const bool multi_install = false; - const bool system_level = true; - HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - TempRegKeyOverride override(root, L"root_dep"); - - ChromePackageProperties properties; - scoped_refptr<Package> package(new Package(multi_install, system_level, - test_dir_.path(), &properties)); - EXPECT_EQ(0U, package->GetMultiInstallDependencyCount()); - - const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); - - BrowserDistribution* chrome = BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs); - 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. - 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. - 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. - 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 de56027..a1e99f6 100644 --- a/chrome/installer/util/product.cc +++ b/chrome/installer/util/product.cc @@ -10,74 +10,70 @@ #include "base/logging.h" #include "base/process_util.h" #include "base/win/registry.h" +#include "chrome/installer/util/chrome_browser_operations.h" +#include "chrome/installer/util/chrome_browser_sxs_operations.h" +#include "chrome/installer/util/chrome_frame_operations.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_properties.h" +#include "chrome/installer/util/product_operations.h" using base::win::RegKey; using installer::MasterPreferences; -namespace { -class ProductIsOfType { - public: - explicit ProductIsOfType(BrowserDistribution::Type type) - : type_(type) { } - bool operator()( - const scoped_refptr<const installer::Product>& pi) const { - return pi->distribution()->GetType() == type_; - } - private: - BrowserDistribution::Type type_; -}; // class ProductIsOfType -} // end namespace - namespace installer { -const Product* FindProduct(const Products& products, - BrowserDistribution::Type type) { - Products::const_iterator i = - std::find_if(products.begin(), products.end(), ProductIsOfType(type)); - return i == products.end() ? NULL : *i; +Product::Product(BrowserDistribution* distribution) + : distribution_(distribution) { + switch (distribution->GetType()) { + case BrowserDistribution::CHROME_BROWSER: + operations_.reset(InstallUtil::IsChromeSxSProcess() ? + new ChromeBrowserSxSOperations() : + new ChromeBrowserOperations()); + break; + case BrowserDistribution::CHROME_FRAME: + operations_.reset(new ChromeFrameOperations()); + break; + default: + NOTREACHED() << "Unsupported BrowserDistribution::Type: " + << distribution->GetType(); + } } -//////////////////////////////////////////////////////////////////////////////// +Product::~Product() { +} -Product::Product(BrowserDistribution* distribution, Package* package) - : distribution_(distribution), - package_(package), - msi_(false), - cache_state_(0) { - package_->AssociateProduct(this); +void Product::InitializeFromPreferences(const MasterPreferences& prefs) { + operations_->ReadOptions(prefs, &options_); } -const Package& Product::package() const { - return *package_.get(); +void Product::InitializeFromUninstallCommand( + const CommandLine& uninstall_command) { + operations_->ReadOptions(uninstall_command, &options_); } FilePath Product::GetUserDataPath() const { return GetChromeUserDataPath(distribution_); } -bool Product::LaunchChrome() const { - const FilePath& install_package = package_->path(); - bool success = !install_package.empty(); +bool Product::LaunchChrome(const FilePath& application_path) const { + bool success = !application_path.empty(); if (success) { - CommandLine cmd(install_package.Append(installer::kChromeExe)); + CommandLine cmd(application_path.Append(installer::kChromeExe)); success = base::LaunchApp(cmd, false, false, NULL); } return success; } -bool Product::LaunchChromeAndWait(const CommandLine& options, +bool Product::LaunchChromeAndWait(const FilePath& application_path, + const CommandLine& options, int32* exit_code) const { - const FilePath& install_package = package_->path(); - if (install_package.empty()) + if (application_path.empty()) return false; - CommandLine cmd(install_package.Append(installer::kChromeExe)); + CommandLine cmd(application_path.Append(installer::kChromeExe)); cmd.AppendArguments(options, false); bool success = false; @@ -113,36 +109,8 @@ bool Product::LaunchChromeAndWait(const CommandLine& options, return success; } -bool Product::IsMsi() const { - if ((cache_state_ & MSI_STATE) == 0) { - msi_ = false; // Covers failure cases below. - - const MasterPreferences& prefs = MasterPreferences::ForCurrentProcess(); - - bool is_msi = false; - prefs.GetBool(installer::master_preferences::kMsi, &is_msi); - - if (!is_msi) { - // We didn't find it in the preferences, try looking in the registry. - HKEY reg_root = system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; - RegKey key; - if (key.Open(reg_root, distribution_->GetStateKey().c_str(), - KEY_READ) == ERROR_SUCCESS) { - DWORD msi_value = 0; - key.ReadValueDW(google_update::kRegMSIField, &msi_value); - msi_ = msi_value != 0; - } - } else { - msi_ = true; - } - cache_state_ |= MSI_STATE; - } - - return msi_; -} - -bool Product::SetMsiMarker(bool set) const { - HKEY reg_root = system_level() ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; +bool Product::SetMsiMarker(bool system_install, bool set) const { + HKEY reg_root = system_install ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; RegKey client_state_key; LONG result = client_state_key.Open(reg_root, distribution_->GetStateKey().c_str(), KEY_READ | KEY_WRITE); @@ -158,92 +126,23 @@ bool Product::SetMsiMarker(bool set) const { } 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) - : package_properties_(new ActivePackageProperties()), - multi_install_(multi_install), - system_level_(system_level) { + return operations_->ShouldCreateUninstallEntry(options_); } -const Packages& ProductPackageMapping::packages() const { - return packages_; +void Product::AddKeyFiles(std::vector<FilePath>* key_files) const { + operations_->AddKeyFiles(options_, key_files); } -const Products& ProductPackageMapping::products() const { - return products_; +void Product::AddComDllList(std::vector<FilePath>* com_dll_list) const { + operations_->AddComDllList(options_, com_dll_list); } -bool ProductPackageMapping::AddDistribution(BrowserDistribution* distribution) { - DCHECK(distribution); - // Each product type can be added exactly once. - DCHECK(FindProduct(products_, distribution->GetType()) == NULL); - - 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<Package> target_package; - for (size_t i = 0; i < packages_.size(); ++i) { - if (packages_[i]->IsEqual(install_package)) { - // Use an existing Package. - target_package = packages_[i]; - break; - } - } - - if (!target_package.get()) { - DCHECK(packages_.empty()) << "Multiple packages per run unsupported."; - // create new one and add. - target_package = new Package(multi_install_, system_level_, install_package, - package_properties_.get()); - packages_.push_back(target_package); - } - - scoped_refptr<Product> product(new Product(distribution, target_package)); -#ifndef NDEBUG - for (size_t i = 0; i < products_.size(); ++i) { - DCHECK_EQ(product->IsMsi(), products_[i]->IsMsi()); - } -#endif - products_.push_back(product); - - return true; +void Product::AppendProductFlags(CommandLine* command_line) const { + operations_->AppendProductFlags(options_, command_line); } -bool ProductPackageMapping::AddDistribution( - BrowserDistribution::Type type, - const MasterPreferences& prefs) { - BrowserDistribution* distribution = - BrowserDistribution::GetSpecificDistribution(type, prefs); - if (!distribution) { - NOTREACHED(); - return false; - } - - return AddDistribution(distribution); +bool Product::SetChannelFlags(bool set, ChannelInfo* channel_info) const { + return operations_->SetChannelFlags(options_, set, channel_info); } } // namespace installer - diff --git a/chrome/installer/util/product.h b/chrome/installer/util/product.h index 28361df..86d3fca 100644 --- a/chrome/installer/util/product.h +++ b/chrome/installer/util/product.h @@ -6,30 +6,20 @@ #define CHROME_INSTALLER_UTIL_PRODUCT_H_ #pragma once +#include <set> +#include <string> #include <vector> -#include "base/ref_counted.h" #include "base/scoped_ptr.h" #include "chrome/installer/util/browser_distribution.h" -#include "chrome/installer/util/package.h" class CommandLine; namespace installer { -class MasterPreferences; -} - -namespace installer { +class MasterPreferences; class Product; -class Package; -class PackageProperties; - -typedef std::vector<scoped_refptr<Package> > Packages; -typedef std::vector<scoped_refptr<const Product> > Products; - -const Product* FindProduct(const Products& products, - BrowserDistribution::Type type); +class ProductOperations; // Represents an installation of a specific product which has a one-to-one // relation to a BrowserDistribution. A product has registry settings, related @@ -40,23 +30,22 @@ const Product* FindProduct(const Products& products, // the future, as we move away from global functions and towards a data driven // installation, each distribution could derive from this class and provide // distribution specific functionality. -class Product : public base::RefCounted<Product> { +class Product { public: - Product(BrowserDistribution* distribution, Package* installation_package); + explicit Product(BrowserDistribution* distribution); + + ~Product(); + + void InitializeFromPreferences(const MasterPreferences& prefs); + + void InitializeFromUninstallCommand(const CommandLine& uninstall_command); BrowserDistribution* distribution() const { return distribution_; } - // Returns the install package object for the installation of this product. - // If the product is installed at system level,the function returns a system - // wide location (ProgramFiles\Google). Otherwise it returns a package in a - // user specific location (Users\<user>\Local Settings...) - const Package& package() const; - - // Convenience getter for package().system_level(). - bool system_level() const { - return package().system_level(); + bool is_type(BrowserDistribution::Type type) const { + return distribution_->GetType() == type; } bool is_chrome() const { @@ -67,6 +56,18 @@ class Product : public base::RefCounted<Product> { return distribution_->GetType() == BrowserDistribution::CHROME_FRAME; } + bool HasOption(const std::wstring& option) const { + return options_.find(option) != options_.end(); + } + + // Returns true if the set of options is mutated by this operation. + bool SetOption(const std::wstring& option, bool set) { + if (set) + return options_.insert(option).second; + else + return options_.erase(option) != 0; + } + // Returns the path to the directory that holds the user data. This is always // inside "Users\<user>\Local Settings". Note that this is the default user // data directory and does not take into account that it can be overriden with @@ -74,7 +75,7 @@ class Product : public base::RefCounted<Product> { FilePath GetUserDataPath() const; // Launches Chrome without waiting for it to exit. - bool LaunchChrome() const; + bool LaunchChrome(const FilePath& application_path) const; // Launches Chrome with given command line, waits for Chrome indefinitely // (until it terminates), and gets the process exit code if available. @@ -82,77 +83,44 @@ class Product : public base::RefCounted<Product> { // The status of Chrome at the return of the function is given by exit_code. // NOTE: The 'options' CommandLine object should only contain parameters. // The program part will be ignored. - bool LaunchChromeAndWait(const CommandLine& options, int32* exit_code) const; - - // Returns true if this setup process is running as an install managed by an - // MSI wrapper. There are three things that are checked: - // 1) the presence of --msi on the command line - // 2) the presence of "msi": true in the master preferences file - // 3) the presence of a DWORD value in the ClientState key called msi with - // value 1 - bool IsMsi() const; + bool LaunchChromeAndWait(const FilePath& application_path, + const CommandLine& options, + int32* exit_code) const; // Sets the boolean MSI marker for this installation if set is true or clears // it otherwise. The MSI marker is stored in the registry under the // ClientState key. - bool SetMsiMarker(bool set) const; + bool SetMsiMarker(bool system_install, bool set) const; // Returns true if setup should create an entry in the Add/Remove list // of installed applications. bool ShouldCreateUninstallEntry() const; + // See ProductOperations::AddKeyFiles. + void AddKeyFiles(std::vector<FilePath>* key_files) const; + + // See ProductOperations::AddComDllList. + void AddComDllList(std::vector<FilePath>* com_dll_list) const; + + // See ProductOperations::AppendProductFlags. + void AppendProductFlags(CommandLine* command_line) const; + + // See Productoperations::SetChannelFlags. + bool SetChannelFlags(bool set, ChannelInfo* channel_info) const; + protected: enum CacheStateFlags { MSI_STATE = 0x01 }; BrowserDistribution* distribution_; - scoped_refptr<Package> package_; - mutable bool msi_; - mutable uint8 cache_state_; + scoped_ptr<ProductOperations> operations_; + std::set<std::wstring> options_; private: - friend class base::RefCounted<Product>; - ~Product() { - } DISALLOW_COPY_AND_ASSIGN(Product); }; -// A collection of Product objects and related physical installation -// packages. Each Product is associated with a single installation -// package object, and each package object is associated with one or more -// Product objects. -class ProductPackageMapping { - public: - explicit ProductPackageMapping(bool multi_install, bool system_level); - - bool multi_install() const { - return multi_install_; - } - - bool system_level() const { - return system_level_; - } - - const Packages& packages() const; - - const Products& products() const; - - bool AddDistribution(BrowserDistribution::Type type, - const installer::MasterPreferences& prefs); - bool AddDistribution(BrowserDistribution* distribution); - - protected: - bool multi_install_; - bool system_level_; - Packages packages_; - Products products_; - scoped_ptr<PackageProperties> package_properties_; - - private: - DISALLOW_COPY_AND_ASSIGN(ProductPackageMapping); -}; - } // namespace installer #endif // CHROME_INSTALLER_UTIL_PRODUCT_H_ diff --git a/chrome/installer/util/product_operations.h b/chrome/installer/util/product_operations.h new file mode 100644 index 0000000..0874d3d --- /dev/null +++ b/chrome/installer/util/product_operations.h @@ -0,0 +1,74 @@ +// Copyright (c) 2011 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_UTIL_PRODUCT_OPERATIONS_H_ +#define CHROME_INSTALLER_UTIL_PRODUCT_OPERATIONS_H_ +#pragma once + +#include <set> +#include <string> +#include <vector> + +#include "base/file_path.h" + +class CommandLine; + +namespace installer { + +class ChannelInfo; +class MasterPreferences; + +// An interface to product-specific operations that depend on product +// configuration. Implementations are expected to be stateless. Configuration +// can be read from a MasterPreferences instance or from a product's uninstall +// command. +class ProductOperations { + public: + virtual ~ProductOperations() {} + + // Reads product-specific options from |prefs|, adding them to |options|. + virtual void ReadOptions(const MasterPreferences& prefs, + std::set<std::wstring>* options) const = 0; + + // Reads product-specific options from |command|, adding them to |options|. + virtual void ReadOptions(const CommandLine& command, + std::set<std::wstring>* options) const = 0; + + // A key-file is a file such as a DLL on Windows that is expected to be in use + // when the product is being used. For example "chrome.dll" for Chrome. + // Before attempting to delete an installation directory during an + // uninstallation, the uninstaller will check if any one of a potential set of + // key files is in use and if they are, abort the delete operation. Only if + // none of the key files are in use, can the folder be deleted. Note that + // this function does not return a full path to the key file(s), only (a) file + // name(s). + virtual void AddKeyFiles(const std::set<std::wstring>& options, + std::vector<FilePath>* key_files) const = 0; + + // Adds to |com_dll_list| the list of COM DLLs that are to be registered + // and/or unregistered. The list may be empty. + virtual void AddComDllList(const std::set<std::wstring>& options, + std::vector<FilePath>* com_dll_list) const = 0; + + // Given a command line, appends the set of uninstall flags the uninstaller + // for this product will require. + virtual void AppendProductFlags(const std::set<std::wstring>& options, + CommandLine* uninstall_command) const = 0; + + // Adds or removes product-specific flags in |channel_info|. Returns true if + // |channel_info| is modified. + virtual bool SetChannelFlags(const std::set<std::wstring>& options, + bool set, + ChannelInfo* channel_info) const = 0; + + // Returns true if setup should create an entry in the Add/Remove list + // of installed applications for this product. This does not test for use of + // MSI; see InstallerState::is_msi. + virtual bool ShouldCreateUninstallEntry( + const std::set<std::wstring>& options) const = 0; +}; + +} // namespace installer + +#endif // CHROME_INSTALLER_UTIL_PRODUCT_OPERATIONS_H_ diff --git a/chrome/installer/util/product_state_unittest.cc b/chrome/installer/util/product_state_unittest.cc new file mode 100644 index 0000000..c6031359 --- /dev/null +++ b/chrome/installer/util/product_state_unittest.cc @@ -0,0 +1,416 @@ +// Copyright (c) 2011 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 <windows.h> + +#include "base/utf_string_conversions.h" +#include "base/version.h" +#include "base/win/registry.h" +#include "chrome/installer/util/browser_distribution.h" +#include "chrome/installer/util/google_update_constants.h" +#include "chrome/installer/util/installation_state.h" +#include "chrome/installer/util/product_unittest.h" +#include "chrome/installer/util/util_constants.h" +#include "testing/gtest/include/gtest/gtest.h" + +using base::win::RegKey; +using installer::ProductState; + +class ProductStateTest : public testing::Test { + protected: + static void SetUpTestCase(); + static void TearDownTestCase(); + + virtual void SetUp(); + virtual void TearDown(); + + void ApplyUninstallCommand(const wchar_t* exe_path, const wchar_t* args); + void MinimallyInstallProduct(const wchar_t* version); + + static BrowserDistribution* dist_; + static std::wstring temp_key_path_; + bool system_install_; + HKEY overridden_; + RegKey clients_; + RegKey client_state_; +}; + +BrowserDistribution* ProductStateTest::dist_; +std::wstring ProductStateTest::temp_key_path_; + +// static +void ProductStateTest::SetUpTestCase() { + testing::Test::SetUpTestCase(); + + // We'll use Chrome as our test subject. + dist_ = BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BROWSER); + + // And we'll play in HKCU here: + temp_key_path_.assign(TempRegKeyOverride::kTempTestKeyPath) + .append(1, L'\\') + .append(L"ProductStateTest"); +} + +// static +void ProductStateTest::TearDownTestCase() { + temp_key_path_.clear(); + dist_ = NULL; + + testing::Test::TearDownTestCase(); +} + +void ProductStateTest::SetUp() { + testing::Test::SetUp(); + + // Create/open the keys for the product we'll test. + system_install_ = true; + overridden_ = (system_install_ ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER); + + // Override for test purposes. We don't use TempRegKeyOverride + // directly because it doesn't suit itself to our use here. + RegKey temp_key; + EXPECT_EQ(ERROR_SUCCESS, + temp_key.Create(HKEY_CURRENT_USER, temp_key_path_.c_str(), + KEY_ALL_ACCESS)); + EXPECT_EQ(ERROR_SUCCESS, + ::RegOverridePredefKey(overridden_, temp_key.Handle())); + + EXPECT_EQ(ERROR_SUCCESS, + clients_.Create(overridden_, dist_->GetVersionKey().c_str(), + KEY_ALL_ACCESS)); + EXPECT_EQ(ERROR_SUCCESS, + client_state_.Create(overridden_, dist_->GetStateKey().c_str(), + KEY_ALL_ACCESS)); +} + +void ProductStateTest::TearDown() { + // Done with the keys. + client_state_.Close(); + clients_.Close(); + EXPECT_EQ(ERROR_SUCCESS, ::RegOverridePredefKey(overridden_, NULL)); + overridden_ = NULL; + system_install_ = false; + + // Shotgun approach to clearing out data we may have written. + TempRegKeyOverride::DeleteAllTempKeys(); + + testing::Test::TearDown(); +} + +void ProductStateTest::MinimallyInstallProduct(const wchar_t* version) { + EXPECT_EQ(ERROR_SUCCESS, + clients_.WriteValue(google_update::kRegVersionField, version)); +} + +void ProductStateTest::ApplyUninstallCommand(const wchar_t* exe_path, + const wchar_t* args) { + if (exe_path == NULL) { + LONG result = client_state_.DeleteValue(installer::kUninstallStringField); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + } else { + EXPECT_EQ(ERROR_SUCCESS, + client_state_.WriteValue(installer::kUninstallStringField, + exe_path)); + } + + if (args == NULL) { + LONG result = + client_state_.DeleteValue(installer::kUninstallArgumentsField); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + } else { + EXPECT_EQ(ERROR_SUCCESS, + client_state_.WriteValue(installer::kUninstallArgumentsField, + args)); + } +} + +TEST_F(ProductStateTest, InitializeInstalled) { + // Not installed. + { + ProductState state; + LONG result = clients_.DeleteValue(google_update::kRegVersionField); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_FALSE(state.Initialize(system_install_, dist_)); + } + + // Empty version. + { + ProductState state; + LONG result = clients_.WriteValue(google_update::kRegVersionField, L""); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_FALSE(state.Initialize(system_install_, dist_)); + } + + // Bogus version. + { + ProductState state; + LONG result = clients_.WriteValue(google_update::kRegVersionField, + L"goofy"); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_FALSE(state.Initialize(system_install_, dist_)); + } + + // Valid "pv" value. + { + ProductState state; + LONG result = clients_.WriteValue(google_update::kRegVersionField, + L"10.0.47.0"); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_EQ("10.0.47.0", state.version().GetString()); + } +} + +// Test extraction of the "opv" value from the Clients key. +TEST_F(ProductStateTest, InitializeOldVersion) { + MinimallyInstallProduct(L"10.0.1.1"); + + // No "opv" value. + { + ProductState state; + LONG result = clients_.DeleteValue(google_update::kRegOldVersionField); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.old_version() == NULL); + } + + // Empty "opv" value. + { + ProductState state; + LONG result = clients_.WriteValue(google_update::kRegOldVersionField, L""); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.old_version() == NULL); + } + + // Bogus "opv" value. + { + ProductState state; + LONG result = clients_.WriteValue(google_update::kRegOldVersionField, + L"coming home"); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.old_version() == NULL); + } + + // Valid "opv" value. + { + ProductState state; + LONG result = clients_.WriteValue(google_update::kRegOldVersionField, + L"10.0.47.0"); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.old_version() != NULL); + EXPECT_EQ("10.0.47.0", state.old_version()->GetString()); + } +} + +// Test extraction of the "cmd" value from the Clients key. +TEST_F(ProductStateTest, InitializeRenameCmd) { + MinimallyInstallProduct(L"10.0.1.1"); + + // No "cmd" value. + { + ProductState state; + LONG result = clients_.DeleteValue(google_update::kRegRenameCmdField); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.rename_cmd().empty()); + } + + // Empty "cmd" value. + { + ProductState state; + LONG result = clients_.WriteValue(google_update::kRegRenameCmdField, L""); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.rename_cmd().empty()); + } + + // Valid "cmd" value. + { + ProductState state; + LONG result = clients_.WriteValue(google_update::kRegRenameCmdField, + L"spam.exe --spamalot"); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_EQ(L"spam.exe --spamalot", state.rename_cmd()); + } +} + +// Test extraction of the "ap" value from the ClientState key. +TEST_F(ProductStateTest, InitializeChannelInfo) { + MinimallyInstallProduct(L"10.0.1.1"); + + // No "ap" value. + { + ProductState state; + LONG result = client_state_.DeleteValue(google_update::kRegApField); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.channel().value().empty()); + } + + // Empty "ap" value. + { + ProductState state; + LONG result = client_state_.WriteValue(google_update::kRegApField, L""); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.channel().value().empty()); + } + + // Valid "ap" value. + { + ProductState state; + LONG result = client_state_.WriteValue(google_update::kRegApField, L"spam"); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_EQ(L"spam", state.channel().value()); + } +} + +// Test extraction of the uninstall command and arguments from the ClientState +// key. +TEST_F(ProductStateTest, InitializeUninstallCommand) { + MinimallyInstallProduct(L"10.0.1.1"); + + // No uninstall command. + { + ProductState state; + ApplyUninstallCommand(NULL, NULL); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.GetSetupPath().empty()); + EXPECT_TRUE(state.uninstall_command().command_line_string().empty()); + EXPECT_EQ(0U, state.uninstall_command().GetSwitchCount()); + } + + // Empty values. + { + ProductState state; + ApplyUninstallCommand(L"", L""); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.GetSetupPath().empty()); + EXPECT_TRUE(state.uninstall_command().command_line_string().empty()); + EXPECT_EQ(0U, state.uninstall_command().GetSwitchCount()); + } + + // Uninstall command without exe. + { + ProductState state; + ApplyUninstallCommand(NULL, L"--uninstall"); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.GetSetupPath().empty()); + EXPECT_EQ(L"\"\" --uninstall", + state.uninstall_command().command_line_string()); + EXPECT_EQ(1U, state.uninstall_command().GetSwitchCount()); + } + + // Uninstall command without args. + { + ProductState state; + ApplyUninstallCommand(L"setup.exe", NULL); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_EQ(L"setup.exe", state.GetSetupPath().value()); + EXPECT_EQ(L"\"setup.exe\"", + state.uninstall_command().command_line_string()); + EXPECT_EQ(0U, state.uninstall_command().GetSwitchCount()); + } + + // Uninstall command with both exe and args. + { + ProductState state; + ApplyUninstallCommand(L"setup.exe", L"--uninstall"); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_EQ(L"setup.exe", state.GetSetupPath().value()); + EXPECT_EQ(L"\"setup.exe\" --uninstall", + state.uninstall_command().command_line_string()); + EXPECT_EQ(1U, state.uninstall_command().GetSwitchCount()); + } +} + +// Test extraction of the msi marker from the ClientState key. +TEST_F(ProductStateTest, InitializeMsi) { + MinimallyInstallProduct(L"10.0.1.1"); + + // No msi marker. + { + ProductState state; + LONG result = client_state_.DeleteValue(google_update::kRegMSIField); + EXPECT_TRUE(result == ERROR_SUCCESS || result == ERROR_FILE_NOT_FOUND); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_FALSE(state.is_msi()); + } + + // Msi marker set to zero. + { + ProductState state; + EXPECT_EQ(ERROR_SUCCESS, + client_state_.WriteValue(google_update::kRegMSIField, + static_cast<DWORD>(0))); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_FALSE(state.is_msi()); + } + + // Msi marker set to one. + { + ProductState state; + EXPECT_EQ(ERROR_SUCCESS, + client_state_.WriteValue(google_update::kRegMSIField, + static_cast<DWORD>(1))); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.is_msi()); + } + + // Msi marker set to a bogus DWORD. + { + ProductState state; + EXPECT_EQ(ERROR_SUCCESS, + client_state_.WriteValue(google_update::kRegMSIField, + static_cast<DWORD>(47))); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.is_msi()); + } + + // Msi marker set to a bogus string. + { + ProductState state; + EXPECT_EQ(ERROR_SUCCESS, + client_state_.WriteValue(google_update::kRegMSIField, + L"bogus!")); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_FALSE(state.is_msi()); + } +} + +// Test detection of multi-install. +TEST_F(ProductStateTest, InitializeMultiInstall) { + MinimallyInstallProduct(L"10.0.1.1"); + + // No uninstall command means single install. + { + ProductState state; + ApplyUninstallCommand(NULL, NULL); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_FALSE(state.is_multi_install()); + } + + // Uninstall command without --multi-install is single install. + { + ProductState state; + ApplyUninstallCommand(L"setup.exe", L"--uninstall"); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_FALSE(state.is_multi_install()); + } + + // Uninstall command with --multi-install is multi install. + { + ProductState state; + ApplyUninstallCommand(L"setup.exe", + L"--uninstall --chrome --multi-install"); + EXPECT_TRUE(state.Initialize(system_install_, dist_)); + EXPECT_TRUE(state.is_multi_install()); + } +} diff --git a/chrome/installer/util/product_unittest.cc b/chrome/installer/util/product_unittest.cc index c072dc5..913a6ff 100644 --- a/chrome/installer/util/product_unittest.cc +++ b/chrome/installer/util/product_unittest.cc @@ -9,17 +9,12 @@ #include "chrome/installer/util/chrome_frame_distribution.h" #include "chrome/installer/util/google_update_constants.h" #include "chrome/installer/util/installation_state.h" -#include "chrome/installer/util/package.h" -#include "chrome/installer/util/package_properties.h" +#include "chrome/installer/util/installer_state.h" #include "chrome/installer/util/master_preferences.h" #include "chrome/installer/util/product.h" using base::win::RegKey; -using installer::ChromePackageProperties; -using installer::ChromiumPackageProperties; -using installer::Package; using installer::Product; -using installer::ProductPackageMapping; using installer::MasterPreferences; void TestWithTempDir::SetUp() { @@ -82,15 +77,19 @@ TEST_F(ProductTest, ProductInstallBasic) { // TODO(tommi): We should mock this and use our mocked distribution. const bool multi_install = false; const bool system_level = true; - const installer::MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); - BrowserDistribution* distribution = - BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs); - ChromePackageProperties properties; - scoped_refptr<Package> package(new Package(multi_install, system_level, - test_dir_.path(), &properties)); - scoped_refptr<Product> product(new Product(distribution, package.get())); + CommandLine cmd_line = CommandLine::FromString( + std::wstring(L"setup.exe") + + (multi_install ? L" --multi-install --chrome" : L"") + + (system_level ? L" --system-level" : L"")); + installer::MasterPreferences prefs(cmd_line); + installer::InstallationState machine_state; + machine_state.Initialize(); + installer::InstallerState installer_state; + installer_state.Initialize(cmd_line, prefs, machine_state); + + const Product* product = installer_state.products()[0]; + BrowserDistribution* distribution = product->distribution(); + EXPECT_EQ(BrowserDistribution::CHROME_BROWSER, distribution->GetType()); FilePath user_data(product->GetUserDataPath()); EXPECT_FALSE(user_data.empty()); @@ -104,35 +103,15 @@ TEST_F(ProductTest, ProductInstallBasic) { EXPECT_EQ(std::wstring::npos, user_data.value().find(program_files.value())); - // We started out with a non-msi product. - EXPECT_FALSE(product->IsMsi()); + // There should be no installed version in the registry. + machine_state.Initialize(); + EXPECT_TRUE(machine_state.GetProductState( + system_level, distribution->GetType()) == NULL); - HKEY root = system_level ? HKEY_LOCAL_MACHINE : HKEY_CURRENT_USER; + HKEY root = installer_state.root_key(); { TempRegKeyOverride override(root, L"root_pit"); - // Create a make-believe client state key. - RegKey key; - std::wstring state_key_path(distribution->GetStateKey()); - ASSERT_EQ(ERROR_SUCCESS, - key.Create(root, state_key_path.c_str(), KEY_ALL_ACCESS)); - - // Set the MSI marker, delete the objects, create new ones and verify - // that we now see the MSI marker. - EXPECT_TRUE(product->SetMsiMarker(true)); - package = new Package(multi_install, system_level, test_dir_.path(), - &properties); - product = new Product(distribution, package.get()); - EXPECT_TRUE(product->IsMsi()); - - // There should be no installed version in the registry. - { - installer::InstallationState state; - state.Initialize(prefs); - EXPECT_TRUE(state.GetProductState(system_level, - distribution->GetType()) == NULL); - } - // Let's pretend chrome is installed. RegKey version_key(root, distribution->GetVersionKey().c_str(), KEY_ALL_ACCESS); @@ -144,16 +123,30 @@ TEST_F(ProductTest, ProductInstallBasic) { version_key.WriteValue(google_update::kRegVersionField, UTF8ToWide(current_version->GetString()).c_str()); - { - installer::InstallationState state; - state.Initialize(prefs); - const installer::ProductState* prod_state = - state.GetProductState(system_level, distribution->GetType()); - EXPECT_TRUE(prod_state != NULL); - if (prod_state != NULL) { - EXPECT_TRUE(prod_state->version().Equals(*current_version.get())); - } + // We started out with a non-msi product. + machine_state.Initialize(); + const installer::ProductState* chrome_state = + machine_state.GetProductState(system_level, distribution->GetType()); + EXPECT_TRUE(chrome_state != NULL); + if (chrome_state != NULL) { + EXPECT_TRUE(chrome_state->version().Equals(*current_version.get())); + EXPECT_FALSE(chrome_state->is_msi()); } + + // Create a make-believe client state key. + RegKey key; + std::wstring state_key_path(distribution->GetStateKey()); + ASSERT_EQ(ERROR_SUCCESS, + key.Create(root, state_key_path.c_str(), KEY_ALL_ACCESS)); + + // Set the MSI marker, refresh, and verify that we now see the MSI marker. + EXPECT_TRUE(product->SetMsiMarker(system_level, true)); + machine_state.Initialize(); + chrome_state = + machine_state.GetProductState(system_level, distribution->GetType()); + EXPECT_TRUE(chrome_state != NULL); + if (chrome_state != NULL) + EXPECT_TRUE(chrome_state->is_msi()); } } @@ -162,41 +155,3 @@ TEST_F(ProductTest, LaunchChrome) { // Product::LaunchChromeAndWait. LOG(ERROR) << "Test not implemented."; } - -// Overrides ChromeFrameDistribution for the sole purpose of returning -// the Chrome (not Chrome Frame) installation path. -class FakeChromeFrameDistribution : public ChromeFrameDistribution { - public: - explicit FakeChromeFrameDistribution( - const installer::MasterPreferences& prefs) - : ChromeFrameDistribution(prefs) {} - virtual std::wstring GetInstallSubDir() { - const MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); - return BrowserDistribution::GetSpecificDistribution( - BrowserDistribution::CHROME_BROWSER, prefs)->GetInstallSubDir(); - } -}; - -TEST_F(ProductTest, ProductInstallsBasic) { - const bool multi_install = true; - const bool system_level = true; - ProductPackageMapping installs(multi_install, system_level); - EXPECT_EQ(multi_install, installs.multi_install()); - EXPECT_EQ(system_level, installs.system_level()); - EXPECT_EQ(0U, installs.packages().size()); - EXPECT_EQ(0U, installs.products().size()); - - // TODO(robertshield): Include test that use mock master preferences. - const MasterPreferences& prefs = - installer::MasterPreferences::ForCurrentProcess(); - - installs.AddDistribution(BrowserDistribution::CHROME_BROWSER, prefs); - FakeChromeFrameDistribution fake_chrome_frame(prefs); - installs.AddDistribution(&fake_chrome_frame); - EXPECT_EQ(2U, installs.products().size()); - // Since our fake Chrome Frame distribution class is reporting the same - // installation directory as Chrome, we should have only one package object. - EXPECT_EQ(1U, installs.packages().size()); - EXPECT_EQ(multi_install, installs.packages()[0]->multi_install()); -} diff --git a/chrome/installer/util/util_constants.cc b/chrome/installer/util/util_constants.cc index f59d6a4..39e47e9 100644 --- a/chrome/installer/util/util_constants.cc +++ b/chrome/installer/util/util_constants.cc @@ -177,4 +177,8 @@ const wchar_t kInstallerResultUIString[] = L"InstallerResultUIString"; const wchar_t kInstallerSuccessLaunchCmdLine[] = L"InstallerSuccessLaunchCmdLine"; +const wchar_t kOptionCeee[] = L"ceee"; +const wchar_t kOptionMultiInstall[] = L"multi-install"; +const wchar_t kOptionReadyMode[] = L"ready-mode"; + } // namespace installer diff --git a/chrome/installer/util/util_constants.h b/chrome/installer/util/util_constants.h index 5b2f230..9ca8900 100644 --- a/chrome/installer/util/util_constants.h +++ b/chrome/installer/util/util_constants.h @@ -66,6 +66,9 @@ enum InstallStatus { // Chrome Frame. READY_MODE_END_TEMP_OPT_OUT_FAILED, // 38. Failed to end temporary opt-out // of Chrome Frame. + CONFLICTING_CHANNEL_EXISTS, // 39. A multi-install product on a different + // update channel exists. + READY_MODE_REQUIRES_CHROME, // 40. Chrome Frame in ready-mode requires Chrome }; namespace switches { @@ -141,6 +144,11 @@ extern const wchar_t kInstallerError[]; extern const wchar_t kInstallerResultUIString[]; extern const wchar_t kInstallerSuccessLaunchCmdLine[]; +// Product options. +extern const wchar_t kOptionCeee[]; +extern const wchar_t kOptionMultiInstall[]; +extern const wchar_t kOptionReadyMode[]; + } // namespace installer #endif // CHROME_INSTALLER_UTIL_UTIL_CONSTANTS_H_ diff --git a/chrome_frame/ready_mode/ready_mode.cc b/chrome_frame/ready_mode/ready_mode.cc index 51df76f..21155f3 100644 --- a/chrome_frame/ready_mode/ready_mode.cc +++ b/chrome_frame/ready_mode/ready_mode.cc @@ -15,8 +15,8 @@ #include "base/win/scoped_bstr.h" #include "base/win/scoped_comptr.h" #include "base/win/win_util.h" +#include "chrome/installer/util/browser_distribution.h" #include "net/base/registry_controlled_domain.h" -#include "chrome/installer/util/package_properties.h" #include "chrome_frame/infobars/infobar_manager.h" #include "chrome_frame/ready_mode/internal/ready_mode_web_browser_adapter.h" #include "chrome_frame/ready_mode/internal/ready_prompt_content.h" @@ -220,11 +220,13 @@ void BrowserObserver::ShowPrompt() { scoped_ptr<RegistryReadyModeState::Observer> ready_mode_state_observer( new StateObserver(weak_ptr_factory_.GetWeakPtr())); - installer::ActivePackageProperties package_properties; + BrowserDistribution* dist = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES); // Owned by infobar_content scoped_ptr<ReadyModeState> ready_mode_state(new RegistryReadyModeState( - package_properties.GetStateKey(), + dist->GetStateKey(), base::TimeDelta::FromMinutes(kTemporaryDeclineDurationMinutes), ready_mode_state_observer.release())); @@ -360,9 +362,12 @@ void Configure(Delegate* chrome_frame, IWebBrowser2* web_browser) { // Take ownership of the delegate linked_ptr<Delegate> delegate(chrome_frame); chrome_frame = NULL; + BrowserDistribution* dist = + BrowserDistribution::GetSpecificDistribution( + BrowserDistribution::CHROME_BINARIES); RegistryReadyModeState ready_mode_state( - installer::ActivePackageProperties().GetStateKey(), + dist->GetStateKey(), base::TimeDelta::FromMinutes(kTemporaryDeclineDurationMinutes), NULL); // NULL => no observer required |