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