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