| Index: chrome/installer/setup/uninstall.cc
|
| diff --git a/chrome/installer/setup/uninstall.cc b/chrome/installer/setup/uninstall.cc
|
| index fa46341d5eda321e51cfd981365a8ee39d30bc55..788d7936e3959f9b1132f864baf6960dc28c34b7 100644
|
| --- a/chrome/installer/setup/uninstall.cc
|
| +++ b/chrome/installer/setup/uninstall.cc
|
| @@ -52,6 +52,23 @@ using installer::MasterPreferences;
|
|
|
| namespace {
|
|
|
| +// Avoid leaving behind a Temp dir. If one exists, ask SelfCleaningTempDir to
|
| +// clean it up for us. This may involve scheduling it for deletion after
|
| +// reboot. Don't report that a reboot is required in this case, however.
|
| +// TODO(erikwright): Shouldn't this still lead to
|
| +// ScheduleParentAndGrandparentForDeletion?
|
| +void DeleteInstallTempDir(const FilePath& target_path) {
|
| + FilePath temp_path(target_path.DirName().Append(installer::kInstallTempDir));
|
| + if (file_util::DirectoryExists(temp_path)) {
|
| + installer::SelfCleaningTempDir temp_dir;
|
| + if (!temp_dir.Initialize(target_path.DirName(),
|
| + installer::kInstallTempDir) ||
|
| + !temp_dir.Delete()) {
|
| + LOG(ERROR) << "Failed to delete temp dir " << temp_path.value();
|
| + }
|
| + }
|
| +}
|
| +
|
| // Makes appropriate changes to the Google Update "ap" value in the registry.
|
| // Specifically, removes the flags associated with this product ("-chrome" or
|
| // "-chromeframe[-readymode]") from the "ap" values for all other
|
| @@ -75,17 +92,15 @@ void ProcessGoogleUpdateItems(
|
|
|
| // Apply the new channel value to all other products and to the multi package.
|
| if (modified) {
|
| - BrowserDistribution::Type other_dist_types[] = {
|
| - (distribution->GetType() == BrowserDistribution::CHROME_BROWSER) ?
|
| - BrowserDistribution::CHROME_FRAME :
|
| - BrowserDistribution::CHROME_BROWSER,
|
| - BrowserDistribution::CHROME_BINARIES
|
| - };
|
| scoped_ptr<WorkItemList>
|
| update_list(WorkItem::CreateNoRollbackWorkItemList());
|
|
|
| - for (int i = 0; i < arraysize(other_dist_types); ++i) {
|
| - BrowserDistribution::Type other_dist_type = other_dist_types[i];
|
| + for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
|
| + BrowserDistribution::Type other_dist_type =
|
| + static_cast<BrowserDistribution::Type>(i);
|
| + if (distribution->GetType() == other_dist_type)
|
| + continue;
|
| +
|
| product_state =
|
| original_state.GetProductState(system_level, other_dist_type);
|
| // Only modify other products if they're installed and multi.
|
| @@ -120,8 +135,11 @@ void ProcessQuickEnableWorkItems(
|
| scoped_ptr<WorkItemList> work_item_list(
|
| WorkItem::CreateNoRollbackWorkItemList());
|
|
|
| - AddQuickEnableWorkItems(installer_state, machine_state, NULL, NULL,
|
| - work_item_list.get());
|
| + AddQuickEnableChromeFrameWorkItems(installer_state, machine_state, NULL, NULL,
|
| + work_item_list.get());
|
| +
|
| + AddQuickEnableApplicationHostWorkItems(installer_state, machine_state, NULL,
|
| + NULL, work_item_list.get());
|
| if (!work_item_list->Do())
|
| LOG(ERROR) << "Failed to update quick-enable-cf command.";
|
| }
|
| @@ -399,44 +417,80 @@ bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state,
|
| return ret;
|
| }
|
|
|
| -DeleteResult DeleteFilesAndFolders(const InstallerState& installer_state,
|
| - const Version& installed_version) {
|
| +DeleteResult DeleteAppHostFilesAndFolders(const InstallerState& installer_state,
|
| + const Version& installed_version) {
|
| const FilePath& target_path = installer_state.target_path();
|
| if (target_path.empty()) {
|
| - LOG(ERROR) << "DeleteFilesAndFolders: no installation destination path.";
|
| + LOG(ERROR) << "DeleteAppHostFilesAndFolders: no installation destination "
|
| + << "path.";
|
| return DELETE_FAILED; // Nothing else we can do to uninstall, so we return.
|
| }
|
|
|
| + DeleteInstallTempDir(target_path);
|
| +
|
| DeleteResult result = DELETE_SUCCEEDED;
|
|
|
| - // Avoid leaving behind a Temp dir. If one exists, ask SelfCleaningTempDir to
|
| - // clean it up for us. This may involve scheduling it for deletion after
|
| - // reboot. Don't report that a reboot is required in this case, however.
|
| - FilePath temp_path(target_path.DirName().Append(kInstallTempDir));
|
| - if (file_util::DirectoryExists(temp_path)) {
|
| - installer::SelfCleaningTempDir temp_dir;
|
| - if (!temp_dir.Initialize(target_path.DirName(), kInstallTempDir) ||
|
| - !temp_dir.Delete()) {
|
| - LOG(ERROR) << "Failed to delete temp dir " << temp_path.value();
|
| - }
|
| + FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe));
|
| + if (!file_util::Delete(app_host_exe, false)) {
|
| + result = DELETE_FAILED;
|
| + LOG(ERROR) << "Failed to delete path: " << app_host_exe.value();
|
| + } else {
|
| + DeleteEmptyParentDir(target_path);
|
| }
|
|
|
| - VLOG(1) << "Deleting install path " << target_path.value();
|
| - if (!file_util::Delete(target_path, true)) {
|
| - LOG(ERROR) << "Failed to delete folder (1st try): " << 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(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(target_path, true)) {
|
| - LOG(ERROR) << "Failed to delete folder (2nd try): "
|
| - << target_path.value();
|
| - result = DELETE_FAILED;
|
| + return result;
|
| +}
|
| +
|
| +DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state,
|
| + const Version& installed_version) {
|
| + const FilePath& target_path = installer_state.target_path();
|
| + if (target_path.empty()) {
|
| + LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination "
|
| + << "path.";
|
| + return DELETE_FAILED; // Nothing else we can do to uninstall, so we return.
|
| + }
|
| +
|
| + DeleteInstallTempDir(target_path);
|
| +
|
| + DeleteResult result = DELETE_SUCCEEDED;
|
| +
|
| + using file_util::FileEnumerator;
|
| + FileEnumerator file_enumerator(
|
| + target_path,
|
| + false,
|
| + static_cast<FileEnumerator::FileType>(FileEnumerator::FILES |
|
| + FileEnumerator::DIRECTORIES));
|
| + while (true) {
|
| + FilePath to_delete(file_enumerator.Next());
|
| + if (to_delete.empty())
|
| + break;
|
| + if (to_delete.BaseName().value() == installer::kChromeAppHostExe)
|
| + continue;
|
| +
|
| + VLOG(1) << "Deleting install path " << target_path.value();
|
| + if (!file_util::Delete(to_delete, true)) {
|
| + LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.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.
|
| + FileEnumerator::FindInfo find_info;
|
| + file_enumerator.GetFindInfo(&find_info);
|
| + if (FileEnumerator::IsDirectory(find_info))
|
| + ScheduleDirectoryForDeletion(to_delete.value().c_str());
|
| + else
|
| + ScheduleFileSystemEntityForDeletion(to_delete.value().c_str());
|
| + result = DELETE_REQUIRES_REBOOT;
|
| + } else {
|
| + // Try closing any running Chrome processes and deleting files once
|
| + // again.
|
| + CloseAllChromeProcesses();
|
| + if (!file_util::Delete(to_delete, true)) {
|
| + LOG(ERROR) << "Failed to delete path (2nd try): "
|
| + << to_delete.value();
|
| + result = DELETE_FAILED;
|
| + break;
|
| + }
|
| }
|
| }
|
| }
|
| @@ -500,7 +554,7 @@ bool ShouldDeleteProfile(const InstallerState& installer_state,
|
| // 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() && !installer_state.is_msi()) {
|
| + if (product.is_chrome_frame() && !installer_state.is_msi()) {
|
| should_delete = true;
|
| } else {
|
| should_delete =
|
| @@ -889,10 +943,10 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
|
|
|
| auto_launch_util::DisableAllAutoStartFeatures(
|
| ASCIIToUTF16(chrome::kInitialProfile));
|
| - }
|
|
|
| - // First delete shortcuts from Start->Programs, Desktop & Quick Launch.
|
| - DeleteChromeShortcuts(installer_state, product);
|
| + // First delete shortcuts from Start->Programs, Desktop & Quick Launch.
|
| + DeleteChromeShortcuts(installer_state, product);
|
| + }
|
|
|
| // Delete the registry keys (Uninstall key and Version key).
|
| HKEY reg_root = installer_state.root_key();
|
| @@ -901,8 +955,13 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
|
| // product.GetVersionKey().
|
| string16 distribution_data(browser_dist->GetDistributionData(reg_root));
|
|
|
| - // Remove Control Panel uninstall link and Omaha product key.
|
| - InstallUtil::DeleteRegistryKey(reg_root, browser_dist->GetUninstallRegPath());
|
| + // Remove Control Panel uninstall link.
|
| + if (product.ShouldCreateUninstallEntry()) {
|
| + InstallUtil::DeleteRegistryKey(reg_root,
|
| + browser_dist->GetUninstallRegPath());
|
| + }
|
| +
|
| + // Remove Omaha product key.
|
| InstallUtil::DeleteRegistryKey(reg_root, browser_dist->GetVersionKey());
|
|
|
| // Also try to delete the MSI value in the ClientState key (it might not be
|
| @@ -911,63 +970,66 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
|
| // being picked up on reinstall.
|
| product.SetMsiMarker(installer_state.system_install(), false);
|
|
|
| - // Remove all Chrome registration keys.
|
| - // Registration data is put in HKCU for both system level and user level
|
| - // installs.
|
| InstallStatus ret = installer::UNKNOWN_STATUS;
|
| - DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER, suffix,
|
| - installer_state.target_path(), &ret);
|
| -
|
| - // If the user's Chrome is registered with a suffix: it is possible that old
|
| - // unsuffixed registrations were left in HKCU (e.g. if this install was
|
| - // previously installed with no suffix in HKCU (old suffix rules if the user
|
| - // is not an admin (or declined UAC at first run)) and later had to be
|
| - // suffixed when fully registered in HKLM (e.g. when later making Chrome
|
| - // default through the UI)).
|
| - // Remove remaining HKCU entries with no suffix if any.
|
| - if (!suffix.empty()) {
|
| - DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER, string16(),
|
| +
|
| + if (is_chrome) {
|
| + // Remove all Chrome registration keys.
|
| + // Registration data is put in HKCU for both system level and user level
|
| + // installs.
|
| + DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER, suffix,
|
| installer_state.target_path(), &ret);
|
|
|
| - // For similar reasons it is possible in very few installs (from 21.0.1180.0
|
| - // and fixed shortly after) to be installed with the new-style suffix, but
|
| - // have some old-style suffix registrations left behind.
|
| - string16 old_style_suffix;
|
| - if (ShellUtil::GetOldUserSpecificRegistrySuffix(&old_style_suffix) &&
|
| - suffix != old_style_suffix) {
|
| - DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER,
|
| - old_style_suffix,
|
| + // If the user's Chrome is registered with a suffix: it is possible that old
|
| + // unsuffixed registrations were left in HKCU (e.g. if this install was
|
| + // previously installed with no suffix in HKCU (old suffix rules if the user
|
| + // is not an admin (or declined UAC at first run)) and later had to be
|
| + // suffixed when fully registered in HKLM (e.g. when later making Chrome
|
| + // default through the UI)).
|
| + // Remove remaining HKCU entries with no suffix if any.
|
| + if (!suffix.empty()) {
|
| + DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER, string16(),
|
| installer_state.target_path(), &ret);
|
| - }
|
| - }
|
|
|
| - // Chrome is registered in HKLM for all system-level installs and for
|
| - // user-level installs for which Chrome has been made the default browser.
|
| - // Always remove the HKLM registration for system-level installs. For
|
| - // user-level installs, only remove it if both: 1) this uninstall isn't a self
|
| - // destruct following the installation of a system-level Chrome (because the
|
| - // system-level Chrome owns the HKLM registration now), and 2) this user has
|
| - // made Chrome their default browser (i.e. has shell integration entries
|
| - // registered with |suffix| (note: |suffix| will be the empty string if
|
| - // required as it is obtained by GetCurrentInstallationSuffix() above)).
|
| - // TODO(gab): This can still leave parts of a suffixed install behind. To be
|
| - // able to remove them we would need to be able to remove only suffixed
|
| - // entries (as it is now some of the shell integration entries are unsuffixed;
|
| - // thus removing suffixed installs is prohibited in HKLM if !|remove_all| for
|
| - // now).
|
| - if (installer_state.system_install() ||
|
| - (remove_all &&
|
| - ShellUtil::QuickIsChromeRegisteredInHKLM(
|
| - browser_dist, chrome_exe, suffix))) {
|
| - DeleteChromeRegistrationKeys(browser_dist, HKEY_LOCAL_MACHINE, suffix,
|
| - installer_state.target_path(), &ret);
|
| - }
|
| + // For similar reasons it is possible in very few installs (from
|
| + // 21.0.1180.0 and fixed shortly after) to be installed with the new-style
|
| + // suffix, but have some old-style suffix registrations left behind.
|
| + string16 old_style_suffix;
|
| + if (ShellUtil::GetOldUserSpecificRegistrySuffix(&old_style_suffix) &&
|
| + suffix != old_style_suffix) {
|
| + DeleteChromeRegistrationKeys(browser_dist, HKEY_CURRENT_USER,
|
| + old_style_suffix,
|
| + installer_state.target_path(), &ret);
|
| + }
|
| + }
|
|
|
| - ProcessDelegateExecuteWorkItems(installer_state, product);
|
| + // Chrome is registered in HKLM for all system-level installs and for
|
| + // user-level installs for which Chrome has been made the default browser.
|
| + // Always remove the HKLM registration for system-level installs. For
|
| + // user-level installs, only remove it if both: 1) this uninstall isn't a
|
| + // self destruct following the installation of a system-level Chrome
|
| + // (because the system-level Chrome owns the HKLM registration now), and 2)
|
| + // this user has made Chrome their default browser (i.e. has shell
|
| + // integration entries registered with |suffix| (note: |suffix| will be the
|
| + // empty string if required as it is obtained by
|
| + // GetCurrentInstallationSuffix() above)).
|
| + // TODO(gab): This can still leave parts of a suffixed install behind. To be
|
| + // able to remove them we would need to be able to remove only suffixed
|
| + // entries (as it is now some of the shell integration entries are
|
| + // unsuffixed; thus removing suffixed installs is prohibited in HKLM if
|
| + // !|remove_all| for now).
|
| + if (installer_state.system_install() ||
|
| + (remove_all &&
|
| + ShellUtil::QuickIsChromeRegisteredInHKLM(
|
| + browser_dist, chrome_exe, suffix))) {
|
| + DeleteChromeRegistrationKeys(browser_dist, HKEY_LOCAL_MACHINE, suffix,
|
| + installer_state.target_path(), &ret);
|
| + }
|
|
|
| - UninstallActiveSetupEntries(installer_state, product);
|
| + ProcessDelegateExecuteWorkItems(installer_state, product);
|
| + UninstallActiveSetupEntries(installer_state, product);
|
| + }
|
|
|
| - if (!is_chrome) {
|
| + if (product.is_chrome_frame()) {
|
| ProcessChromeFrameWorkItems(original_state, installer_state, setup_path,
|
| product);
|
| }
|
| @@ -1015,7 +1077,7 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
|
| unreg_work_item_list->Do();
|
| }
|
|
|
| - if (!is_chrome)
|
| + if (product.is_chrome_frame())
|
| ProcessIELowRightsPolicyWorkItems(installer_state);
|
| }
|
|
|
| @@ -1035,41 +1097,25 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
|
| 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 = true;
|
| - if (installer_state.is_multi_install()) {
|
| - ProductState prod_state;
|
| - for (size_t i = 0; i < BrowserDistribution::kNumProductTypes; ++i) {
|
| - if (prod_state.Initialize(installer_state.system_install(),
|
| - BrowserDistribution::kProductTypes[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.");
|
| - if (can_delete_files) {
|
| - BrowserDistribution* multi_dist =
|
| - installer_state.multi_package_binaries_distribution();
|
| - InstallUtil::DeleteRegistryKey(reg_root, multi_dist->GetVersionKey());
|
| - }
|
| - }
|
| + // (aka non-multi) installation or we are the Chrome Binaries.
|
|
|
| - FilePath backup_state_file(BackupLocalStateFile(
|
| - GetLocalStateFolder(product)));
|
| + FilePath backup_state_file(
|
| + BackupLocalStateFile(GetLocalStateFolder(product)));
|
|
|
| DeleteResult delete_result = DELETE_SUCCEEDED;
|
| - if (can_delete_files) {
|
| +
|
| + if (product.is_chrome_app_host()) {
|
| + DeleteAppHostFilesAndFolders(installer_state, product_state->version());
|
| + } else if (!installer_state.is_multi_install() ||
|
| + product.is_chrome_binaries()) {
|
| +
|
| // 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(installer_state, setup_path,
|
| product_state->version());
|
| - delete_result = DeleteFilesAndFolders(installer_state,
|
| - product_state->version());
|
| + delete_result = DeleteChromeFilesAndFolders(installer_state,
|
| + product_state->version());
|
| }
|
|
|
| if (delete_profile)
|
| @@ -1082,7 +1128,7 @@ InstallStatus UninstallProduct(const InstallationState& original_state,
|
| }
|
|
|
| if (!force_uninstall) {
|
| - VLOG(1) << "Uninstallation complete. Launching Uninstall survey.";
|
| + VLOG(1) << "Uninstallation complete. Launching post-uninstall operations.";
|
| browser_dist->DoPostUninstallOperations(product_state->version(),
|
| backup_state_file, distribution_data);
|
| }
|
|
|