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