uninstall.cc revision 68043e1e95eeb07d5cae7aca370b26518b0867d6
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
54using base::win::RegKey;
55using installer::InstallStatus;
56using installer::MasterPreferences;
57
58namespace {
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?
65void 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|.
81void 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)->GetDisplayName()
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.
117void 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
152void 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.
165void 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
179void 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
187void 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
208void 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.
256bool 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
285namespace installer {
286
287// Kills all Chrome processes, immediately.
288void 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.
297void 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// Updates shortcuts to |old_target_exe| that have non-empty args, making them
332// target |new_target_exe| instead. The non-empty args requirement is a
333// heuristic to determine whether a shortcut is "user-generated". This routine
334// can only be called for user-level installs.
335void RetargetUserShortcutsWithArgs(const InstallerState& installer_state,
336                                   const Product& product,
337                                   const base::FilePath& old_target_exe,
338                                   const base::FilePath& new_target_exe) {
339  if (installer_state.system_install()) {
340    NOTREACHED();
341    return;
342  }
343  BrowserDistribution* dist = product.distribution();
344  ShellUtil::ShellChange install_level = ShellUtil::CURRENT_USER;
345  ShellUtil::ShortcutProperties updated_properties(install_level);
346  updated_properties.set_target(new_target_exe);
347
348  // TODO(huangs): Make this data-driven, along with DeleteShortcuts().
349  VLOG(1) << "Retargeting Desktop shortcuts.";
350  if (!ShellUtil::UpdateShortcutsWithArgs(
351          ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist, install_level,
352          old_target_exe, updated_properties)) {
353    LOG(WARNING) << "Failed to retarget Desktop shortcuts.";
354  }
355
356  VLOG(1) << "Retargeting Quick Launch shortcuts.";
357  if (!ShellUtil::UpdateShortcutsWithArgs(
358          ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH, dist, install_level,
359          old_target_exe, updated_properties)) {
360    LOG(WARNING) << "Failed to retarget Quick Launch shortcuts.";
361  }
362
363  VLOG(1) << "Retargeting Start Menu shortcuts.";
364  if (!ShellUtil::UpdateShortcutsWithArgs(
365          ShellUtil::SHORTCUT_LOCATION_START_MENU, dist, install_level,
366          old_target_exe, updated_properties)) {
367    LOG(WARNING) << "Failed to retarget Start Menu shortcuts.";
368  }
369
370  // Retarget pinned-to-taskbar shortcuts that point to |chrome_exe|.
371  if (!ShellUtil::UpdateShortcutsWithArgs(
372          ShellUtil::SHORTCUT_LOCATION_TASKBAR_PINS, dist,
373          ShellUtil::CURRENT_USER, old_target_exe, updated_properties)) {
374    LOG(WARNING) << "Failed to retarget taskbar shortcuts at user-level.";
375  }
376
377  // Retarget the folder of secondary tiles from the start screen for |dist|.
378  if (!ShellUtil::UpdateShortcutsWithArgs(
379          ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS, dist, install_level,
380          old_target_exe, updated_properties)) {
381    LOG(WARNING) << "Failed to retarget start-screen shortcuts.";
382  }
383}
384
385// Deletes shortcuts at |install_level| from Start menu, Desktop,
386// Quick Launch, taskbar, and secondary tiles on the Start Screen (Win8+).
387// Only shortcuts pointing to |target_exe| will be removed.
388void DeleteShortcuts(const InstallerState& installer_state,
389                     const Product& product,
390                     const base::FilePath& target_exe) {
391  BrowserDistribution* dist = product.distribution();
392
393  // The per-user shortcut for this user, if present on a system-level install,
394  // has already been deleted in chrome_browser_main_win.cc::DoUninstallTasks().
395  ShellUtil::ShellChange install_level = installer_state.system_install() ?
396      ShellUtil::SYSTEM_LEVEL : ShellUtil::CURRENT_USER;
397
398  VLOG(1) << "Deleting Desktop shortcuts.";
399  if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist,
400                                  install_level, target_exe)) {
401    LOG(WARNING) << "Failed to delete Desktop shortcuts.";
402  }
403
404  VLOG(1) << "Deleting Quick Launch shortcuts.";
405  if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH,
406                                  dist, install_level, target_exe)) {
407    LOG(WARNING) << "Failed to delete Quick Launch shortcuts.";
408  }
409
410  VLOG(1) << "Deleting Start Menu shortcuts.";
411  if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_START_MENU, dist,
412                                  install_level, target_exe)) {
413    LOG(WARNING) << "Failed to delete Start Menu shortcuts.";
414  }
415
416  // Unpin all pinned-to-taskbar shortcuts that point to |chrome_exe|.
417  if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_TASKBAR_PINS,
418                                  dist, ShellUtil::CURRENT_USER, target_exe)) {
419    LOG(WARNING) << "Failed to unpin taskbar shortcuts at user-level.";
420  }
421
422  // Delete the folder of secondary tiles from the start screen for |dist|.
423  if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS,
424                                  dist, install_level, target_exe)) {
425    LOG(WARNING) << "Failed to delete start-screen shortcuts.";
426  }
427}
428
429bool ScheduleParentAndGrandparentForDeletion(const base::FilePath& path) {
430  base::FilePath parent_dir = path.DirName();
431  bool ret = ScheduleFileSystemEntityForDeletion(parent_dir);
432  if (!ret) {
433    LOG(ERROR) << "Failed to schedule parent dir for deletion: "
434               << parent_dir.value();
435  } else {
436    base::FilePath grandparent_dir(parent_dir.DirName());
437    ret = ScheduleFileSystemEntityForDeletion(grandparent_dir);
438    if (!ret) {
439      LOG(ERROR) << "Failed to schedule grandparent dir for deletion: "
440                 << grandparent_dir.value();
441    }
442  }
443  return ret;
444}
445
446enum DeleteResult {
447  DELETE_SUCCEEDED,
448  DELETE_NOT_EMPTY,
449  DELETE_FAILED,
450  DELETE_REQUIRES_REBOOT,
451};
452
453// Deletes the given directory if it is empty. Returns DELETE_SUCCEEDED if the
454// directory is deleted, DELETE_NOT_EMPTY if it is not empty, and DELETE_FAILED
455// otherwise.
456DeleteResult DeleteEmptyDir(const base::FilePath& path) {
457  if (!file_util::IsDirectoryEmpty(path))
458    return DELETE_NOT_EMPTY;
459
460  if (base::DeleteFile(path, true))
461    return DELETE_SUCCEEDED;
462
463  LOG(ERROR) << "Failed to delete folder: " << path.value();
464  return DELETE_FAILED;
465}
466
467void GetLocalStateFolders(const Product& product,
468                          std::vector<base::FilePath>* paths) {
469  // Obtain the location of the user profile data.
470  product.GetUserDataPaths(paths);
471  LOG_IF(ERROR, paths->empty())
472      << "Could not retrieve user's profile directory.";
473}
474
475// Creates a copy of the local state file and returns a path to the copy.
476base::FilePath BackupLocalStateFile(
477    const std::vector<base::FilePath>& local_state_folders) {
478  base::FilePath backup;
479
480  // Copy the first local state file that is found.
481  for (size_t i = 0; i < local_state_folders.size(); ++i) {
482    const base::FilePath& local_state_folder = local_state_folders[i];
483    base::FilePath state_file(
484        local_state_folder.Append(chrome::kLocalStateFilename));
485    if (!base::PathExists(state_file))
486      continue;
487    if (!file_util::CreateTemporaryFile(&backup))
488      LOG(ERROR) << "Failed to create temporary file for Local State.";
489    else
490      base::CopyFile(state_file, backup);
491    break;
492  }
493  return backup;
494}
495
496// Deletes all user data directories for a product.
497DeleteResult DeleteLocalState(
498    const std::vector<base::FilePath>& local_state_folders,
499    bool schedule_on_failure) {
500  if (local_state_folders.empty())
501    return DELETE_SUCCEEDED;
502
503  DeleteResult result = DELETE_SUCCEEDED;
504  for (size_t i = 0; i < local_state_folders.size(); ++i) {
505    const base::FilePath& user_local_state = local_state_folders[i];
506    VLOG(1) << "Deleting user profile " << user_local_state.value();
507    if (!base::DeleteFile(user_local_state, true)) {
508      LOG(ERROR) << "Failed to delete user profile dir: "
509                 << user_local_state.value();
510      if (schedule_on_failure) {
511        ScheduleDirectoryForDeletion(user_local_state);
512        result = DELETE_REQUIRES_REBOOT;
513      } else {
514        result = DELETE_FAILED;
515      }
516    }
517  }
518
519  if (result == DELETE_REQUIRES_REBOOT) {
520    ScheduleParentAndGrandparentForDeletion(local_state_folders[0]);
521  } else {
522    const base::FilePath user_data_dir(local_state_folders[0].DirName());
523    if (!user_data_dir.empty() &&
524        DeleteEmptyDir(user_data_dir) == DELETE_SUCCEEDED) {
525      const base::FilePath product_dir(user_data_dir.DirName());
526      if (!product_dir.empty())
527        DeleteEmptyDir(product_dir);
528    }
529  }
530
531  return result;
532}
533
534// Moves setup to a temporary file, outside of the install folder. Also attempts
535// to change the current directory to the TMP directory. On Windows, each
536// process has a handle to its CWD. If setup.exe's CWD happens to be within the
537// install directory, deletion will fail as a result of the open handle.
538bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state,
539                                 const base::FilePath& setup_exe) {
540  bool ret = false;
541  base::FilePath tmp_dir;
542  base::FilePath temp_file;
543  if (!PathService::Get(base::DIR_TEMP, &tmp_dir)) {
544    NOTREACHED();
545  } else if (!file_util::CreateTemporaryFileInDir(tmp_dir, &temp_file)) {
546    LOG(ERROR) << "Failed to create temporary file for setup.exe.";
547  } else {
548    VLOG(1) << "Changing current directory to: " << tmp_dir.value();
549    if (!file_util::SetCurrentDirectory(tmp_dir))
550      PLOG(ERROR) << "Failed to change the current directory.";
551
552    VLOG(1) << "Attempting to move setup to: " << temp_file.value();
553    ret = base::Move(setup_exe, temp_file);
554    PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value();
555
556    // We cannot delete the file right away, but try to delete it some other
557    // way. Either with the help of a different process or the system.
558    if (ret && !base::DeleteFileAfterReboot(temp_file)) {
559      static const uint32 kDeleteAfterMs = 10 * 1000;
560      installer::DeleteFileFromTempProcess(temp_file, kDeleteAfterMs);
561    }
562  }
563  return ret;
564}
565
566DeleteResult DeleteApplicationProductAndVendorDirectories(
567    const base::FilePath& application_directory) {
568  DeleteResult result(DeleteEmptyDir(application_directory));
569  if (result == DELETE_SUCCEEDED) {
570    // Now check and delete if the parent directories are empty
571    // For example Google\Chrome or Chromium
572    const base::FilePath product_directory(application_directory.DirName());
573    if (!product_directory.empty()) {
574        result = DeleteEmptyDir(product_directory);
575        if (result == DELETE_SUCCEEDED) {
576          const base::FilePath vendor_directory(product_directory.DirName());
577          if (!vendor_directory.empty())
578            result = DeleteEmptyDir(vendor_directory);
579        }
580    }
581  }
582  if (result == DELETE_NOT_EMPTY)
583    result = DELETE_SUCCEEDED;
584  return result;
585}
586
587DeleteResult DeleteAppHostFilesAndFolders(const InstallerState& installer_state,
588                                          const Version& installed_version) {
589  const base::FilePath& target_path = installer_state.target_path();
590  if (target_path.empty()) {
591    LOG(ERROR) << "DeleteAppHostFilesAndFolders: no installation destination "
592               << "path.";
593    return DELETE_FAILED;  // Nothing else we can do to uninstall, so we return.
594  }
595
596  DeleteInstallTempDir(target_path);
597
598  DeleteResult result = DELETE_SUCCEEDED;
599
600  base::FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe));
601  if (!base::DeleteFile(app_host_exe, false)) {
602    result = DELETE_FAILED;
603    LOG(ERROR) << "Failed to delete path: " << app_host_exe.value();
604  }
605
606  return result;
607}
608
609DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state,
610                                         const base::FilePath& setup_exe) {
611  const base::FilePath& target_path = installer_state.target_path();
612  if (target_path.empty()) {
613    LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination "
614               << "path.";
615    return DELETE_FAILED;  // Nothing else we can do to uninstall, so we return.
616  }
617
618  DeleteInstallTempDir(target_path);
619
620  DeleteResult result = DELETE_SUCCEEDED;
621
622  base::FilePath installer_directory;
623  if (target_path.IsParent(setup_exe))
624    installer_directory = setup_exe.DirName();
625
626  // Enumerate all the files in target_path recursively (breadth-first).
627  // We delete a file or folder unless it is a parent/child of the installer
628  // directory. For parents of the installer directory, we will later recurse
629  // and delete all the children (that are not also parents/children of the
630  // installer directory).
631  base::FileEnumerator file_enumerator(target_path, true,
632      base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
633  while (true) {
634    base::FilePath to_delete(file_enumerator.Next());
635    if (to_delete.empty())
636      break;
637    if (to_delete.BaseName().value() == installer::kChromeAppHostExe)
638      continue;
639    if (!installer_directory.empty() &&
640        (to_delete == installer_directory ||
641         installer_directory.IsParent(to_delete) ||
642         to_delete.IsParent(installer_directory))) {
643      continue;
644    }
645
646    VLOG(1) << "Deleting install path " << to_delete.value();
647    if (!base::DeleteFile(to_delete, true)) {
648      LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value();
649      if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) {
650        // We don't try killing Chrome processes for Chrome Frame builds since
651        // that is unlikely to help. Instead, schedule files for deletion and
652        // return a value that will trigger a reboot prompt.
653        base::FileEnumerator::FileInfo find_info = file_enumerator.GetInfo();
654        if (find_info.IsDirectory())
655          ScheduleDirectoryForDeletion(to_delete);
656        else
657          ScheduleFileSystemEntityForDeletion(to_delete);
658        result = DELETE_REQUIRES_REBOOT;
659      } else {
660        // Try closing any running Chrome processes and deleting files once
661        // again.
662        CloseAllChromeProcesses();
663        if (!base::DeleteFile(to_delete, true)) {
664          LOG(ERROR) << "Failed to delete path (2nd try): "
665                     << to_delete.value();
666          result = DELETE_FAILED;
667          break;
668        }
669      }
670    }
671  }
672
673  return result;
674}
675
676// This method checks if Chrome is currently running or if the user has
677// cancelled the uninstall operation by clicking Cancel on the confirmation
678// box that Chrome pops up.
679InstallStatus IsChromeActiveOrUserCancelled(
680    const InstallerState& installer_state,
681    const Product& product) {
682  int32 exit_code = content::RESULT_CODE_NORMAL_EXIT;
683  CommandLine options(CommandLine::NO_PROGRAM);
684  options.AppendSwitch(installer::switches::kUninstall);
685
686  // Here we want to save user from frustration (in case of Chrome crashes)
687  // and continue with the uninstallation as long as chrome.exe process exit
688  // code is NOT one of the following:
689  // - UNINSTALL_CHROME_ALIVE - chrome.exe is currently running
690  // - UNINSTALL_USER_CANCEL - User cancelled uninstallation
691  // - HUNG - chrome.exe was killed by HuntForZombieProcesses() (until we can
692  //          give this method some brains and not kill chrome.exe launched
693  //          by us, we will not uninstall if we get this return code).
694  VLOG(1) << "Launching Chrome to do uninstall tasks.";
695  if (product.LaunchChromeAndWait(installer_state.target_path(), options,
696                                  &exit_code)) {
697    VLOG(1) << "chrome.exe launched for uninstall confirmation returned: "
698            << exit_code;
699    if ((exit_code == chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE) ||
700        (exit_code == chrome::RESULT_CODE_UNINSTALL_USER_CANCEL) ||
701        (exit_code == content::RESULT_CODE_HUNG))
702      return installer::UNINSTALL_CANCELLED;
703
704    if (exit_code == chrome::RESULT_CODE_UNINSTALL_DELETE_PROFILE)
705      return installer::UNINSTALL_DELETE_PROFILE;
706  } else {
707    PLOG(ERROR) << "Failed to launch chrome.exe for uninstall confirmation.";
708  }
709
710  return installer::UNINSTALL_CONFIRMED;
711}
712
713bool ShouldDeleteProfile(const InstallerState& installer_state,
714                         const CommandLine& cmd_line, InstallStatus status,
715                         const Product& product) {
716  bool should_delete = false;
717
718  // Chrome Frame uninstallations always want to delete the profile (we have no
719  // UI to prompt otherwise and the profile stores no useful data anyway)
720  // unless they are managed by MSI. MSI uninstalls will explicitly include
721  // the --delete-profile flag to distinguish them from MSI upgrades.
722  if (product.is_chrome_frame() && !installer_state.is_msi()) {
723    should_delete = true;
724  } else {
725    should_delete =
726        status == installer::UNINSTALL_DELETE_PROFILE ||
727        cmd_line.HasSwitch(installer::switches::kDeleteProfile);
728  }
729
730  return should_delete;
731}
732
733// Removes XP-era filetype registration making Chrome the default browser.
734// MSDN (see http://msdn.microsoft.com/library/windows/desktop/cc144148.aspx)
735// tells us not to do this, but certain applications break following
736// uninstallation if we don't.
737void RemoveFiletypeRegistration(const InstallerState& installer_state,
738                                HKEY root,
739                                const string16& browser_entry_suffix) {
740  string16 classes_path(ShellUtil::kRegClasses);
741  classes_path.push_back(base::FilePath::kSeparators[0]);
742
743  BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
744  const string16 prog_id(
745      distribution->GetBrowserProgIdPrefix() + browser_entry_suffix);
746
747  // Delete each filetype association if it references this Chrome.  Take care
748  // not to delete the association if it references a system-level install of
749  // Chrome (only a risk if the suffix is empty).  Don't delete the whole key
750  // since other apps may have stored data there.
751  std::vector<const wchar_t*> cleared_assocs;
752  if (installer_state.system_install() ||
753      !browser_entry_suffix.empty() ||
754      !base::win::RegKey(HKEY_LOCAL_MACHINE, (classes_path + prog_id).c_str(),
755                         KEY_QUERY_VALUE).Valid()) {
756    InstallUtil::ValueEquals prog_id_pred(prog_id);
757    for (const wchar_t* const* filetype =
758         &ShellUtil::kPotentialFileAssociations[0]; *filetype != NULL;
759         ++filetype) {
760      if (InstallUtil::DeleteRegistryValueIf(
761              root, (classes_path + *filetype).c_str(), NULL,
762              prog_id_pred) == InstallUtil::DELETED) {
763        cleared_assocs.push_back(*filetype);
764      }
765    }
766  }
767
768  // For all filetype associations in HKLM that have just been removed, attempt
769  // to restore some reasonable value.  We have no definitive way of knowing
770  // what handlers are the most appropriate, so we use a fixed mapping based on
771  // the default values for a fresh install of Windows.
772  if (root == HKEY_LOCAL_MACHINE) {
773    string16 assoc;
774    base::win::RegKey key;
775
776    for (size_t i = 0; i < cleared_assocs.size(); ++i) {
777      const wchar_t* replacement_prog_id = NULL;
778      assoc.assign(cleared_assocs[i]);
779
780      // Inelegant, but simpler than a pure data-driven approach.
781      if (assoc == L".htm" || assoc == L".html")
782        replacement_prog_id = L"htmlfile";
783      else if (assoc == L".xht" || assoc == L".xhtml")
784        replacement_prog_id = L"xhtmlfile";
785
786      if (!replacement_prog_id) {
787        LOG(WARNING) << "No known replacement ProgID for " << assoc
788                     << " files.";
789      } else if (key.Open(HKEY_LOCAL_MACHINE,
790                          (classes_path + replacement_prog_id).c_str(),
791                          KEY_QUERY_VALUE) == ERROR_SUCCESS &&
792                 (key.Open(HKEY_LOCAL_MACHINE, (classes_path + assoc).c_str(),
793                           KEY_SET_VALUE) != ERROR_SUCCESS ||
794                  key.WriteValue(NULL, replacement_prog_id) != ERROR_SUCCESS)) {
795        // The replacement ProgID is registered on the computer but the attempt
796        // to set it for the filetype failed.
797        LOG(ERROR) << "Failed to restore system-level filetype association "
798                   << assoc << " = " << replacement_prog_id;
799      }
800    }
801  }
802}
803
804bool DeleteChromeRegistrationKeys(const InstallerState& installer_state,
805                                  BrowserDistribution* dist,
806                                  HKEY root,
807                                  const string16& browser_entry_suffix,
808                                  InstallStatus* exit_code) {
809  DCHECK(exit_code);
810  if (!dist->CanSetAsDefault()) {
811    // We should have never set those keys.
812    return true;
813  }
814
815  base::FilePath chrome_exe(installer_state.target_path().Append(kChromeExe));
816
817  // Delete Software\Classes\ChromeHTML.
818  const string16 prog_id(
819      dist->GetBrowserProgIdPrefix() + browser_entry_suffix);
820  string16 reg_prog_id(ShellUtil::kRegClasses);
821  reg_prog_id.push_back(base::FilePath::kSeparators[0]);
822  reg_prog_id.append(prog_id);
823  InstallUtil::DeleteRegistryKey(root, reg_prog_id);
824
825  // Delete Software\Classes\Chrome.
826  string16 reg_app_id(ShellUtil::kRegClasses);
827  reg_app_id.push_back(base::FilePath::kSeparators[0]);
828  // Append the requested suffix manually here (as ShellUtil::GetBrowserModelId
829  // would otherwise try to figure out the currently installed suffix).
830  reg_app_id.append(dist->GetBaseAppId() + browser_entry_suffix);
831  InstallUtil::DeleteRegistryKey(root, reg_app_id);
832
833  // Delete all Start Menu Internet registrations that refer to this Chrome.
834  {
835    using base::win::RegistryKeyIterator;
836    InstallUtil::ProgramCompare open_command_pred(chrome_exe);
837    string16 client_name;
838    string16 client_key;
839    string16 open_key;
840    for (RegistryKeyIterator iter(root, ShellUtil::kRegStartMenuInternet);
841         iter.Valid(); ++iter) {
842      client_name.assign(iter.Name());
843      client_key.assign(ShellUtil::kRegStartMenuInternet)
844          .append(1, L'\\')
845          .append(client_name);
846      open_key.assign(client_key).append(ShellUtil::kRegShellOpen);
847      if (InstallUtil::DeleteRegistryKeyIf(root, client_key, open_key, NULL,
848              open_command_pred) != InstallUtil::NOT_FOUND) {
849        // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
850        // references this Chrome (i.e., if it was made the default browser).
851        InstallUtil::DeleteRegistryValueIf(
852            root, ShellUtil::kRegStartMenuInternet, NULL,
853            InstallUtil::ValueEquals(client_name));
854        // Also delete the value for the default user if we're operating in
855        // HKLM.
856        if (root == HKEY_LOCAL_MACHINE) {
857          InstallUtil::DeleteRegistryValueIf(
858              HKEY_USERS,
859              string16(L".DEFAULT\\").append(
860                  ShellUtil::kRegStartMenuInternet).c_str(),
861              NULL, InstallUtil::ValueEquals(client_name));
862        }
863      }
864    }
865  }
866
867  // Delete Software\RegisteredApplications\Chromium
868  InstallUtil::DeleteRegistryValue(
869      root, ShellUtil::kRegRegisteredApplications,
870      dist->GetBaseAppName() + browser_entry_suffix);
871
872  // Delete the App Paths and Applications keys that let Explorer find Chrome:
873  // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
874  string16 app_key(ShellUtil::kRegClasses);
875  app_key.push_back(base::FilePath::kSeparators[0]);
876  app_key.append(L"Applications");
877  app_key.push_back(base::FilePath::kSeparators[0]);
878  app_key.append(installer::kChromeExe);
879  InstallUtil::DeleteRegistryKey(root, app_key);
880
881  string16 app_path_key(ShellUtil::kAppPathsRegistryKey);
882  app_path_key.push_back(base::FilePath::kSeparators[0]);
883  app_path_key.append(installer::kChromeExe);
884  InstallUtil::DeleteRegistryKey(root, app_path_key);
885
886  // Cleanup OpenWithList and OpenWithProgids:
887  // http://msdn.microsoft.com/en-us/library/bb166549
888  string16 file_assoc_key;
889  string16 open_with_list_key;
890  string16 open_with_progids_key;
891  for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; ++i) {
892    file_assoc_key.assign(ShellUtil::kRegClasses);
893    file_assoc_key.push_back(base::FilePath::kSeparators[0]);
894    file_assoc_key.append(ShellUtil::kPotentialFileAssociations[i]);
895    file_assoc_key.push_back(base::FilePath::kSeparators[0]);
896
897    open_with_list_key.assign(file_assoc_key);
898    open_with_list_key.append(L"OpenWithList");
899    open_with_list_key.push_back(base::FilePath::kSeparators[0]);
900    open_with_list_key.append(installer::kChromeExe);
901    InstallUtil::DeleteRegistryKey(root, open_with_list_key);
902
903    open_with_progids_key.assign(file_assoc_key);
904    open_with_progids_key.append(ShellUtil::kRegOpenWithProgids);
905    InstallUtil::DeleteRegistryValue(root, open_with_progids_key, prog_id);
906  }
907
908  // Cleanup in case Chrome had been made the default browser.
909
910  // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
911  // references this Chrome.  Do this explicitly here for the case where HKCU is
912  // being processed; the iteration above will have no hits since registration
913  // lives in HKLM.
914  InstallUtil::DeleteRegistryValueIf(
915      root, ShellUtil::kRegStartMenuInternet, NULL,
916      InstallUtil::ValueEquals(dist->GetBaseAppName() + browser_entry_suffix));
917
918  // Delete each protocol association if it references this Chrome.
919  InstallUtil::ProgramCompare open_command_pred(chrome_exe);
920  string16 parent_key(ShellUtil::kRegClasses);
921  parent_key.push_back(base::FilePath::kSeparators[0]);
922  const string16::size_type base_length = parent_key.size();
923  string16 child_key;
924  for (const wchar_t* const* proto =
925           &ShellUtil::kPotentialProtocolAssociations[0];
926       *proto != NULL;
927       ++proto) {
928    parent_key.resize(base_length);
929    parent_key.append(*proto);
930    child_key.assign(parent_key).append(ShellUtil::kRegShellOpen);
931    InstallUtil::DeleteRegistryKeyIf(root, parent_key, child_key, NULL,
932                                     open_command_pred);
933  }
934
935  RemoveFiletypeRegistration(installer_state, root, browser_entry_suffix);
936
937  *exit_code = installer::UNINSTALL_SUCCESSFUL;
938  return true;
939}
940
941void RemoveChromeLegacyRegistryKeys(BrowserDistribution* dist,
942                                    const string16& chrome_exe) {
943  // We used to register Chrome to handle crx files, but this turned out
944  // to be not worth the hassle. Remove these old registry entries if
945  // they exist. See: http://codereview.chromium.org/210007
946
947#if defined(GOOGLE_CHROME_BUILD)
948const wchar_t kChromeExtProgId[] = L"ChromeExt";
949#else
950const wchar_t kChromeExtProgId[] = L"ChromiumExt";
951#endif
952
953  HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
954  for (size_t i = 0; i < arraysize(roots); ++i) {
955    string16 suffix;
956    if (roots[i] == HKEY_LOCAL_MACHINE)
957      suffix = ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe);
958
959    // Delete Software\Classes\ChromeExt,
960    string16 ext_prog_id(ShellUtil::kRegClasses);
961    ext_prog_id.push_back(base::FilePath::kSeparators[0]);
962    ext_prog_id.append(kChromeExtProgId);
963    ext_prog_id.append(suffix);
964    InstallUtil::DeleteRegistryKey(roots[i], ext_prog_id);
965
966    // Delete Software\Classes\.crx,
967    string16 ext_association(ShellUtil::kRegClasses);
968    ext_association.append(L"\\");
969    ext_association.append(extensions::kExtensionFileExtension);
970    InstallUtil::DeleteRegistryKey(roots[i], ext_association);
971  }
972}
973
974// Builds and executes a work item list to remove DelegateExecute verb handler
975// work items for |product|.  This will be a noop for products whose
976// corresponding BrowserDistribution implementations do not publish a CLSID via
977// GetCommandExecuteImplClsid.
978bool ProcessDelegateExecuteWorkItems(const InstallerState& installer_state,
979                                     const Product& product) {
980  scoped_ptr<WorkItemList> item_list(WorkItem::CreateNoRollbackWorkItemList());
981  AddDelegateExecuteWorkItems(installer_state, base::FilePath(), Version(),
982                              product, item_list.get());
983  return item_list->Do();
984}
985
986// Removes Active Setup entries from the registry. This cannot be done through
987// a work items list as usual because of different paths based on conditionals,
988// but otherwise respects the no rollback/best effort uninstall mentality.
989// This will only apply for system-level installs of Chrome/Chromium and will be
990// a no-op for all other types of installs.
991void UninstallActiveSetupEntries(const InstallerState& installer_state,
992                                 const Product& product) {
993  VLOG(1) << "Uninstalling registry entries for ActiveSetup.";
994  BrowserDistribution* distribution = product.distribution();
995
996  if (!product.is_chrome() || !installer_state.system_install()) {
997    const char* install_level =
998        installer_state.system_install() ? "system" : "user";
999    VLOG(1) << "No Active Setup processing to do for " << install_level
1000            << "-level " << distribution->GetDisplayName();
1001    return;
1002  }
1003
1004  const string16 active_setup_path(
1005      InstallUtil::GetActiveSetupPath(distribution));
1006  InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, active_setup_path);
1007
1008  // Windows leaves keys behind in HKCU\\Software\\(Wow6432Node\\)?Microsoft\\
1009  //     Active Setup\\Installed Components\\{guid}
1010  // for every user that logged in since system-level Chrome was installed.
1011  // This is a problem because Windows compares the value of the Version subkey
1012  // in there with the value of the Version subkey in the matching HKLM entries
1013  // before running Chrome's Active Setup so if Chrome was to be reinstalled
1014  // with a lesser version (e.g. switching back to a more stable channel), the
1015  // affected users would not have Chrome's Active Setup called until Chrome
1016  // eventually updated passed that user's registered Version.
1017  //
1018  // It is however very hard to delete those values as the registry hives for
1019  // other users are not loaded by default under HKEY_USERS (unless a user is
1020  // logged on or has a process impersonating him).
1021  //
1022  // Following our best effort uninstall practices, try to delete the value in
1023  // all users hives. If a given user's hive is not loaded, try to load it to
1024  // proceed with the deletion (failure to do so is ignored).
1025
1026  static const wchar_t kProfileList[] =
1027      L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\";
1028
1029  // Windows automatically adds Wow6432Node when creating/deleting the HKLM key,
1030  // but doesn't seem to do so when manually deleting the user-level keys it
1031  // created.
1032  string16 alternate_active_setup_path(active_setup_path);
1033  alternate_active_setup_path.insert(arraysize("Software\\") - 1,
1034                                     L"Wow6432Node\\");
1035
1036  // These two privileges are required by RegLoadKey() and RegUnloadKey() below.
1037  ScopedTokenPrivilege se_restore_name_privilege(SE_RESTORE_NAME);
1038  ScopedTokenPrivilege se_backup_name_privilege(SE_BACKUP_NAME);
1039  if (!se_restore_name_privilege.is_enabled() ||
1040      !se_backup_name_privilege.is_enabled()) {
1041    // This is not a critical failure as those privileges aren't required to
1042    // clean hives that are already loaded, but attempts to LoadRegKey() below
1043    // will fail.
1044    LOG(WARNING) << "Failed to enable privileges required to load registry "
1045                    "hives.";
1046  }
1047
1048  for (base::win::RegistryKeyIterator it(HKEY_LOCAL_MACHINE, kProfileList);
1049       it.Valid(); ++it) {
1050    const wchar_t* profile_sid = it.Name();
1051
1052    // First check if this user's registry hive needs to be loaded in
1053    // HKEY_USERS.
1054    base::win::RegKey user_reg_root_probe(
1055        HKEY_USERS, profile_sid, KEY_READ);
1056    bool loaded_hive = false;
1057    if (!user_reg_root_probe.Valid()) {
1058      VLOG(1) << "Attempting to load registry hive for " << profile_sid;
1059
1060      string16 reg_profile_info_path(kProfileList);
1061      reg_profile_info_path.append(profile_sid);
1062      base::win::RegKey reg_profile_info_key(
1063          HKEY_LOCAL_MACHINE, reg_profile_info_path.c_str(), KEY_READ);
1064
1065      string16 profile_path;
1066      LONG result = reg_profile_info_key.ReadValue(L"ProfileImagePath",
1067                                                   &profile_path);
1068      if (result != ERROR_SUCCESS) {
1069        LOG(ERROR) << "Error reading ProfileImagePath: " << result;
1070        continue;
1071      }
1072      base::FilePath registry_hive_file(profile_path);
1073      registry_hive_file = registry_hive_file.AppendASCII("NTUSER.DAT");
1074
1075      result = RegLoadKey(HKEY_USERS, profile_sid,
1076                          registry_hive_file.value().c_str());
1077      if (result != ERROR_SUCCESS) {
1078        LOG(ERROR) << "Error loading registry hive: " << result;
1079        continue;
1080      }
1081
1082      VLOG(1) << "Loaded registry hive for " << profile_sid;
1083      loaded_hive = true;
1084    }
1085
1086    base::win::RegKey user_reg_root(
1087        HKEY_USERS, profile_sid, KEY_ALL_ACCESS);
1088
1089    LONG result = user_reg_root.DeleteKey(active_setup_path.c_str());
1090    if (result != ERROR_SUCCESS) {
1091      result = user_reg_root.DeleteKey(alternate_active_setup_path.c_str());
1092      if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
1093        LOG(ERROR) << "Failed to delete key at " << active_setup_path
1094                   << " and at " << alternate_active_setup_path
1095                   << ", result: " << result;
1096      }
1097    }
1098
1099    if (loaded_hive) {
1100      user_reg_root.Close();
1101      if (RegUnLoadKey(HKEY_USERS, profile_sid) == ERROR_SUCCESS)
1102        VLOG(1) << "Unloaded registry hive for " << profile_sid;
1103      else
1104        LOG(ERROR) << "Error unloading registry hive for " << profile_sid;
1105    }
1106  }
1107}
1108
1109bool ProcessChromeFrameWorkItems(const InstallationState& original_state,
1110                                 const InstallerState& installer_state,
1111                                 const base::FilePath& setup_path,
1112                                 const Product& product) {
1113  if (!product.is_chrome_frame())
1114    return false;
1115
1116  scoped_ptr<WorkItemList> item_list(WorkItem::CreateNoRollbackWorkItemList());
1117  AddChromeFrameWorkItems(original_state, installer_state, setup_path,
1118                          Version(), product, item_list.get());
1119  return item_list->Do();
1120}
1121
1122InstallStatus UninstallProduct(const InstallationState& original_state,
1123                               const InstallerState& installer_state,
1124                               const base::FilePath& setup_path,
1125                               const Product& product,
1126                               bool remove_all,
1127                               bool force_uninstall,
1128                               const CommandLine& cmd_line) {
1129  InstallStatus status = installer::UNINSTALL_CONFIRMED;
1130  BrowserDistribution* browser_dist = product.distribution();
1131  const string16 chrome_exe(
1132      installer_state.target_path().Append(installer::kChromeExe).value());
1133
1134  const string16 suffix(ShellUtil::GetCurrentInstallationSuffix(browser_dist,
1135                                                                chrome_exe));
1136
1137  bool is_chrome = product.is_chrome();
1138
1139  VLOG(1) << "UninstallProduct: " << browser_dist->GetDisplayName();
1140
1141  if (force_uninstall) {
1142    // Since --force-uninstall command line option is used, we are going to
1143    // do silent uninstall. Try to close all running Chrome instances.
1144    // NOTE: We don't do this for Chrome Frame.
1145    if (is_chrome)
1146      CloseAllChromeProcesses();
1147  } else if (is_chrome) {
1148    // no --force-uninstall so lets show some UI dialog boxes.
1149    status = IsChromeActiveOrUserCancelled(installer_state, product);
1150    if (status != installer::UNINSTALL_CONFIRMED &&
1151        status != installer::UNINSTALL_DELETE_PROFILE)
1152      return status;
1153
1154    // Check if we need admin rights to cleanup HKLM (the conditions for
1155    // requiring a cleanup are the same as the conditions to do the actual
1156    // cleanup where DeleteChromeRegistrationKeys() is invoked for
1157    // HKEY_LOCAL_MACHINE below). If we do, try to launch another uninstaller
1158    // (silent) in elevated mode to do HKLM cleanup.
1159    // And continue uninstalling in the current process also to do HKCU cleanup.
1160    if (remove_all &&
1161        ShellUtil::QuickIsChromeRegisteredInHKLM(
1162            browser_dist, chrome_exe, suffix) &&
1163        !::IsUserAnAdmin() &&
1164        base::win::GetVersion() >= base::win::VERSION_VISTA &&
1165        !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
1166      CommandLine new_cmd(CommandLine::NO_PROGRAM);
1167      new_cmd.AppendArguments(cmd_line, true);
1168      // Append --run-as-admin flag to let the new instance of setup.exe know
1169      // that we already tried to launch ourselves as admin.
1170      new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
1171      // Append --remove-chrome-registration to remove registry keys only.
1172      new_cmd.AppendSwitch(installer::switches::kRemoveChromeRegistration);
1173      if (!suffix.empty()) {
1174        new_cmd.AppendSwitchNative(
1175            installer::switches::kRegisterChromeBrowserSuffix, suffix);
1176      }
1177      DWORD exit_code = installer::UNKNOWN_STATUS;
1178      InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
1179    }
1180  }
1181
1182  if (is_chrome) {
1183    // Chrome is not in use so lets uninstall Chrome by deleting various files
1184    // and registry entries. Here we will just make best effort and keep going
1185    // in case of errors.
1186    ClearRlzProductState();
1187    // Delete the key that delegate_execute might make.
1188    if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
1189      InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER,
1190                                     chrome::kMetroRegistryPath);
1191    }
1192
1193    auto_launch_util::DisableAllAutoStartFeatures(
1194        ASCIIToUTF16(chrome::kInitialProfile));
1195
1196    // If user-level chrome is self-destructing as a result of encountering a
1197    // system-level chrome, retarget owned non-default shortcuts (app shortcuts,
1198    // profile shortcuts, etc.) to the system-level chrome.
1199    if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
1200        !installer_state.system_install()) {
1201      const base::FilePath system_chrome_path(
1202          GetChromeInstallPath(true, browser_dist).
1203              Append(installer::kChromeExe));
1204      VLOG(1) << "Retargeting user-generated Chrome shortcuts.";
1205      if (base::PathExists(system_chrome_path)) {
1206        RetargetUserShortcutsWithArgs(installer_state, product,
1207                                      base::FilePath(chrome_exe),
1208                                      system_chrome_path);
1209      } else {
1210        LOG(ERROR) << "Retarget failed: system-level Chrome not found.";
1211      }
1212    }
1213
1214    DeleteShortcuts(installer_state, product, base::FilePath(chrome_exe));
1215
1216  } else if (product.is_chrome_app_host()) {
1217    const base::FilePath app_host_exe(
1218        installer_state.target_path().Append(installer::kChromeAppHostExe));
1219    DeleteShortcuts(installer_state, product, app_host_exe);
1220  }
1221
1222  // Delete the registry keys (Uninstall key and Version key).
1223  HKEY reg_root = installer_state.root_key();
1224
1225  // Note that we must retrieve the distribution-specific data before deleting
1226  // product.GetVersionKey().
1227  string16 distribution_data(browser_dist->GetDistributionData(reg_root));
1228
1229  // Remove Control Panel uninstall link.
1230  if (product.ShouldCreateUninstallEntry()) {
1231    InstallUtil::DeleteRegistryKey(reg_root,
1232                                   browser_dist->GetUninstallRegPath());
1233  }
1234
1235  // Remove Omaha product key.
1236  InstallUtil::DeleteRegistryKey(reg_root, browser_dist->GetVersionKey());
1237
1238  // Also try to delete the MSI value in the ClientState key (it might not be
1239  // there). This is due to a Google Update behaviour where an uninstall and a
1240  // rapid reinstall might result in stale values from the old ClientState key
1241  // being picked up on reinstall.
1242  product.SetMsiMarker(installer_state.system_install(), false);
1243
1244  InstallStatus ret = installer::UNKNOWN_STATUS;
1245
1246  if (is_chrome) {
1247    // Remove all Chrome registration keys.
1248    // Registration data is put in HKCU for both system level and user level
1249    // installs.
1250    DeleteChromeRegistrationKeys(installer_state, browser_dist,
1251                                 HKEY_CURRENT_USER, suffix, &ret);
1252
1253    // If the user's Chrome is registered with a suffix: it is possible that old
1254    // unsuffixed registrations were left in HKCU (e.g. if this install was
1255    // previously installed with no suffix in HKCU (old suffix rules if the user
1256    // is not an admin (or declined UAC at first run)) and later had to be
1257    // suffixed when fully registered in HKLM (e.g. when later making Chrome
1258    // default through the UI)).
1259    // Remove remaining HKCU entries with no suffix if any.
1260    if (!suffix.empty()) {
1261      DeleteChromeRegistrationKeys(installer_state, browser_dist,
1262                                   HKEY_CURRENT_USER, string16(), &ret);
1263
1264      // For similar reasons it is possible in very few installs (from
1265      // 21.0.1180.0 and fixed shortly after) to be installed with the new-style
1266      // suffix, but have some old-style suffix registrations left behind.
1267      string16 old_style_suffix;
1268      if (ShellUtil::GetOldUserSpecificRegistrySuffix(&old_style_suffix) &&
1269          suffix != old_style_suffix) {
1270        DeleteChromeRegistrationKeys(installer_state, browser_dist,
1271                                     HKEY_CURRENT_USER, old_style_suffix, &ret);
1272      }
1273    }
1274
1275    // Chrome is registered in HKLM for all system-level installs and for
1276    // user-level installs for which Chrome has been made the default browser.
1277    // Always remove the HKLM registration for system-level installs.  For
1278    // user-level installs, only remove it if both: 1) this uninstall isn't a
1279    // self destruct following the installation of a system-level Chrome
1280    // (because the system-level Chrome owns the HKLM registration now), and 2)
1281    // this user has made Chrome their default browser (i.e. has shell
1282    // integration entries registered with |suffix| (note: |suffix| will be the
1283    // empty string if required as it is obtained by
1284    // GetCurrentInstallationSuffix() above)).
1285    // TODO(gab): This can still leave parts of a suffixed install behind. To be
1286    // able to remove them we would need to be able to remove only suffixed
1287    // entries (as it is now some of the registry entries (e.g. App Paths) are
1288    // unsuffixed; thus removing suffixed installs is prohibited in HKLM if
1289    // !|remove_all| for now).
1290    if (installer_state.system_install() ||
1291        (remove_all &&
1292         ShellUtil::QuickIsChromeRegisteredInHKLM(
1293             browser_dist, chrome_exe, suffix))) {
1294      DeleteChromeRegistrationKeys(installer_state, browser_dist,
1295                                   HKEY_LOCAL_MACHINE, suffix, &ret);
1296    }
1297
1298    ProcessDelegateExecuteWorkItems(installer_state, product);
1299
1300    ProcessOnOsUpgradeWorkItems(installer_state, product);
1301
1302    UninstallActiveSetupEntries(installer_state, product);
1303
1304    // Notify the shell that associations have changed since Chrome was likely
1305    // unregistered.
1306    SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
1307
1308    // TODO(huangs): Implement actual migration code and remove the hack below.
1309    // Remove the "shadow" App Launcher registry keys.
1310    if (installer_state.is_multi_install()) {
1311      // If we're not uninstalling the legacy App Launcher, and if it was
1312      // not installed in the first place, then delete the "shadow" keys.
1313      chrome_launcher_support::InstallationState level_to_check =
1314          installer_state.system_install() ?
1315              chrome_launcher_support::INSTALLED_AT_SYSTEM_LEVEL :
1316              chrome_launcher_support::INSTALLED_AT_USER_LEVEL;
1317      bool has_legacy_app_launcher = level_to_check ==
1318          chrome_launcher_support::GetAppLauncherInstallationState();
1319      if (!has_legacy_app_launcher) {
1320        BrowserDistribution* shadow_app_launcher_dist =
1321            BrowserDistribution::GetSpecificDistribution(
1322                BrowserDistribution::CHROME_APP_HOST);
1323        InstallUtil::DeleteRegistryKey(reg_root,
1324            shadow_app_launcher_dist->GetVersionKey());
1325      }
1326    }
1327  }
1328
1329  if (product.is_chrome_frame()) {
1330    ProcessChromeFrameWorkItems(original_state, installer_state, setup_path,
1331                                product);
1332  }
1333
1334  if (installer_state.is_multi_install()) {
1335    ProcessGoogleUpdateItems(original_state, installer_state, product);
1336
1337    ProcessQuickEnableWorkItems(installer_state, original_state);
1338  }
1339
1340  // Get the state of the installed product (if any)
1341  const ProductState* product_state =
1342      original_state.GetProductState(installer_state.system_install(),
1343                                     browser_dist->GetType());
1344
1345  // Delete shared registry keys as well (these require admin rights) if
1346  // remove_all option is specified.
1347  if (remove_all) {
1348    if (!InstallUtil::IsChromeSxSProcess() && is_chrome) {
1349      // Delete media player registry key that exists only in HKLM.
1350      // We don't delete this key in SxS uninstall or Chrome Frame uninstall
1351      // as we never set the key for those products.
1352      string16 reg_path(installer::kMediaPlayerRegPath);
1353      reg_path.push_back(base::FilePath::kSeparators[0]);
1354      reg_path.append(installer::kChromeExe);
1355      InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path);
1356    }
1357
1358    // Unregister any dll servers that we may have registered for this
1359    // product.
1360    if (product_state != NULL) {
1361      std::vector<base::FilePath> com_dll_list;
1362      product.AddComDllList(&com_dll_list);
1363      base::FilePath dll_folder = installer_state.target_path().AppendASCII(
1364          product_state->version().GetString());
1365
1366      scoped_ptr<WorkItemList> unreg_work_item_list(
1367          WorkItem::CreateWorkItemList());
1368
1369      AddRegisterComDllWorkItems(dll_folder,
1370                                 com_dll_list,
1371                                 installer_state.system_install(),
1372                                 false,  // Unregister
1373                                 true,   // May fail
1374                                 unreg_work_item_list.get());
1375      unreg_work_item_list->Do();
1376    }
1377
1378    if (product.is_chrome_frame())
1379      ProcessIELowRightsPolicyWorkItems(installer_state);
1380  }
1381
1382  // Close any Chrome Frame helper processes that may be running.
1383  if (product.is_chrome_frame()) {
1384    VLOG(1) << "Closing the Chrome Frame helper process";
1385    CloseChromeFrameHelperProcess();
1386  }
1387
1388  if (product_state == NULL)
1389    return installer::UNINSTALL_SUCCESSFUL;
1390
1391  // Finally delete all the files from Chrome folder after moving setup.exe
1392  // and the user's Local State to a temp location.
1393  bool delete_profile = ShouldDeleteProfile(installer_state, cmd_line, status,
1394                                            product);
1395  ret = installer::UNINSTALL_SUCCESSFUL;
1396
1397  // When deleting files, we must make sure that we're either a "single"
1398  // (aka non-multi) installation or we are the Chrome Binaries.
1399
1400  std::vector<base::FilePath> local_state_folders;
1401  GetLocalStateFolders(product, &local_state_folders);
1402  base::FilePath backup_state_file(BackupLocalStateFile(local_state_folders));
1403
1404  if (product.is_chrome_app_host()) {
1405    DeleteAppHostFilesAndFolders(installer_state, product_state->version());
1406  } else if (!installer_state.is_multi_install() ||
1407             product.is_chrome_binaries()) {
1408    base::FilePath setup_exe(base::MakeAbsoluteFilePath(cmd_line.GetProgram()));
1409    DeleteResult delete_result = DeleteChromeFilesAndFolders(
1410        installer_state, setup_exe);
1411    if (delete_result == DELETE_FAILED) {
1412      ret = installer::UNINSTALL_FAILED;
1413    } else if (delete_result == DELETE_REQUIRES_REBOOT) {
1414      ret = installer::UNINSTALL_REQUIRES_REBOOT;
1415    }
1416  }
1417
1418  if (delete_profile)
1419    DeleteLocalState(local_state_folders, product.is_chrome_frame());
1420
1421  if (!force_uninstall) {
1422    VLOG(1) << "Uninstallation complete. Launching post-uninstall operations.";
1423    browser_dist->DoPostUninstallOperations(product_state->version(),
1424        backup_state_file, distribution_data);
1425  }
1426
1427  // Try and delete the preserved local state once the post-install
1428  // operations are complete.
1429  if (!backup_state_file.empty())
1430    base::DeleteFile(backup_state_file, false);
1431
1432  return ret;
1433}
1434
1435void CleanUpInstallationDirectoryAfterUninstall(
1436    const InstallationState& original_state,
1437    const InstallerState& installer_state,
1438    const CommandLine& cmd_line,
1439    installer::InstallStatus* uninstall_status) {
1440  if (*uninstall_status != installer::UNINSTALL_SUCCESSFUL &&
1441      *uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT) {
1442    return;
1443  }
1444  const base::FilePath target_path(installer_state.target_path());
1445  if (target_path.empty()) {
1446    LOG(ERROR) << "No installation destination path.";
1447    *uninstall_status = installer::UNINSTALL_FAILED;
1448    return;
1449  }
1450  base::FilePath setup_exe(base::MakeAbsoluteFilePath(cmd_line.GetProgram()));
1451  if (!target_path.IsParent(setup_exe)) {
1452    LOG(INFO) << "setup.exe is not in target path. Skipping installer cleanup.";
1453    return;
1454  }
1455  base::FilePath install_directory(setup_exe.DirName());
1456
1457  bool remove_setup = true;
1458  bool remove_archive = true;
1459  CheckShouldRemoveSetupAndArchive(original_state, installer_state,
1460                                   &remove_setup, &remove_archive);
1461  if (!remove_archive)
1462    return;
1463
1464  if (remove_setup) {
1465    // In order to be able to remove the folder in which we're running, we
1466    // need to move setup.exe out of the install folder.
1467    // TODO(tommi): What if the temp folder is on a different volume?
1468    MoveSetupOutOfInstallFolder(installer_state, setup_exe);
1469  }
1470
1471  // Remove files from "...\<product>\Application\<version>\Installer"
1472  if (!RemoveInstallerFiles(install_directory, remove_setup)) {
1473    *uninstall_status = installer::UNINSTALL_FAILED;
1474    return;
1475  }
1476
1477  if (!remove_setup)
1478    return;
1479
1480  // Try to remove the empty directory hierarchy.
1481
1482  // Delete "...\<product>\Application\<version>\Installer"
1483  if (DeleteEmptyDir(install_directory) != DELETE_SUCCEEDED) {
1484    *uninstall_status = installer::UNINSTALL_FAILED;
1485    return;
1486  }
1487
1488  // Delete "...\<product>\Application\<version>"
1489  DeleteResult delete_result = DeleteEmptyDir(install_directory.DirName());
1490  if (delete_result == DELETE_FAILED ||
1491      (delete_result == DELETE_NOT_EMPTY &&
1492       *uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT)) {
1493    *uninstall_status = installer::UNINSTALL_FAILED;
1494    return;
1495  }
1496
1497  if (*uninstall_status == installer::UNINSTALL_REQUIRES_REBOOT) {
1498    // Delete the Application directory at reboot if empty.
1499    ScheduleFileSystemEntityForDeletion(target_path);
1500
1501    // If we need a reboot to continue, schedule the parent directories for
1502    // deletion unconditionally. If they are not empty, the session manager
1503    // will not delete them on reboot.
1504    ScheduleParentAndGrandparentForDeletion(target_path);
1505  } else if (DeleteApplicationProductAndVendorDirectories(target_path) ==
1506             installer::DELETE_FAILED) {
1507    *uninstall_status = installer::UNINSTALL_FAILED;
1508  }
1509}
1510
1511}  // namespace installer
1512