Home | History | Annotate | Download | only in setup
      1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
      2 // Use of this source code is governed by a BSD-style license that can be
      3 // found in the LICENSE file.
      4 //
      5 // This file defines the methods useful for uninstalling Chrome.
      6 
      7 #include "chrome/installer/setup/uninstall.h"
      8 
      9 #include <windows.h>
     10 
     11 #include <vector>
     12 
     13 #include "base/file_util.h"
     14 #include "base/files/file_enumerator.h"
     15 #include "base/path_service.h"
     16 #include "base/process/kill.h"
     17 #include "base/strings/string16.h"
     18 #include "base/strings/string_number_conversions.h"
     19 #include "base/strings/string_util.h"
     20 #include "base/strings/utf_string_conversions.h"
     21 #include "base/win/registry.h"
     22 #include "base/win/scoped_handle.h"
     23 #include "base/win/shortcut.h"
     24 #include "base/win/windows_version.h"
     25 #include "chrome/common/chrome_constants.h"
     26 #include "chrome/common/chrome_paths_internal.h"
     27 #include "chrome/common/chrome_result_codes.h"
     28 #include "chrome/installer/launcher_support/chrome_launcher_support.h"
     29 #include "chrome/installer/setup/install.h"
     30 #include "chrome/installer/setup/install_worker.h"
     31 #include "chrome/installer/setup/setup_constants.h"
     32 #include "chrome/installer/setup/setup_util.h"
     33 #include "chrome/installer/util/auto_launch_util.h"
     34 #include "chrome/installer/util/browser_distribution.h"
     35 #include "chrome/installer/util/channel_info.h"
     36 #include "chrome/installer/util/delete_after_reboot_helper.h"
     37 #include "chrome/installer/util/google_update_constants.h"
     38 #include "chrome/installer/util/google_update_settings.h"
     39 #include "chrome/installer/util/helper.h"
     40 #include "chrome/installer/util/install_util.h"
     41 #include "chrome/installer/util/installation_state.h"
     42 #include "chrome/installer/util/installer_state.h"
     43 #include "chrome/installer/util/logging_installer.h"
     44 #include "chrome/installer/util/self_cleaning_temp_dir.h"
     45 #include "chrome/installer/util/shell_util.h"
     46 #include "chrome/installer/util/util_constants.h"
     47 #include "content/public/common/result_codes.h"
     48 #include "extensions/common/constants.h"
     49 #include "rlz/lib/rlz_lib.h"
     50 
     51 // Build-time generated include file.
     52 #include "registered_dlls.h"  // NOLINT
     53 
     54 using base::win::RegKey;
     55 using installer::InstallStatus;
     56 using installer::MasterPreferences;
     57 
     58 namespace {
     59 
     60 // Avoid leaving behind a Temp dir.  If one exists, ask SelfCleaningTempDir to
     61 // clean it up for us.  This may involve scheduling it for deletion after
     62 // reboot.  Don't report that a reboot is required in this case, however.
     63 // TODO(erikwright): Shouldn't this still lead to
     64 // ScheduleParentAndGrandparentForDeletion?
     65 void DeleteInstallTempDir(const base::FilePath& target_path) {
     66   base::FilePath temp_path(target_path.DirName().Append(
     67       installer::kInstallTempDir));
     68   if (base::DirectoryExists(temp_path)) {
     69     installer::SelfCleaningTempDir temp_dir;
     70     if (!temp_dir.Initialize(target_path.DirName(),
     71                              installer::kInstallTempDir) ||
     72         !temp_dir.Delete()) {
     73       LOG(ERROR) << "Failed to delete temp dir " << temp_path.value();
     74     }
     75   }
     76 }
     77 
     78 // Iterates over the list of distribution types in |dist_types|, and
     79 // adds to |update_list| the work item to update the corresponding "ap"
     80 // registry value specified in |channel_info|.
     81 void AddChannelValueUpdateWorkItems(
     82     const installer::InstallationState& original_state,
     83     const installer::InstallerState& installer_state,
     84     const installer::ChannelInfo& channel_info,
     85     const std::vector<BrowserDistribution::Type>& dist_types,
     86     WorkItemList* update_list) {
     87   const bool system_level = installer_state.system_install();
     88   const HKEY reg_root = installer_state.root_key();
     89   for (size_t i = 0; i < dist_types.size(); ++i) {
     90     BrowserDistribution::Type dist_type = dist_types[i];
     91     const installer::ProductState* product_state =
     92         original_state.GetProductState(system_level, dist_type);
     93     // Only modify other products if they're installed and multi.
     94     if (product_state != NULL &&
     95         product_state->is_multi_install() &&
     96         !product_state->channel().Equals(channel_info)) {
     97       BrowserDistribution* other_dist =
     98           BrowserDistribution::GetSpecificDistribution(dist_type);
     99       update_list->AddSetRegValueWorkItem(reg_root, other_dist->GetStateKey(),
    100           google_update::kRegApField, channel_info.value(), true);
    101     } else {
    102       LOG_IF(ERROR,
    103              product_state != NULL && product_state->is_multi_install())
    104           << "Channel value for "
    105           << BrowserDistribution::GetSpecificDistribution(
    106                  dist_type)->GetAppShortCutName()
    107           << " is somehow already set to the desired new value of "
    108           << channel_info.value();
    109     }
    110   }
    111 }
    112 
    113 // Makes appropriate changes to the Google Update "ap" value in the registry.
    114 // Specifically, removes the flags associated with this product ("-chrome" or
    115 // "-chromeframe[-readymode]") from the "ap" values for all other
    116 // installed products and for the multi-installer package.
    117 void ProcessGoogleUpdateItems(
    118     const installer::InstallationState& original_state,
    119     const installer::InstallerState& installer_state,
    120     const installer::Product& product) {
    121   DCHECK(installer_state.is_multi_install());
    122   const bool system_level = installer_state.system_install();
    123   BrowserDistribution* distribution = product.distribution();
    124   const installer::ProductState* product_state =
    125       original_state.GetProductState(system_level, distribution->GetType());
    126   DCHECK(product_state != NULL);
    127   installer::ChannelInfo channel_info;
    128 
    129   // Remove product's flags from the channel value.
    130   channel_info.set_value(product_state->channel().value());
    131   const bool modified = product.SetChannelFlags(false, &channel_info);
    132 
    133   // Apply the new channel value to all other products and to the multi package.
    134   if (modified) {
    135     scoped_ptr<WorkItemList>
    136         update_list(WorkItem::CreateNoRollbackWorkItemList());
    137     std::vector<BrowserDistribution::Type> dist_types;
    138     for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
    139       BrowserDistribution::Type other_dist_type =
    140           static_cast<BrowserDistribution::Type>(i);
    141       if (distribution->GetType() != other_dist_type)
    142         dist_types.push_back(other_dist_type);
    143     }
    144     AddChannelValueUpdateWorkItems(original_state, installer_state,
    145                                    channel_info, dist_types,
    146                                    update_list.get());
    147     bool success = update_list->Do();
    148     LOG_IF(ERROR, !success) << "Failed updating channel values.";
    149   }
    150 }
    151 
    152 void ProcessOnOsUpgradeWorkItems(
    153     const installer::InstallerState& installer_state,
    154     const installer::Product& product) {
    155   scoped_ptr<WorkItemList> work_item_list(
    156       WorkItem::CreateNoRollbackWorkItemList());
    157   AddOsUpgradeWorkItems(installer_state, base::FilePath(), Version(), product,
    158                         work_item_list.get());
    159   if (!work_item_list->Do())
    160     LOG(ERROR) << "Failed to remove on-os-upgrade command.";
    161 }
    162 
    163 // Adds or removes the quick-enable-cf command to the binaries' version key in
    164 // the registry as needed.
    165 void ProcessQuickEnableWorkItems(
    166     const installer::InstallerState& installer_state,
    167     const installer::InstallationState& machine_state) {
    168   scoped_ptr<WorkItemList> work_item_list(
    169       WorkItem::CreateNoRollbackWorkItemList());
    170 
    171   AddQuickEnableChromeFrameWorkItems(installer_state, machine_state,
    172                                      base::FilePath(),
    173                                      Version(), work_item_list.get());
    174 
    175   if (!work_item_list->Do())
    176     LOG(ERROR) << "Failed to update quick-enable-cf command.";
    177 }
    178 
    179 void ProcessIELowRightsPolicyWorkItems(
    180     const installer::InstallerState& installer_state) {
    181   scoped_ptr<WorkItemList> work_items(WorkItem::CreateNoRollbackWorkItemList());
    182   AddDeleteOldIELowRightsPolicyWorkItems(installer_state, work_items.get());
    183   work_items->Do();
    184   installer::RefreshElevationPolicy();
    185 }
    186 
    187 void ClearRlzProductState() {
    188   const rlz_lib::AccessPoint points[] = {rlz_lib::CHROME_OMNIBOX,
    189                                          rlz_lib::CHROME_HOME_PAGE,
    190                                          rlz_lib::NO_ACCESS_POINT};
    191 
    192   rlz_lib::ClearProductState(rlz_lib::CHROME, points);
    193 
    194   // If chrome has been reactivated, clear all events for this brand as well.
    195   string16 reactivation_brand_wide;
    196   if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand_wide)) {
    197     std::string reactivation_brand(WideToASCII(reactivation_brand_wide));
    198     rlz_lib::SupplementaryBranding branding(reactivation_brand.c_str());
    199     rlz_lib::ClearProductState(rlz_lib::CHROME, points);
    200   }
    201 }
    202 
    203 // Decides whether setup.exe and the installer archive should be removed based
    204 // on the original and installer states:
    205 // * non-multi product being uninstalled: remove both
    206 // * any multi product left besides App Host: keep both
    207 // * only App Host left: keep setup.exe
    208 void CheckShouldRemoveSetupAndArchive(
    209     const installer::InstallationState& original_state,
    210     const installer::InstallerState& installer_state,
    211     bool* remove_setup,
    212     bool* remove_archive) {
    213   *remove_setup = true;
    214   *remove_archive = true;
    215 
    216   // If any multi-install product is left (other than App Host) we must leave
    217   // the installer and archive. For the App Host, we only leave the installer.
    218   if (!installer_state.is_multi_install()) {
    219     VLOG(1) << "Removing all installer files for a non-multi installation.";
    220   } else {
    221     // Loop through all known products...
    222     for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
    223       BrowserDistribution::Type dist_type =
    224           static_cast<BrowserDistribution::Type>(i);
    225       const installer::ProductState* product_state =
    226           original_state.GetProductState(
    227               installer_state.system_install(), dist_type);
    228       // If the product is installed, in multi mode, and is not part of the
    229       // active uninstallation...
    230       if (product_state && product_state->is_multi_install() &&
    231           !installer_state.FindProduct(dist_type)) {
    232         // setup.exe will not be removed as there is a remaining multi-install
    233         // product.
    234         *remove_setup = false;
    235         // As a special case, we can still remove the actual archive if the
    236         // only remaining product is the App Host.
    237         if (dist_type != BrowserDistribution::CHROME_APP_HOST) {
    238           VLOG(1) << "Keeping all installer files due to a remaining "
    239                   << "multi-install product.";
    240           *remove_archive = false;
    241           return;
    242         }
    243         VLOG(1) << "Keeping setup.exe due to a remaining "
    244                 << "app-host installation.";
    245       }
    246     }
    247     VLOG(1) << "Removing the installer archive.";
    248     if (remove_setup)
    249       VLOG(1) << "Removing setup.exe.";
    250   }
    251 }
    252 
    253 // Removes all files from the installer directory, leaving setup.exe iff
    254 // |remove_setup| is false.
    255 // Returns false in case of an error.
    256 bool RemoveInstallerFiles(const base::FilePath& installer_directory,
    257                           bool remove_setup) {
    258   base::FileEnumerator file_enumerator(
    259       installer_directory,
    260       false,
    261       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
    262   bool success = true;
    263 
    264   base::FilePath setup_exe_base_name(installer::kSetupExe);
    265 
    266   while (true) {
    267     base::FilePath to_delete(file_enumerator.Next());
    268     if (to_delete.empty())
    269       break;
    270     if (!remove_setup && to_delete.BaseName() == setup_exe_base_name)
    271       continue;
    272 
    273     VLOG(1) << "Deleting installer path " << to_delete.value();
    274     if (!base::DeleteFile(to_delete, true)) {
    275       LOG(ERROR) << "Failed to delete path: " << to_delete.value();
    276       success = false;
    277     }
    278   }
    279 
    280   return success;
    281 }
    282 
    283 }  // namespace
    284 
    285 namespace installer {
    286 
    287 // Kills all Chrome processes, immediately.
    288 void CloseAllChromeProcesses() {
    289   base::CleanupProcesses(installer::kChromeExe, base::TimeDelta(),
    290                          content::RESULT_CODE_HUNG, NULL);
    291   base::CleanupProcesses(installer::kNaClExe, base::TimeDelta(),
    292                          content::RESULT_CODE_HUNG, NULL);
    293 }
    294 
    295 // Attempts to close the Chrome Frame helper process by sending WM_CLOSE
    296 // messages to its window, or just killing it if that doesn't work.
    297 void CloseChromeFrameHelperProcess() {
    298   HWND window = FindWindow(installer::kChromeFrameHelperWndClass, NULL);
    299   if (!::IsWindow(window))
    300     return;
    301 
    302   const DWORD kWaitMs = 3000;
    303 
    304   DWORD pid = 0;
    305   ::GetWindowThreadProcessId(window, &pid);
    306   DCHECK_NE(pid, 0U);
    307   base::win::ScopedHandle process(::OpenProcess(SYNCHRONIZE, FALSE, pid));
    308   PLOG_IF(INFO, !process) << "Failed to open process: " << pid;
    309 
    310   bool kill = true;
    311   if (SendMessageTimeout(window, WM_CLOSE, 0, 0, SMTO_BLOCK, kWaitMs, NULL) &&
    312       process) {
    313     VLOG(1) << "Waiting for " << installer::kChromeFrameHelperExe;
    314     DWORD wait = ::WaitForSingleObject(process, kWaitMs);
    315     if (wait != WAIT_OBJECT_0) {
    316       LOG(WARNING) << "Wait for " << installer::kChromeFrameHelperExe
    317                    << " to exit failed or timed out.";
    318     } else {
    319       kill = false;
    320       VLOG(1) << installer::kChromeFrameHelperExe << " exited normally.";
    321     }
    322   }
    323 
    324   if (kill) {
    325     VLOG(1) << installer::kChromeFrameHelperExe << " hung.  Killing.";
    326     base::CleanupProcesses(installer::kChromeFrameHelperExe, base::TimeDelta(),
    327                            content::RESULT_CODE_HUNG, NULL);
    328   }
    329 }
    330 
    331 // Deletes shortcuts at |install_level| from Start menu, Desktop,
    332 // Quick Launch, taskbar, and secondary tiles on the Start Screen (Win8+).
    333 // Only shortcuts pointing to |target_exe| will be removed.
    334 void DeleteShortcuts(const InstallerState& installer_state,
    335                      const Product& product,
    336                      const base::FilePath& target_exe) {
    337   BrowserDistribution* dist = product.distribution();
    338 
    339   // The per-user shortcut for this user, if present on a system-level install,
    340   // has already been deleted in chrome_browser_main_win.cc::DoUninstallTasks().
    341   ShellUtil::ShellChange install_level = installer_state.system_install() ?
    342       ShellUtil::SYSTEM_LEVEL : ShellUtil::CURRENT_USER;
    343 
    344   VLOG(1) << "Deleting Desktop shortcuts.";
    345   if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist,
    346                                   install_level, target_exe)) {
    347     LOG(WARNING) << "Failed to delete Desktop shortcuts.";
    348   }
    349 
    350   VLOG(1) << "Deleting Quick Launch shortcuts.";
    351   if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH,
    352                                   dist, install_level, target_exe)) {
    353     LOG(WARNING) << "Failed to delete Quick Launch shortcuts.";
    354   }
    355 
    356   VLOG(1) << "Deleting Start Menu shortcuts.";
    357   if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_START_MENU, dist,
    358                                   install_level, target_exe)) {
    359     LOG(WARNING) << "Failed to delete Start Menu shortcuts.";
    360   }
    361 
    362   // Unpin all pinned-to-taskbar shortcuts that point to |chrome_exe|.
    363   if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_TASKBAR_PINS,
    364                                   dist, ShellUtil::CURRENT_USER, target_exe)) {
    365     LOG(WARNING) << "Failed to unpin taskbar shortcuts at user-level.";
    366   }
    367 
    368   // Delete the folder of secondary tiles from the start screen for |dist|.
    369   if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS,
    370                                   dist, install_level, target_exe)) {
    371     LOG(WARNING) << "Failed to delete start-screen shortcuts.";
    372   }
    373 }
    374 
    375 bool ScheduleParentAndGrandparentForDeletion(const base::FilePath& path) {
    376   base::FilePath parent_dir = path.DirName();
    377   bool ret = ScheduleFileSystemEntityForDeletion(parent_dir.value().c_str());
    378   if (!ret) {
    379     LOG(ERROR) << "Failed to schedule parent dir for deletion: "
    380                << parent_dir.value();
    381   } else {
    382     base::FilePath grandparent_dir(parent_dir.DirName());
    383     ret = ScheduleFileSystemEntityForDeletion(grandparent_dir.value().c_str());
    384     if (!ret) {
    385       LOG(ERROR) << "Failed to schedule grandparent dir for deletion: "
    386                  << grandparent_dir.value();
    387     }
    388   }
    389   return ret;
    390 }
    391 
    392 enum DeleteResult {
    393   DELETE_SUCCEEDED,
    394   DELETE_NOT_EMPTY,
    395   DELETE_FAILED,
    396   DELETE_REQUIRES_REBOOT,
    397 };
    398 
    399 // Deletes the given directory if it is empty. Returns DELETE_SUCCEEDED if the
    400 // directory is deleted, DELETE_NOT_EMPTY if it is not empty, and DELETE_FAILED
    401 // otherwise.
    402 DeleteResult DeleteEmptyDir(const base::FilePath& path) {
    403   if (!file_util::IsDirectoryEmpty(path))
    404     return DELETE_NOT_EMPTY;
    405 
    406   if (base::DeleteFile(path, true))
    407     return DELETE_SUCCEEDED;
    408 
    409   LOG(ERROR) << "Failed to delete folder: " << path.value();
    410   return DELETE_FAILED;
    411 }
    412 
    413 void GetLocalStateFolders(const Product& product,
    414                           std::vector<base::FilePath>* paths) {
    415   // Obtain the location of the user profile data.
    416   product.GetUserDataPaths(paths);
    417   LOG_IF(ERROR, paths->empty())
    418       << "Could not retrieve user's profile directory.";
    419 }
    420 
    421 // Creates a copy of the local state file and returns a path to the copy.
    422 base::FilePath BackupLocalStateFile(
    423     const std::vector<base::FilePath>& local_state_folders) {
    424   base::FilePath backup;
    425 
    426   // Copy the first local state file that is found.
    427   for (size_t i = 0; i < local_state_folders.size(); ++i) {
    428     const base::FilePath& local_state_folder = local_state_folders[i];
    429     base::FilePath state_file(
    430         local_state_folder.Append(chrome::kLocalStateFilename));
    431     if (!base::PathExists(state_file))
    432       continue;
    433     if (!file_util::CreateTemporaryFile(&backup))
    434       LOG(ERROR) << "Failed to create temporary file for Local State.";
    435     else
    436       base::CopyFile(state_file, backup);
    437     break;
    438   }
    439   return backup;
    440 }
    441 
    442 // Deletes all user data directories for a product.
    443 DeleteResult DeleteLocalState(
    444     const std::vector<base::FilePath>& local_state_folders,
    445     bool schedule_on_failure) {
    446   if (local_state_folders.empty())
    447     return DELETE_SUCCEEDED;
    448 
    449   DeleteResult result = DELETE_SUCCEEDED;
    450   for (size_t i = 0; i < local_state_folders.size(); ++i) {
    451     const base::FilePath& user_local_state = local_state_folders[i];
    452     VLOG(1) << "Deleting user profile " << user_local_state.value();
    453     if (!base::DeleteFile(user_local_state, true)) {
    454       LOG(ERROR) << "Failed to delete user profile dir: "
    455                  << user_local_state.value();
    456       if (schedule_on_failure) {
    457         ScheduleDirectoryForDeletion(user_local_state.value().c_str());
    458         result = DELETE_REQUIRES_REBOOT;
    459       } else {
    460         result = DELETE_FAILED;
    461       }
    462     }
    463   }
    464 
    465   if (result == DELETE_REQUIRES_REBOOT) {
    466     ScheduleParentAndGrandparentForDeletion(local_state_folders[0]);
    467   } else {
    468     const base::FilePath user_data_dir(local_state_folders[0].DirName());
    469     if (!user_data_dir.empty() &&
    470         DeleteEmptyDir(user_data_dir) == DELETE_SUCCEEDED) {
    471       const base::FilePath product_dir(user_data_dir.DirName());
    472       if (!product_dir.empty())
    473         DeleteEmptyDir(product_dir);
    474     }
    475   }
    476 
    477   return result;
    478 }
    479 
    480 // Moves setup to a temporary file, outside of the install folder. Also attempts
    481 // to change the current directory to the TMP directory. On Windows, each
    482 // process has a handle to its CWD. If setup.exe's CWD happens to be within the
    483 // install directory, deletion will fail as a result of the open handle.
    484 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state,
    485                                  const base::FilePath& setup_exe) {
    486   bool ret = false;
    487   base::FilePath tmp_dir;
    488   base::FilePath temp_file;
    489   if (!PathService::Get(base::DIR_TEMP, &tmp_dir)) {
    490     NOTREACHED();
    491   } else if (!file_util::CreateTemporaryFileInDir(tmp_dir, &temp_file)) {
    492     LOG(ERROR) << "Failed to create temporary file for setup.exe.";
    493   } else {
    494     VLOG(1) << "Changing current directory to: " << tmp_dir.value();
    495     if (!file_util::SetCurrentDirectory(tmp_dir))
    496       PLOG(ERROR) << "Failed to change the current directory.";
    497 
    498     VLOG(1) << "Attempting to move setup to: " << temp_file.value();
    499     ret = base::Move(setup_exe, temp_file);
    500     PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value();
    501 
    502     // We cannot delete the file right away, but try to delete it some other
    503     // way. Either with the help of a different process or the system.
    504     if (ret && !base::DeleteFileAfterReboot(temp_file)) {
    505       static const uint32 kDeleteAfterMs = 10 * 1000;
    506       installer::DeleteFileFromTempProcess(temp_file, kDeleteAfterMs);
    507     }
    508   }
    509   return ret;
    510 }
    511 
    512 DeleteResult DeleteApplicationProductAndVendorDirectories(
    513     const base::FilePath& application_directory) {
    514   DeleteResult result(DeleteEmptyDir(application_directory));
    515   if (result == DELETE_SUCCEEDED) {
    516     // Now check and delete if the parent directories are empty
    517     // For example Google\Chrome or Chromium
    518     const base::FilePath product_directory(application_directory.DirName());
    519     if (!product_directory.empty()) {
    520         result = DeleteEmptyDir(product_directory);
    521         if (result == DELETE_SUCCEEDED) {
    522           const base::FilePath vendor_directory(product_directory.DirName());
    523           if (!vendor_directory.empty())
    524             result = DeleteEmptyDir(vendor_directory);
    525         }
    526     }
    527   }
    528   if (result == DELETE_NOT_EMPTY)
    529     result = DELETE_SUCCEEDED;
    530   return result;
    531 }
    532 
    533 DeleteResult DeleteAppHostFilesAndFolders(const InstallerState& installer_state,
    534                                           const Version& installed_version) {
    535   const base::FilePath& target_path = installer_state.target_path();
    536   if (target_path.empty()) {
    537     LOG(ERROR) << "DeleteAppHostFilesAndFolders: no installation destination "
    538                << "path.";
    539     return DELETE_FAILED;  // Nothing else we can do to uninstall, so we return.
    540   }
    541 
    542   DeleteInstallTempDir(target_path);
    543 
    544   DeleteResult result = DELETE_SUCCEEDED;
    545 
    546   base::FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe));
    547   if (!base::DeleteFile(app_host_exe, false)) {
    548     result = DELETE_FAILED;
    549     LOG(ERROR) << "Failed to delete path: " << app_host_exe.value();
    550   }
    551 
    552   return result;
    553 }
    554 
    555 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state,
    556                                          const base::FilePath& setup_exe) {
    557   const base::FilePath& target_path = installer_state.target_path();
    558   if (target_path.empty()) {
    559     LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination "
    560                << "path.";
    561     return DELETE_FAILED;  // Nothing else we can do to uninstall, so we return.
    562   }
    563 
    564   DeleteInstallTempDir(target_path);
    565 
    566   DeleteResult result = DELETE_SUCCEEDED;
    567 
    568   base::FilePath installer_directory;
    569   if (target_path.IsParent(setup_exe))
    570     installer_directory = setup_exe.DirName();
    571 
    572   // Enumerate all the files in target_path recursively (breadth-first).
    573   // We delete a file or folder unless it is a parent/child of the installer
    574   // directory. For parents of the installer directory, we will later recurse
    575   // and delete all the children (that are not also parents/children of the
    576   // installer directory).
    577   base::FileEnumerator file_enumerator(target_path, true,
    578       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
    579   while (true) {
    580     base::FilePath to_delete(file_enumerator.Next());
    581     if (to_delete.empty())
    582       break;
    583     if (to_delete.BaseName().value() == installer::kChromeAppHostExe)
    584       continue;
    585     if (!installer_directory.empty() &&
    586         (to_delete == installer_directory ||
    587          installer_directory.IsParent(to_delete) ||
    588          to_delete.IsParent(installer_directory))) {
    589       continue;
    590     }
    591 
    592     VLOG(1) << "Deleting install path " << to_delete.value();
    593     if (!base::DeleteFile(to_delete, true)) {
    594       LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value();
    595       if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) {
    596         // We don't try killing Chrome processes for Chrome Frame builds since
    597         // that is unlikely to help. Instead, schedule files for deletion and
    598         // return a value that will trigger a reboot prompt.
    599         base::FileEnumerator::FileInfo find_info = file_enumerator.GetInfo();
    600         if (find_info.IsDirectory())
    601           ScheduleDirectoryForDeletion(to_delete.value().c_str());
    602         else
    603           ScheduleFileSystemEntityForDeletion(to_delete.value().c_str());
    604         result = DELETE_REQUIRES_REBOOT;
    605       } else {
    606         // Try closing any running Chrome processes and deleting files once
    607         // again.
    608         CloseAllChromeProcesses();
    609         if (!base::DeleteFile(to_delete, true)) {
    610           LOG(ERROR) << "Failed to delete path (2nd try): "
    611                      << to_delete.value();
    612           result = DELETE_FAILED;
    613           break;
    614         }
    615       }
    616     }
    617   }
    618 
    619   return result;
    620 }
    621 
    622 // This method checks if Chrome is currently running or if the user has
    623 // cancelled the uninstall operation by clicking Cancel on the confirmation
    624 // box that Chrome pops up.
    625 InstallStatus IsChromeActiveOrUserCancelled(
    626     const InstallerState& installer_state,
    627     const Product& product) {
    628   int32 exit_code = content::RESULT_CODE_NORMAL_EXIT;
    629   CommandLine options(CommandLine::NO_PROGRAM);
    630   options.AppendSwitch(installer::switches::kUninstall);
    631 
    632   // Here we want to save user from frustration (in case of Chrome crashes)
    633   // and continue with the uninstallation as long as chrome.exe process exit
    634   // code is NOT one of the following:
    635   // - UNINSTALL_CHROME_ALIVE - chrome.exe is currently running
    636   // - UNINSTALL_USER_CANCEL - User cancelled uninstallation
    637   // - HUNG - chrome.exe was killed by HuntForZombieProcesses() (until we can
    638   //          give this method some brains and not kill chrome.exe launched
    639   //          by us, we will not uninstall if we get this return code).
    640   VLOG(1) << "Launching Chrome to do uninstall tasks.";
    641   if (product.LaunchChromeAndWait(installer_state.target_path(), options,
    642                                   &exit_code)) {
    643     VLOG(1) << "chrome.exe launched for uninstall confirmation returned: "
    644             << exit_code;
    645     if ((exit_code == chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE) ||
    646         (exit_code == chrome::RESULT_CODE_UNINSTALL_USER_CANCEL) ||
    647         (exit_code == content::RESULT_CODE_HUNG))
    648       return installer::UNINSTALL_CANCELLED;
    649 
    650     if (exit_code == chrome::RESULT_CODE_UNINSTALL_DELETE_PROFILE)
    651       return installer::UNINSTALL_DELETE_PROFILE;
    652   } else {
    653     PLOG(ERROR) << "Failed to launch chrome.exe for uninstall confirmation.";
    654   }
    655 
    656   return installer::UNINSTALL_CONFIRMED;
    657 }
    658 
    659 bool ShouldDeleteProfile(const InstallerState& installer_state,
    660                          const CommandLine& cmd_line, InstallStatus status,
    661                          const Product& product) {
    662   bool should_delete = false;
    663 
    664   // Chrome Frame uninstallations always want to delete the profile (we have no
    665   // UI to prompt otherwise and the profile stores no useful data anyway)
    666   // unless they are managed by MSI. MSI uninstalls will explicitly include
    667   // the --delete-profile flag to distinguish them from MSI upgrades.
    668   if (product.is_chrome_frame() && !installer_state.is_msi()) {
    669     should_delete = true;
    670   } else {
    671     should_delete =
    672         status == installer::UNINSTALL_DELETE_PROFILE ||
    673         cmd_line.HasSwitch(installer::switches::kDeleteProfile);
    674   }
    675 
    676   return should_delete;
    677 }
    678 
    679 // Removes XP-era filetype registration making Chrome the default browser.
    680 // MSDN (see http://msdn.microsoft.com/library/windows/desktop/cc144148.aspx)
    681 // tells us not to do this, but certain applications break following
    682 // uninstallation if we don't.
    683 void RemoveFiletypeRegistration(const InstallerState& installer_state,
    684                                 HKEY root,
    685                                 const string16& browser_entry_suffix) {
    686   string16 classes_path(ShellUtil::kRegClasses);
    687   classes_path.push_back(base::FilePath::kSeparators[0]);
    688 
    689   const string16 prog_id(ShellUtil::kChromeHTMLProgId + browser_entry_suffix);
    690 
    691   // Delete each filetype association if it references this Chrome.  Take care
    692   // not to delete the association if it references a system-level install of
    693   // Chrome (only a risk if the suffix is empty).  Don't delete the whole key
    694   // since other apps may have stored data there.
    695   std::vector<const wchar_t*> cleared_assocs;
    696   if (installer_state.system_install() ||
    697       !browser_entry_suffix.empty() ||
    698       !base::win::RegKey(HKEY_LOCAL_MACHINE, (classes_path + prog_id).c_str(),
    699                          KEY_QUERY_VALUE).Valid()) {
    700     InstallUtil::ValueEquals prog_id_pred(prog_id);
    701     for (const wchar_t* const* filetype =
    702          &ShellUtil::kPotentialFileAssociations[0]; *filetype != NULL;
    703          ++filetype) {
    704       if (InstallUtil::DeleteRegistryValueIf(
    705               root, (classes_path + *filetype).c_str(), NULL,
    706               prog_id_pred) == InstallUtil::DELETED) {
    707         cleared_assocs.push_back(*filetype);
    708       }
    709     }
    710   }
    711 
    712   // For all filetype associations in HKLM that have just been removed, attempt
    713   // to restore some reasonable value.  We have no definitive way of knowing
    714   // what handlers are the most appropriate, so we use a fixed mapping based on
    715   // the default values for a fresh install of Windows.
    716   if (root == HKEY_LOCAL_MACHINE) {
    717     string16 assoc;
    718     base::win::RegKey key;
    719 
    720     for (size_t i = 0; i < cleared_assocs.size(); ++i) {
    721       const wchar_t* replacement_prog_id = NULL;
    722       assoc.assign(cleared_assocs[i]);
    723 
    724       // Inelegant, but simpler than a pure data-driven approach.
    725       if (assoc == L".htm" || assoc == L".html")
    726         replacement_prog_id = L"htmlfile";
    727       else if (assoc == L".xht" || assoc == L".xhtml")
    728         replacement_prog_id = L"xhtmlfile";
    729 
    730       if (!replacement_prog_id) {
    731         LOG(WARNING) << "No known replacement ProgID for " << assoc
    732                      << " files.";
    733       } else if (key.Open(HKEY_LOCAL_MACHINE,
    734                           (classes_path + replacement_prog_id).c_str(),
    735                           KEY_QUERY_VALUE) == ERROR_SUCCESS &&
    736                  (key.Open(HKEY_LOCAL_MACHINE, (classes_path + assoc).c_str(),
    737                            KEY_SET_VALUE) != ERROR_SUCCESS ||
    738                   key.WriteValue(NULL, replacement_prog_id) != ERROR_SUCCESS)) {
    739         // The replacement ProgID is registered on the computer but the attempt
    740         // to set it for the filetype failed.
    741         LOG(ERROR) << "Failed to restore system-level filetype association "
    742                    << assoc << " = " << replacement_prog_id;
    743       }
    744     }
    745   }
    746 }
    747 
    748 bool DeleteChromeRegistrationKeys(const InstallerState& installer_state,
    749                                   BrowserDistribution* dist,
    750                                   HKEY root,
    751                                   const string16& browser_entry_suffix,
    752                                   InstallStatus* exit_code) {
    753   DCHECK(exit_code);
    754   if (!dist->CanSetAsDefault()) {
    755     // We should have never set those keys.
    756     return true;
    757   }
    758 
    759   base::FilePath chrome_exe(installer_state.target_path().Append(kChromeExe));
    760 
    761   // Delete Software\Classes\ChromeHTML.
    762   const string16 prog_id(ShellUtil::kChromeHTMLProgId + browser_entry_suffix);
    763   string16 reg_prog_id(ShellUtil::kRegClasses);
    764   reg_prog_id.push_back(base::FilePath::kSeparators[0]);
    765   reg_prog_id.append(prog_id);
    766   InstallUtil::DeleteRegistryKey(root, reg_prog_id);
    767 
    768   // Delete Software\Classes\Chrome.
    769   string16 reg_app_id(ShellUtil::kRegClasses);
    770   reg_app_id.push_back(base::FilePath::kSeparators[0]);
    771   // Append the requested suffix manually here (as ShellUtil::GetBrowserModelId
    772   // would otherwise try to figure out the currently installed suffix).
    773   reg_app_id.append(dist->GetBaseAppId() + browser_entry_suffix);
    774   InstallUtil::DeleteRegistryKey(root, reg_app_id);
    775 
    776   // Delete all Start Menu Internet registrations that refer to this Chrome.
    777   {
    778     using base::win::RegistryKeyIterator;
    779     InstallUtil::ProgramCompare open_command_pred(chrome_exe);
    780     string16 client_name;
    781     string16 client_key;
    782     string16 open_key;
    783     for (RegistryKeyIterator iter(root, ShellUtil::kRegStartMenuInternet);
    784          iter.Valid(); ++iter) {
    785       client_name.assign(iter.Name());
    786       client_key.assign(ShellUtil::kRegStartMenuInternet)
    787           .append(1, L'\\')
    788           .append(client_name);
    789       open_key.assign(client_key).append(ShellUtil::kRegShellOpen);
    790       if (InstallUtil::DeleteRegistryKeyIf(root, client_key, open_key, NULL,
    791               open_command_pred) != InstallUtil::NOT_FOUND) {
    792         // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
    793         // references this Chrome (i.e., if it was made the default browser).
    794         InstallUtil::DeleteRegistryValueIf(
    795             root, ShellUtil::kRegStartMenuInternet, NULL,
    796             InstallUtil::ValueEquals(client_name));
    797         // Also delete the value for the default user if we're operating in
    798         // HKLM.
    799         if (root == HKEY_LOCAL_MACHINE) {
    800           InstallUtil::DeleteRegistryValueIf(
    801               HKEY_USERS,
    802               string16(L".DEFAULT\\").append(
    803                   ShellUtil::kRegStartMenuInternet).c_str(),
    804               NULL, InstallUtil::ValueEquals(client_name));
    805         }
    806       }
    807     }
    808   }
    809 
    810   // Delete Software\RegisteredApplications\Chromium
    811   InstallUtil::DeleteRegistryValue(
    812       root, ShellUtil::kRegRegisteredApplications,
    813       dist->GetBaseAppName() + browser_entry_suffix);
    814 
    815   // Delete the App Paths and Applications keys that let Explorer find Chrome:
    816   // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
    817   string16 app_key(ShellUtil::kRegClasses);
    818   app_key.push_back(base::FilePath::kSeparators[0]);
    819   app_key.append(L"Applications");
    820   app_key.push_back(base::FilePath::kSeparators[0]);
    821   app_key.append(installer::kChromeExe);
    822   InstallUtil::DeleteRegistryKey(root, app_key);
    823 
    824   string16 app_path_key(ShellUtil::kAppPathsRegistryKey);
    825   app_path_key.push_back(base::FilePath::kSeparators[0]);
    826   app_path_key.append(installer::kChromeExe);
    827   InstallUtil::DeleteRegistryKey(root, app_path_key);
    828 
    829   // Cleanup OpenWithList and OpenWithProgids:
    830   // http://msdn.microsoft.com/en-us/library/bb166549
    831   string16 file_assoc_key;
    832   string16 open_with_list_key;
    833   string16 open_with_progids_key;
    834   for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; ++i) {
    835     file_assoc_key.assign(ShellUtil::kRegClasses);
    836     file_assoc_key.push_back(base::FilePath::kSeparators[0]);
    837     file_assoc_key.append(ShellUtil::kPotentialFileAssociations[i]);
    838     file_assoc_key.push_back(base::FilePath::kSeparators[0]);
    839 
    840     open_with_list_key.assign(file_assoc_key);
    841     open_with_list_key.append(L"OpenWithList");
    842     open_with_list_key.push_back(base::FilePath::kSeparators[0]);
    843     open_with_list_key.append(installer::kChromeExe);
    844     InstallUtil::DeleteRegistryKey(root, open_with_list_key);
    845 
    846     open_with_progids_key.assign(file_assoc_key);
    847     open_with_progids_key.append(ShellUtil::kRegOpenWithProgids);
    848     InstallUtil::DeleteRegistryValue(root, open_with_progids_key, prog_id);
    849   }
    850 
    851   // Cleanup in case Chrome had been made the default browser.
    852 
    853   // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
    854   // references this Chrome.  Do this explicitly here for the case where HKCU is
    855   // being processed; the iteration above will have no hits since registration
    856   // lives in HKLM.
    857   InstallUtil::DeleteRegistryValueIf(
    858       root, ShellUtil::kRegStartMenuInternet, NULL,
    859       InstallUtil::ValueEquals(dist->GetBaseAppName() + browser_entry_suffix));
    860 
    861   // Delete each protocol association if it references this Chrome.
    862   InstallUtil::ProgramCompare open_command_pred(chrome_exe);
    863   string16 parent_key(ShellUtil::kRegClasses);
    864   parent_key.push_back(base::FilePath::kSeparators[0]);
    865   const string16::size_type base_length = parent_key.size();
    866   string16 child_key;
    867   for (const wchar_t* const* proto =
    868            &ShellUtil::kPotentialProtocolAssociations[0];
    869        *proto != NULL;
    870        ++proto) {
    871     parent_key.resize(base_length);
    872     parent_key.append(*proto);
    873     child_key.assign(parent_key).append(ShellUtil::kRegShellOpen);
    874     InstallUtil::DeleteRegistryKeyIf(root, parent_key, child_key, NULL,
    875                                      open_command_pred);
    876   }
    877 
    878   RemoveFiletypeRegistration(installer_state, root, browser_entry_suffix);
    879 
    880   *exit_code = installer::UNINSTALL_SUCCESSFUL;
    881   return true;
    882 }
    883 
    884 void RemoveChromeLegacyRegistryKeys(BrowserDistribution* dist,
    885                                     const string16& chrome_exe) {
    886   // We used to register Chrome to handle crx files, but this turned out
    887   // to be not worth the hassle. Remove these old registry entries if
    888   // they exist. See: http://codereview.chromium.org/210007
    889 
    890 #if defined(GOOGLE_CHROME_BUILD)
    891 const wchar_t kChromeExtProgId[] = L"ChromeExt";
    892 #else
    893 const wchar_t kChromeExtProgId[] = L"ChromiumExt";
    894 #endif
    895 
    896   HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
    897   for (size_t i = 0; i < arraysize(roots); ++i) {
    898     string16 suffix;
    899     if (roots[i] == HKEY_LOCAL_MACHINE)
    900       suffix = ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe);
    901 
    902     // Delete Software\Classes\ChromeExt,
    903     string16 ext_prog_id(ShellUtil::kRegClasses);
    904     ext_prog_id.push_back(base::FilePath::kSeparators[0]);
    905     ext_prog_id.append(kChromeExtProgId);
    906     ext_prog_id.append(suffix);
    907     InstallUtil::DeleteRegistryKey(roots[i], ext_prog_id);
    908 
    909     // Delete Software\Classes\.crx,
    910     string16 ext_association(ShellUtil::kRegClasses);
    911     ext_association.append(L"\\");
    912     ext_association.append(extensions::kExtensionFileExtension);
    913     InstallUtil::DeleteRegistryKey(roots[i], ext_association);
    914   }
    915 }
    916 
    917 // Builds and executes a work item list to remove DelegateExecute verb handler
    918 // work items for |product|.  This will be a noop for products whose
    919 // corresponding BrowserDistribution implementations do not publish a CLSID via
    920 // GetCommandExecuteImplClsid.
    921 bool ProcessDelegateExecuteWorkItems(const InstallerState& installer_state,
    922                                      const Product& product) {
    923   scoped_ptr<WorkItemList> item_list(WorkItem::CreateNoRollbackWorkItemList());
    924   AddDelegateExecuteWorkItems(installer_state, base::FilePath(), Version(),
    925                               product, item_list.get());
    926   return item_list->Do();
    927 }
    928 
    929 // Removes Active Setup entries from the registry. This cannot be done through
    930 // a work items list as usual because of different paths based on conditionals,
    931 // but otherwise respects the no rollback/best effort uninstall mentality.
    932 // This will only apply for system-level installs of Chrome/Chromium and will be
    933 // a no-op for all other types of installs.
    934 void UninstallActiveSetupEntries(const InstallerState& installer_state,
    935                                  const Product& product) {
    936   VLOG(1) << "Uninstalling registry entries for ActiveSetup.";
    937   BrowserDistribution* distribution = product.distribution();
    938 
    939   if (!product.is_chrome() || !installer_state.system_install()) {
    940     const char* install_level =
    941         installer_state.system_install() ? "system" : "user";
    942     VLOG(1) << "No Active Setup processing to do for " << install_level
    943             << "-level " << distribution->GetAppShortCutName();
    944     return;
    945   }
    946 
    947   const string16 active_setup_path(
    948       InstallUtil::GetActiveSetupPath(distribution));
    949   InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, active_setup_path);
    950 
    951   // Windows leaves keys behind in HKCU\\Software\\(Wow6432Node\\)?Microsoft\\
    952   //     Active Setup\\Installed Components\\{guid}
    953   // for every user that logged in since system-level Chrome was installed.
    954   // This is a problem because Windows compares the value of the Version subkey
    955   // in there with the value of the Version subkey in the matching HKLM entries
    956   // before running Chrome's Active Setup so if Chrome was to be reinstalled
    957   // with a lesser version (e.g. switching back to a more stable channel), the
    958   // affected users would not have Chrome's Active Setup called until Chrome
    959   // eventually updated passed that user's registered Version.
    960   //
    961   // It is however very hard to delete those values as the registry hives for
    962   // other users are not loaded by default under HKEY_USERS (unless a user is
    963   // logged on or has a process impersonating him).
    964   //
    965   // Following our best effort uninstall practices, try to delete the value in
    966   // all users hives. If a given user's hive is not loaded, try to load it to
    967   // proceed with the deletion (failure to do so is ignored).
    968 
    969   static const wchar_t kProfileList[] =
    970       L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\";
    971 
    972   // Windows automatically adds Wow6432Node when creating/deleting the HKLM key,
    973   // but doesn't seem to do so when manually deleting the user-level keys it
    974   // created.
    975   string16 alternate_active_setup_path(active_setup_path);
    976   alternate_active_setup_path.insert(arraysize("Software\\") - 1,
    977                                      L"Wow6432Node\\");
    978 
    979   // These two privileges are required by RegLoadKey() and RegUnloadKey() below.
    980   ScopedTokenPrivilege se_restore_name_privilege(SE_RESTORE_NAME);
    981   ScopedTokenPrivilege se_backup_name_privilege(SE_BACKUP_NAME);
    982   if (!se_restore_name_privilege.is_enabled() ||
    983       !se_backup_name_privilege.is_enabled()) {
    984     // This is not a critical failure as those privileges aren't required to
    985     // clean hives that are already loaded, but attempts to LoadRegKey() below
    986     // will fail.
    987     LOG(WARNING) << "Failed to enable privileges required to load registry "
    988                     "hives.";
    989   }
    990 
    991   for (base::win::RegistryKeyIterator it(HKEY_LOCAL_MACHINE, kProfileList);
    992        it.Valid(); ++it) {
    993     const wchar_t* profile_sid = it.Name();
    994 
    995     // First check if this user's registry hive needs to be loaded in
    996     // HKEY_USERS.
    997     base::win::RegKey user_reg_root_probe(
    998         HKEY_USERS, profile_sid, KEY_READ);
    999     bool loaded_hive = false;
   1000     if (!user_reg_root_probe.Valid()) {
   1001       VLOG(1) << "Attempting to load registry hive for " << profile_sid;
   1002 
   1003       string16 reg_profile_info_path(kProfileList);
   1004       reg_profile_info_path.append(profile_sid);
   1005       base::win::RegKey reg_profile_info_key(
   1006           HKEY_LOCAL_MACHINE, reg_profile_info_path.c_str(), KEY_READ);
   1007 
   1008       string16 profile_path;
   1009       LONG result = reg_profile_info_key.ReadValue(L"ProfileImagePath",
   1010                                                    &profile_path);
   1011       if (result != ERROR_SUCCESS) {
   1012         LOG(ERROR) << "Error reading ProfileImagePath: " << result;
   1013         continue;
   1014       }
   1015       base::FilePath registry_hive_file(profile_path);
   1016       registry_hive_file = registry_hive_file.AppendASCII("NTUSER.DAT");
   1017 
   1018       result = RegLoadKey(HKEY_USERS, profile_sid,
   1019                           registry_hive_file.value().c_str());
   1020       if (result != ERROR_SUCCESS) {
   1021         LOG(ERROR) << "Error loading registry hive: " << result;
   1022         continue;
   1023       }
   1024 
   1025       VLOG(1) << "Loaded registry hive for " << profile_sid;
   1026       loaded_hive = true;
   1027     }
   1028 
   1029     base::win::RegKey user_reg_root(
   1030         HKEY_USERS, profile_sid, KEY_ALL_ACCESS);
   1031 
   1032     LONG result = user_reg_root.DeleteKey(active_setup_path.c_str());
   1033     if (result != ERROR_SUCCESS) {
   1034       result = user_reg_root.DeleteKey(alternate_active_setup_path.c_str());
   1035       if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
   1036         LOG(ERROR) << "Failed to delete key at " << active_setup_path
   1037                    << " and at " << alternate_active_setup_path
   1038                    << ", result: " << result;
   1039       }
   1040     }
   1041 
   1042     if (loaded_hive) {
   1043       user_reg_root.Close();
   1044       if (RegUnLoadKey(HKEY_USERS, profile_sid) == ERROR_SUCCESS)
   1045         VLOG(1) << "Unloaded registry hive for " << profile_sid;
   1046       else
   1047         LOG(ERROR) << "Error unloading registry hive for " << profile_sid;
   1048     }
   1049   }
   1050 }
   1051 
   1052 bool ProcessChromeFrameWorkItems(const InstallationState& original_state,
   1053                                  const InstallerState& installer_state,
   1054                                  const base::FilePath& setup_path,
   1055                                  const Product& product) {
   1056   if (!product.is_chrome_frame())
   1057     return false;
   1058 
   1059   scoped_ptr<WorkItemList> item_list(WorkItem::CreateNoRollbackWorkItemList());
   1060   AddChromeFrameWorkItems(original_state, installer_state, setup_path,
   1061                           Version(), product, item_list.get());
   1062   return item_list->Do();
   1063 }
   1064 
   1065 InstallStatus UninstallProduct(const InstallationState& original_state,
   1066                                const InstallerState& installer_state,
   1067                                const base::FilePath& setup_path,
   1068                                const Product& product,
   1069                                bool remove_all,
   1070                                bool force_uninstall,
   1071                                const CommandLine& cmd_line) {
   1072   InstallStatus status = installer::UNINSTALL_CONFIRMED;
   1073   BrowserDistribution* browser_dist = product.distribution();
   1074   const string16 chrome_exe(
   1075       installer_state.target_path().Append(installer::kChromeExe).value());
   1076 
   1077   const string16 suffix(ShellUtil::GetCurrentInstallationSuffix(browser_dist,
   1078                                                                 chrome_exe));
   1079 
   1080   bool is_chrome = product.is_chrome();
   1081 
   1082   VLOG(1) << "UninstallProduct: " << browser_dist->GetAppShortCutName();
   1083 
   1084   if (force_uninstall) {
   1085     // Since --force-uninstall command line option is used, we are going to
   1086     // do silent uninstall. Try to close all running Chrome instances.
   1087     // NOTE: We don't do this for Chrome Frame.
   1088     if (is_chrome)
   1089       CloseAllChromeProcesses();
   1090   } else if (is_chrome) {
   1091     // no --force-uninstall so lets show some UI dialog boxes.
   1092     status = IsChromeActiveOrUserCancelled(installer_state, product);
   1093     if (status != installer::UNINSTALL_CONFIRMED &&
   1094         status != installer::UNINSTALL_DELETE_PROFILE)
   1095       return status;
   1096 
   1097     // Check if we need admin rights to cleanup HKLM (the conditions for
   1098     // requiring a cleanup are the same as the conditions to do the actual
   1099     // cleanup where DeleteChromeRegistrationKeys() is invoked for
   1100     // HKEY_LOCAL_MACHINE below). If we do, try to launch another uninstaller
   1101     // (silent) in elevated mode to do HKLM cleanup.
   1102     // And continue uninstalling in the current process also to do HKCU cleanup.
   1103     if (remove_all &&
   1104         ShellUtil::QuickIsChromeRegisteredInHKLM(
   1105             browser_dist, chrome_exe, suffix) &&
   1106         !::IsUserAnAdmin() &&
   1107         base::win::GetVersion() >= base::win::VERSION_VISTA &&
   1108         !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
   1109       CommandLine new_cmd(CommandLine::NO_PROGRAM);
   1110       new_cmd.AppendArguments(cmd_line, true);
   1111       // Append --run-as-admin flag to let the new instance of setup.exe know
   1112       // that we already tried to launch ourselves as admin.
   1113       new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
   1114       // Append --remove-chrome-registration to remove registry keys only.
   1115       new_cmd.AppendSwitch(installer::switches::kRemoveChromeRegistration);
   1116       if (!suffix.empty()) {
   1117         new_cmd.AppendSwitchNative(
   1118             installer::switches::kRegisterChromeBrowserSuffix, suffix);
   1119       }
   1120       DWORD exit_code = installer::UNKNOWN_STATUS;
   1121       InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
   1122     }
   1123   }
   1124 
   1125   if (is_chrome) {
   1126     // Chrome is not in use so lets uninstall Chrome by deleting various files
   1127     // and registry entries. Here we will just make best effort and keep going
   1128     // in case of errors.
   1129     ClearRlzProductState();
   1130     // Delete the key that delegate_execute might make.
   1131     if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
   1132       InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER,
   1133                                      chrome::kMetroRegistryPath);
   1134     }
   1135 
   1136     auto_launch_util::DisableAllAutoStartFeatures(
   1137         ASCIIToUTF16(chrome::kInitialProfile));
   1138 
   1139     DeleteShortcuts(installer_state, product, base::FilePath(chrome_exe));
   1140 
   1141   } else if (product.is_chrome_app_host()) {
   1142     const base::FilePath app_host_exe(
   1143         installer_state.target_path().Append(installer::kChromeAppHostExe));
   1144     DeleteShortcuts(installer_state, product, app_host_exe);
   1145   }
   1146 
   1147   // Delete the registry keys (Uninstall key and Version key).
   1148   HKEY reg_root = installer_state.root_key();
   1149 
   1150   // Note that we must retrieve the distribution-specific data before deleting
   1151   // product.GetVersionKey().
   1152   string16 distribution_data(browser_dist->GetDistributionData(reg_root));
   1153 
   1154   // Remove Control Panel uninstall link.
   1155   if (product.ShouldCreateUninstallEntry()) {
   1156     InstallUtil::DeleteRegistryKey(reg_root,
   1157                                    browser_dist->GetUninstallRegPath());
   1158   }
   1159 
   1160   // Remove Omaha product key.
   1161   InstallUtil::DeleteRegistryKey(reg_root, browser_dist->GetVersionKey());
   1162 
   1163   // Also try to delete the MSI value in the ClientState key (it might not be
   1164   // there). This is due to a Google Update behaviour where an uninstall and a
   1165   // rapid reinstall might result in stale values from the old ClientState key
   1166   // being picked up on reinstall.
   1167   product.SetMsiMarker(installer_state.system_install(), false);
   1168 
   1169   InstallStatus ret = installer::UNKNOWN_STATUS;
   1170 
   1171   if (is_chrome) {
   1172     // Remove all Chrome registration keys.
   1173     // Registration data is put in HKCU for both system level and user level
   1174     // installs.
   1175     DeleteChromeRegistrationKeys(installer_state, browser_dist,
   1176                                  HKEY_CURRENT_USER, suffix, &ret);
   1177 
   1178     // If the user's Chrome is registered with a suffix: it is possible that old
   1179     // unsuffixed registrations were left in HKCU (e.g. if this install was
   1180     // previously installed with no suffix in HKCU (old suffix rules if the user
   1181     // is not an admin (or declined UAC at first run)) and later had to be
   1182     // suffixed when fully registered in HKLM (e.g. when later making Chrome
   1183     // default through the UI)).
   1184     // Remove remaining HKCU entries with no suffix if any.
   1185     if (!suffix.empty()) {
   1186       DeleteChromeRegistrationKeys(installer_state, browser_dist,
   1187                                    HKEY_CURRENT_USER, string16(), &ret);
   1188 
   1189       // For similar reasons it is possible in very few installs (from
   1190       // 21.0.1180.0 and fixed shortly after) to be installed with the new-style
   1191       // suffix, but have some old-style suffix registrations left behind.
   1192       string16 old_style_suffix;
   1193       if (ShellUtil::GetOldUserSpecificRegistrySuffix(&old_style_suffix) &&
   1194           suffix != old_style_suffix) {
   1195         DeleteChromeRegistrationKeys(installer_state, browser_dist,
   1196                                      HKEY_CURRENT_USER, old_style_suffix, &ret);
   1197       }
   1198     }
   1199 
   1200     // Chrome is registered in HKLM for all system-level installs and for
   1201     // user-level installs for which Chrome has been made the default browser.
   1202     // Always remove the HKLM registration for system-level installs.  For
   1203     // user-level installs, only remove it if both: 1) this uninstall isn't a
   1204     // self destruct following the installation of a system-level Chrome
   1205     // (because the system-level Chrome owns the HKLM registration now), and 2)
   1206     // this user has made Chrome their default browser (i.e. has shell
   1207     // integration entries registered with |suffix| (note: |suffix| will be the
   1208     // empty string if required as it is obtained by
   1209     // GetCurrentInstallationSuffix() above)).
   1210     // TODO(gab): This can still leave parts of a suffixed install behind. To be
   1211     // able to remove them we would need to be able to remove only suffixed
   1212     // entries (as it is now some of the registry entries (e.g. App Paths) are
   1213     // unsuffixed; thus removing suffixed installs is prohibited in HKLM if
   1214     // !|remove_all| for now).
   1215     if (installer_state.system_install() ||
   1216         (remove_all &&
   1217          ShellUtil::QuickIsChromeRegisteredInHKLM(
   1218              browser_dist, chrome_exe, suffix))) {
   1219       DeleteChromeRegistrationKeys(installer_state, browser_dist,
   1220                                    HKEY_LOCAL_MACHINE, suffix, &ret);
   1221     }
   1222 
   1223     ProcessDelegateExecuteWorkItems(installer_state, product);
   1224 
   1225     ProcessOnOsUpgradeWorkItems(installer_state, product);
   1226 
   1227     UninstallActiveSetupEntries(installer_state, product);
   1228 
   1229     // Notify the shell that associations have changed since Chrome was likely
   1230     // unregistered.
   1231     SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
   1232 
   1233     // TODO(huangs): Implement actual migration code and remove the hack below.
   1234     // Remove the "shadow" App Launcher registry keys.
   1235     if (installer_state.is_multi_install()) {
   1236       // If we're not uninstalling the legacy App Launcher, and if it was
   1237       // not installed in the first place, then delete the "shadow" keys.
   1238       chrome_launcher_support::InstallationState level_to_check =
   1239           installer_state.system_install() ?
   1240               chrome_launcher_support::INSTALLED_AT_SYSTEM_LEVEL :
   1241               chrome_launcher_support::INSTALLED_AT_USER_LEVEL;
   1242       bool has_legacy_app_launcher = level_to_check ==
   1243           chrome_launcher_support::GetAppLauncherInstallationState();
   1244       if (!has_legacy_app_launcher) {
   1245         BrowserDistribution* shadow_app_launcher_dist =
   1246             BrowserDistribution::GetSpecificDistribution(
   1247                 BrowserDistribution::CHROME_APP_HOST);
   1248         InstallUtil::DeleteRegistryKey(reg_root,
   1249             shadow_app_launcher_dist->GetVersionKey());
   1250       }
   1251     }
   1252   }
   1253 
   1254   if (product.is_chrome_frame()) {
   1255     ProcessChromeFrameWorkItems(original_state, installer_state, setup_path,
   1256                                 product);
   1257   }
   1258 
   1259   if (installer_state.is_multi_install()) {
   1260     ProcessGoogleUpdateItems(original_state, installer_state, product);
   1261 
   1262     ProcessQuickEnableWorkItems(installer_state, original_state);
   1263   }
   1264 
   1265   // Get the state of the installed product (if any)
   1266   const ProductState* product_state =
   1267       original_state.GetProductState(installer_state.system_install(),
   1268                                      browser_dist->GetType());
   1269 
   1270   // Delete shared registry keys as well (these require admin rights) if
   1271   // remove_all option is specified.
   1272   if (remove_all) {
   1273     if (!InstallUtil::IsChromeSxSProcess() && is_chrome) {
   1274       // Delete media player registry key that exists only in HKLM.
   1275       // We don't delete this key in SxS uninstall or Chrome Frame uninstall
   1276       // as we never set the key for those products.
   1277       string16 reg_path(installer::kMediaPlayerRegPath);
   1278       reg_path.push_back(base::FilePath::kSeparators[0]);
   1279       reg_path.append(installer::kChromeExe);
   1280       InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path);
   1281     }
   1282 
   1283     // Unregister any dll servers that we may have registered for this
   1284     // product.
   1285     if (product_state != NULL) {
   1286       std::vector<base::FilePath> com_dll_list;
   1287       product.AddComDllList(&com_dll_list);
   1288       base::FilePath dll_folder = installer_state.target_path().AppendASCII(
   1289           product_state->version().GetString());
   1290 
   1291       scoped_ptr<WorkItemList> unreg_work_item_list(
   1292           WorkItem::CreateWorkItemList());
   1293 
   1294       AddRegisterComDllWorkItems(dll_folder,
   1295                                  com_dll_list,
   1296                                  installer_state.system_install(),
   1297                                  false,  // Unregister
   1298                                  true,   // May fail
   1299                                  unreg_work_item_list.get());
   1300       unreg_work_item_list->Do();
   1301     }
   1302 
   1303     if (product.is_chrome_frame())
   1304       ProcessIELowRightsPolicyWorkItems(installer_state);
   1305   }
   1306 
   1307   // Close any Chrome Frame helper processes that may be running.
   1308   if (product.is_chrome_frame()) {
   1309     VLOG(1) << "Closing the Chrome Frame helper process";
   1310     CloseChromeFrameHelperProcess();
   1311   }
   1312 
   1313   if (product_state == NULL)
   1314     return installer::UNINSTALL_SUCCESSFUL;
   1315 
   1316   // Finally delete all the files from Chrome folder after moving setup.exe
   1317   // and the user's Local State to a temp location.
   1318   bool delete_profile = ShouldDeleteProfile(installer_state, cmd_line, status,
   1319                                             product);
   1320   ret = installer::UNINSTALL_SUCCESSFUL;
   1321 
   1322   // When deleting files, we must make sure that we're either a "single"
   1323   // (aka non-multi) installation or we are the Chrome Binaries.
   1324 
   1325   std::vector<base::FilePath> local_state_folders;
   1326   GetLocalStateFolders(product, &local_state_folders);
   1327   base::FilePath backup_state_file(BackupLocalStateFile(local_state_folders));
   1328 
   1329   if (product.is_chrome_app_host()) {
   1330     DeleteAppHostFilesAndFolders(installer_state, product_state->version());
   1331   } else if (!installer_state.is_multi_install() ||
   1332              product.is_chrome_binaries()) {
   1333     base::FilePath setup_exe(base::MakeAbsoluteFilePath(cmd_line.GetProgram()));
   1334     DeleteResult delete_result = DeleteChromeFilesAndFolders(
   1335         installer_state, setup_exe);
   1336     if (delete_result == DELETE_FAILED) {
   1337       ret = installer::UNINSTALL_FAILED;
   1338     } else if (delete_result == DELETE_REQUIRES_REBOOT) {
   1339       ret = installer::UNINSTALL_REQUIRES_REBOOT;
   1340     }
   1341   }
   1342 
   1343   if (delete_profile)
   1344     DeleteLocalState(local_state_folders, product.is_chrome_frame());
   1345 
   1346   if (!force_uninstall) {
   1347     VLOG(1) << "Uninstallation complete. Launching post-uninstall operations.";
   1348     browser_dist->DoPostUninstallOperations(product_state->version(),
   1349         backup_state_file, distribution_data);
   1350   }
   1351 
   1352   // Try and delete the preserved local state once the post-install
   1353   // operations are complete.
   1354   if (!backup_state_file.empty())
   1355     base::DeleteFile(backup_state_file, false);
   1356 
   1357   return ret;
   1358 }
   1359 
   1360 void CleanUpInstallationDirectoryAfterUninstall(
   1361     const InstallationState& original_state,
   1362     const InstallerState& installer_state,
   1363     const CommandLine& cmd_line,
   1364     installer::InstallStatus* uninstall_status) {
   1365   if (*uninstall_status != installer::UNINSTALL_SUCCESSFUL &&
   1366       *uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT) {
   1367     return;
   1368   }
   1369   const base::FilePath target_path(installer_state.target_path());
   1370   if (target_path.empty()) {
   1371     LOG(ERROR) << "No installation destination path.";
   1372     *uninstall_status = installer::UNINSTALL_FAILED;
   1373     return;
   1374   }
   1375   base::FilePath setup_exe(base::MakeAbsoluteFilePath(cmd_line.GetProgram()));
   1376   if (!target_path.IsParent(setup_exe)) {
   1377     LOG(INFO) << "setup.exe is not in target path. Skipping installer cleanup.";
   1378     return;
   1379   }
   1380   base::FilePath install_directory(setup_exe.DirName());
   1381 
   1382   bool remove_setup = true;
   1383   bool remove_archive = true;
   1384   CheckShouldRemoveSetupAndArchive(original_state, installer_state,
   1385                                    &remove_setup, &remove_archive);
   1386   if (!remove_archive)
   1387     return;
   1388 
   1389   if (remove_setup) {
   1390     // In order to be able to remove the folder in which we're running, we
   1391     // need to move setup.exe out of the install folder.
   1392     // TODO(tommi): What if the temp folder is on a different volume?
   1393     MoveSetupOutOfInstallFolder(installer_state, setup_exe);
   1394   }
   1395 
   1396   // Remove files from "...\<product>\Application\<version>\Installer"
   1397   if (!RemoveInstallerFiles(install_directory, remove_setup)) {
   1398     *uninstall_status = installer::UNINSTALL_FAILED;
   1399     return;
   1400   }
   1401 
   1402   if (!remove_setup)
   1403     return;
   1404 
   1405   // Try to remove the empty directory hierarchy.
   1406 
   1407   // Delete "...\<product>\Application\<version>\Installer"
   1408   if (DeleteEmptyDir(install_directory) != DELETE_SUCCEEDED) {
   1409     *uninstall_status = installer::UNINSTALL_FAILED;
   1410     return;
   1411   }
   1412 
   1413   // Delete "...\<product>\Application\<version>"
   1414   DeleteResult delete_result = DeleteEmptyDir(install_directory.DirName());
   1415   if (delete_result == DELETE_FAILED ||
   1416       (delete_result == DELETE_NOT_EMPTY &&
   1417        *uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT)) {
   1418     *uninstall_status = installer::UNINSTALL_FAILED;
   1419     return;
   1420   }
   1421 
   1422   if (*uninstall_status == installer::UNINSTALL_REQUIRES_REBOOT) {
   1423     // Delete the Application directory at reboot if empty.
   1424     ScheduleFileSystemEntityForDeletion(target_path.value().c_str());
   1425 
   1426     // If we need a reboot to continue, schedule the parent directories for
   1427     // deletion unconditionally. If they are not empty, the session manager
   1428     // will not delete them on reboot.
   1429     ScheduleParentAndGrandparentForDeletion(target_path);
   1430   } else if (DeleteApplicationProductAndVendorDirectories(target_path) ==
   1431              installer::DELETE_FAILED) {
   1432     *uninstall_status = installer::UNINSTALL_FAILED;
   1433   }
   1434 }
   1435 
   1436 }  // namespace installer
   1437