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