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