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