| Index: chrome/installer/setup/uninstall.cc
|
| ===================================================================
|
| --- chrome/installer/setup/uninstall.cc (revision 71802)
|
| +++ chrome/installer/setup/uninstall.cc (working copy)
|
| @@ -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 @@
|
| // 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.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,41 +60,41 @@
|
|
|
| // 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.";
|
| + 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->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->multi_install())
|
| + << "Channel value for "
|
| + << BrowserDistribution::GetSpecificDistribution(
|
| + other_dist_type)->GetAppShortCutName()
|
| + << " is somehow already set to the desired new value of "
|
| + << channel_info.value();
|
| + }
|
| }
|
|
|
| - 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.";
|
| - }
|
| -
|
| bool success = update_list->Do();
|
| LOG_IF(ERROR, !success) << "Failed updating channel values.";
|
| }
|
| @@ -175,14 +177,16 @@
|
| // 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 +203,15 @@
|
| // 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 +337,11 @@
|
| 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 +354,33 @@
|
| 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 +390,11 @@
|
| // 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 +402,9 @@
|
| // 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 +418,8 @@
|
| // 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 +436,8 @@
|
| 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 +445,7 @@
|
| // 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.msi()) {
|
| should_delete = true;
|
| } else {
|
| should_delete =
|
| @@ -542,16 +551,16 @@
|
| }
|
| }
|
|
|
| -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 +590,7 @@
|
| 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 +599,8 @@
|
| // 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 +625,10 @@
|
| // 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 +643,34 @@
|
| // 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(original_state, installer_state, setup_path,
|
| + product);
|
| + }
|
|
|
| - if (!is_chrome)
|
| - ProcessChromeFrameWorkItems(setup_path, product, product_state);
|
| + if (installer_state.multi_install())
|
| + ProcessGoogleUpdateItems(original_state, installer_state, product);
|
|
|
| - if (product.package().multi_install())
|
| - ProcessGoogleUpdateItems(original_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 +687,9 @@
|
| // 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 +698,7 @@
|
|
|
| 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 +717,35 @@
|
|
|
| // 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.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.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 +756,9 @@
|
| // 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 +786,3 @@
|
| }
|
|
|
| } // namespace installer
|
| -
|
|
|