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.
5 // This file defines the methods useful for uninstalling Chrome.
7 #include "chrome/installer/setup/uninstall.h"
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"
51 // Build-time generated include file.
52 #include "registered_dlls.h" // NOLINT
54 using base::win::RegKey;
55 using installer::InstallStatus;
56 using installer::MasterPreferences;
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?
65 void 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) ||
73 LOG(ERROR) << "Failed to delete temp dir " << temp_path.value();
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|.
81 void 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);
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();
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[-readymode]") from the "ap" values for all other
116 // installed products and for the multi-installer package.
117 void 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;
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);
133 // Apply the new channel value to all other products and to the multi package.
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);
144 AddChannelValueUpdateWorkItems(original_state, installer_state,
145 channel_info, dist_types,
147 bool success = update_list->Do();
148 LOG_IF(ERROR, !success) << "Failed updating channel values.";
152 void 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.";
163 // Adds or removes the quick-enable-cf command to the binaries' version key in
164 // the registry as needed.
165 void ProcessQuickEnableWorkItems(
166 const installer::InstallerState& installer_state,
167 const installer::InstallationState& machine_state) {
168 scoped_ptr<WorkItemList> work_item_list(
169 WorkItem::CreateNoRollbackWorkItemList());
171 AddQuickEnableChromeFrameWorkItems(installer_state, machine_state,
173 Version(), work_item_list.get());
175 if (!work_item_list->Do())
176 LOG(ERROR) << "Failed to update quick-enable-cf command.";
179 void ProcessIELowRightsPolicyWorkItems(
180 const installer::InstallerState& installer_state) {
181 scoped_ptr<WorkItemList> work_items(WorkItem::CreateNoRollbackWorkItemList());
182 AddDeleteOldIELowRightsPolicyWorkItems(installer_state, work_items.get());
184 installer::RefreshElevationPolicy();
187 void ClearRlzProductState() {
188 const rlz_lib::AccessPoint points[] = {rlz_lib::CHROME_OMNIBOX,
189 rlz_lib::CHROME_HOME_PAGE,
190 rlz_lib::NO_ACCESS_POINT};
192 rlz_lib::ClearProductState(rlz_lib::CHROME, points);
194 // If chrome has been reactivated, clear all events for this brand as well.
195 string16 reactivation_brand_wide;
196 if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand_wide)) {
197 std::string reactivation_brand(WideToASCII(reactivation_brand_wide));
198 rlz_lib::SupplementaryBranding branding(reactivation_brand.c_str());
199 rlz_lib::ClearProductState(rlz_lib::CHROME, points);
203 // Decides whether setup.exe and the installer archive should be removed based
204 // on the original and installer states:
205 // * non-multi product being uninstalled: remove both
206 // * any multi product left besides App Host: keep both
207 // * only App Host left: keep setup.exe
208 void CheckShouldRemoveSetupAndArchive(
209 const installer::InstallationState& original_state,
210 const installer::InstallerState& installer_state,
212 bool* remove_archive) {
213 *remove_setup = true;
214 *remove_archive = true;
216 // If any multi-install product is left (other than App Host) we must leave
217 // the installer and archive. For the App Host, we only leave the installer.
218 if (!installer_state.is_multi_install()) {
219 VLOG(1) << "Removing all installer files for a non-multi installation.";
221 // Loop through all known products...
222 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
223 BrowserDistribution::Type dist_type =
224 static_cast<BrowserDistribution::Type>(i);
225 const installer::ProductState* product_state =
226 original_state.GetProductState(
227 installer_state.system_install(), dist_type);
228 // If the product is installed, in multi mode, and is not part of the
229 // active uninstallation...
230 if (product_state && product_state->is_multi_install() &&
231 !installer_state.FindProduct(dist_type)) {
232 // setup.exe will not be removed as there is a remaining multi-install
234 *remove_setup = false;
235 // As a special case, we can still remove the actual archive if the
236 // only remaining product is the App Host.
237 if (dist_type != BrowserDistribution::CHROME_APP_HOST) {
238 VLOG(1) << "Keeping all installer files due to a remaining "
239 << "multi-install product.";
240 *remove_archive = false;
243 VLOG(1) << "Keeping setup.exe due to a remaining "
244 << "app-host installation.";
247 VLOG(1) << "Removing the installer archive.";
249 VLOG(1) << "Removing setup.exe.";
253 // Removes all files from the installer directory, leaving setup.exe iff
254 // |remove_setup| is false.
255 // Returns false in case of an error.
256 bool RemoveInstallerFiles(const base::FilePath& installer_directory,
258 base::FileEnumerator file_enumerator(
261 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
264 base::FilePath setup_exe_base_name(installer::kSetupExe);
267 base::FilePath to_delete(file_enumerator.Next());
268 if (to_delete.empty())
270 if (!remove_setup && to_delete.BaseName() == setup_exe_base_name)
273 VLOG(1) << "Deleting installer path " << to_delete.value();
274 if (!base::DeleteFile(to_delete, true)) {
275 LOG(ERROR) << "Failed to delete path: " << to_delete.value();
285 namespace installer {
287 // Kills all Chrome processes, immediately.
288 void CloseAllChromeProcesses() {
289 base::CleanupProcesses(installer::kChromeExe, base::TimeDelta(),
290 content::RESULT_CODE_HUNG, NULL);
291 base::CleanupProcesses(installer::kNaClExe, base::TimeDelta(),
292 content::RESULT_CODE_HUNG, NULL);
295 // Attempts to close the Chrome Frame helper process by sending WM_CLOSE
296 // messages to its window, or just killing it if that doesn't work.
297 void CloseChromeFrameHelperProcess() {
298 HWND window = FindWindow(installer::kChromeFrameHelperWndClass, NULL);
299 if (!::IsWindow(window))
302 const DWORD kWaitMs = 3000;
305 ::GetWindowThreadProcessId(window, &pid);
307 base::win::ScopedHandle process(::OpenProcess(SYNCHRONIZE, FALSE, pid));
308 PLOG_IF(INFO, !process) << "Failed to open process: " << pid;
311 if (SendMessageTimeout(window, WM_CLOSE, 0, 0, SMTO_BLOCK, kWaitMs, NULL) &&
313 VLOG(1) << "Waiting for " << installer::kChromeFrameHelperExe;
314 DWORD wait = ::WaitForSingleObject(process, kWaitMs);
315 if (wait != WAIT_OBJECT_0) {
316 LOG(WARNING) << "Wait for " << installer::kChromeFrameHelperExe
317 << " to exit failed or timed out.";
320 VLOG(1) << installer::kChromeFrameHelperExe << " exited normally.";
325 VLOG(1) << installer::kChromeFrameHelperExe << " hung. Killing.";
326 base::CleanupProcesses(installer::kChromeFrameHelperExe, base::TimeDelta(),
327 content::RESULT_CODE_HUNG, NULL);
331 // Updates shortcuts to |old_target_exe| that have non-empty args, making them
332 // target |new_target_exe| instead. The non-empty args requirement is a
333 // heuristic to determine whether a shortcut is "user-generated". This routine
334 // can only be called for user-level installs.
335 void RetargetUserShortcutsWithArgs(const InstallerState& installer_state,
336 const Product& product,
337 const base::FilePath& old_target_exe,
338 const base::FilePath& new_target_exe) {
339 if (installer_state.system_install()) {
343 BrowserDistribution* dist = product.distribution();
344 ShellUtil::ShellChange install_level = ShellUtil::CURRENT_USER;
345 ShellUtil::ShortcutProperties updated_properties(install_level);
346 updated_properties.set_target(new_target_exe);
348 // TODO(huangs): Make this data-driven, along with DeleteShortcuts().
349 VLOG(1) << "Retargeting Desktop shortcuts.";
350 if (!ShellUtil::UpdateShortcutsWithArgs(
351 ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist, install_level,
352 old_target_exe, updated_properties)) {
353 LOG(WARNING) << "Failed to retarget Desktop shortcuts.";
356 VLOG(1) << "Retargeting Quick Launch shortcuts.";
357 if (!ShellUtil::UpdateShortcutsWithArgs(
358 ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH, dist, install_level,
359 old_target_exe, updated_properties)) {
360 LOG(WARNING) << "Failed to retarget Quick Launch shortcuts.";
363 VLOG(1) << "Retargeting Start Menu shortcuts.";
364 if (!ShellUtil::UpdateShortcutsWithArgs(
365 ShellUtil::SHORTCUT_LOCATION_START_MENU, dist, install_level,
366 old_target_exe, updated_properties)) {
367 LOG(WARNING) << "Failed to retarget Start Menu shortcuts.";
370 // Retarget pinned-to-taskbar shortcuts that point to |chrome_exe|.
371 if (!ShellUtil::UpdateShortcutsWithArgs(
372 ShellUtil::SHORTCUT_LOCATION_TASKBAR_PINS, dist,
373 ShellUtil::CURRENT_USER, old_target_exe, updated_properties)) {
374 LOG(WARNING) << "Failed to retarget taskbar shortcuts at user-level.";
377 // Retarget the folder of secondary tiles from the start screen for |dist|.
378 if (!ShellUtil::UpdateShortcutsWithArgs(
379 ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS, dist, install_level,
380 old_target_exe, updated_properties)) {
381 LOG(WARNING) << "Failed to retarget start-screen shortcuts.";
385 // Deletes shortcuts at |install_level| from Start menu, Desktop,
386 // Quick Launch, taskbar, and secondary tiles on the Start Screen (Win8+).
387 // Only shortcuts pointing to |target_exe| will be removed.
388 void DeleteShortcuts(const InstallerState& installer_state,
389 const Product& product,
390 const base::FilePath& target_exe) {
391 BrowserDistribution* dist = product.distribution();
393 // The per-user shortcut for this user, if present on a system-level install,
394 // has already been deleted in chrome_browser_main_win.cc::DoUninstallTasks().
395 ShellUtil::ShellChange install_level = installer_state.system_install() ?
396 ShellUtil::SYSTEM_LEVEL : ShellUtil::CURRENT_USER;
398 VLOG(1) << "Deleting Desktop shortcuts.";
399 if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_DESKTOP, dist,
400 install_level, target_exe)) {
401 LOG(WARNING) << "Failed to delete Desktop shortcuts.";
404 VLOG(1) << "Deleting Quick Launch shortcuts.";
405 if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH,
406 dist, install_level, target_exe)) {
407 LOG(WARNING) << "Failed to delete Quick Launch shortcuts.";
410 VLOG(1) << "Deleting Start Menu shortcuts.";
411 if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_START_MENU, dist,
412 install_level, target_exe)) {
413 LOG(WARNING) << "Failed to delete Start Menu shortcuts.";
416 // Unpin all pinned-to-taskbar shortcuts that point to |chrome_exe|.
417 if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_TASKBAR_PINS,
418 dist, ShellUtil::CURRENT_USER, target_exe)) {
419 LOG(WARNING) << "Failed to unpin taskbar shortcuts at user-level.";
422 // Delete the folder of secondary tiles from the start screen for |dist|.
423 if (!ShellUtil::RemoveShortcuts(ShellUtil::SHORTCUT_LOCATION_APP_SHORTCUTS,
424 dist, install_level, target_exe)) {
425 LOG(WARNING) << "Failed to delete start-screen shortcuts.";
429 bool ScheduleParentAndGrandparentForDeletion(const base::FilePath& path) {
430 base::FilePath parent_dir = path.DirName();
431 bool ret = ScheduleFileSystemEntityForDeletion(parent_dir);
433 LOG(ERROR) << "Failed to schedule parent dir for deletion: "
434 << parent_dir.value();
436 base::FilePath grandparent_dir(parent_dir.DirName());
437 ret = ScheduleFileSystemEntityForDeletion(grandparent_dir);
439 LOG(ERROR) << "Failed to schedule grandparent dir for deletion: "
440 << grandparent_dir.value();
446 // Deletes the given directory if it is empty. Returns DELETE_SUCCEEDED if the
447 // directory is deleted, DELETE_NOT_EMPTY if it is not empty, and DELETE_FAILED
449 DeleteResult DeleteEmptyDir(const base::FilePath& path) {
450 if (!file_util::IsDirectoryEmpty(path))
451 return DELETE_NOT_EMPTY;
453 if (base::DeleteFile(path, true))
454 return DELETE_SUCCEEDED;
456 LOG(ERROR) << "Failed to delete folder: " << path.value();
457 return DELETE_FAILED;
460 void GetLocalStateFolders(const Product& product,
461 std::vector<base::FilePath>* paths) {
462 // Obtain the location of the user profile data.
463 product.GetUserDataPaths(paths);
464 LOG_IF(ERROR, paths->empty())
465 << "Could not retrieve user's profile directory.";
468 // Creates a copy of the local state file and returns a path to the copy.
469 base::FilePath BackupLocalStateFile(
470 const std::vector<base::FilePath>& local_state_folders) {
471 base::FilePath backup;
473 // Copy the first local state file that is found.
474 for (size_t i = 0; i < local_state_folders.size(); ++i) {
475 const base::FilePath& local_state_folder = local_state_folders[i];
476 base::FilePath state_file(
477 local_state_folder.Append(chrome::kLocalStateFilename));
478 if (!base::PathExists(state_file))
480 if (!file_util::CreateTemporaryFile(&backup))
481 LOG(ERROR) << "Failed to create temporary file for Local State.";
483 base::CopyFile(state_file, backup);
489 // Deletes all user data directories for a product.
490 DeleteResult DeleteLocalState(
491 const std::vector<base::FilePath>& local_state_folders,
492 bool schedule_on_failure) {
493 if (local_state_folders.empty())
494 return DELETE_SUCCEEDED;
496 DeleteResult result = DELETE_SUCCEEDED;
497 for (size_t i = 0; i < local_state_folders.size(); ++i) {
498 const base::FilePath& user_local_state = local_state_folders[i];
499 VLOG(1) << "Deleting user profile " << user_local_state.value();
500 if (!base::DeleteFile(user_local_state, true)) {
501 LOG(ERROR) << "Failed to delete user profile dir: "
502 << user_local_state.value();
503 if (schedule_on_failure) {
504 ScheduleDirectoryForDeletion(user_local_state);
505 result = DELETE_REQUIRES_REBOOT;
507 result = DELETE_FAILED;
512 if (result == DELETE_REQUIRES_REBOOT) {
513 ScheduleParentAndGrandparentForDeletion(local_state_folders[0]);
515 const base::FilePath user_data_dir(local_state_folders[0].DirName());
516 if (!user_data_dir.empty() &&
517 DeleteEmptyDir(user_data_dir) == DELETE_SUCCEEDED) {
518 const base::FilePath product_dir(user_data_dir.DirName());
519 if (!product_dir.empty())
520 DeleteEmptyDir(product_dir);
527 // Moves setup to a temporary file, outside of the install folder. Also attempts
528 // to change the current directory to the TMP directory. On Windows, each
529 // process has a handle to its CWD. If setup.exe's CWD happens to be within the
530 // install directory, deletion will fail as a result of the open handle.
531 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state,
532 const base::FilePath& setup_exe) {
534 base::FilePath tmp_dir;
535 base::FilePath temp_file;
536 if (!PathService::Get(base::DIR_TEMP, &tmp_dir)) {
538 } else if (!file_util::CreateTemporaryFileInDir(tmp_dir, &temp_file)) {
539 LOG(ERROR) << "Failed to create temporary file for setup.exe.";
541 VLOG(1) << "Changing current directory to: " << tmp_dir.value();
542 if (!file_util::SetCurrentDirectory(tmp_dir))
543 PLOG(ERROR) << "Failed to change the current directory.";
545 VLOG(1) << "Attempting to move setup to: " << temp_file.value();
546 ret = base::Move(setup_exe, temp_file);
547 PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value();
549 // We cannot delete the file right away, but try to delete it some other
550 // way. Either with the help of a different process or the system.
551 if (ret && !base::DeleteFileAfterReboot(temp_file)) {
552 static const uint32 kDeleteAfterMs = 10 * 1000;
553 installer::DeleteFileFromTempProcess(temp_file, kDeleteAfterMs);
559 DeleteResult DeleteChromeDirectoriesIfEmpty(
560 const base::FilePath& application_directory) {
561 DeleteResult result(DeleteEmptyDir(application_directory));
562 if (result == DELETE_SUCCEEDED) {
563 // Now check and delete if the parent directories are empty
564 // For example Google\Chrome or Chromium
565 const base::FilePath product_directory(application_directory.DirName());
566 if (!product_directory.empty()) {
567 result = DeleteEmptyDir(product_directory);
568 if (result == DELETE_SUCCEEDED) {
569 const base::FilePath vendor_directory(product_directory.DirName());
570 if (!vendor_directory.empty())
571 result = DeleteEmptyDir(vendor_directory);
575 if (result == DELETE_NOT_EMPTY)
576 result = DELETE_SUCCEEDED;
580 DeleteResult DeleteAppHostFilesAndFolders(const InstallerState& installer_state,
581 const Version& installed_version) {
582 const base::FilePath& target_path = installer_state.target_path();
583 if (target_path.empty()) {
584 LOG(ERROR) << "DeleteAppHostFilesAndFolders: no installation destination "
586 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return.
589 DeleteInstallTempDir(target_path);
591 DeleteResult result = DELETE_SUCCEEDED;
593 base::FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe));
594 if (!base::DeleteFile(app_host_exe, false)) {
595 result = DELETE_FAILED;
596 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value();
602 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state,
603 const base::FilePath& setup_exe) {
604 const base::FilePath& target_path = installer_state.target_path();
605 if (target_path.empty()) {
606 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination "
608 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return.
611 DeleteInstallTempDir(target_path);
613 DeleteResult result = DELETE_SUCCEEDED;
615 base::FilePath installer_directory;
616 if (target_path.IsParent(setup_exe))
617 installer_directory = setup_exe.DirName();
619 // Enumerate all the files in target_path recursively (breadth-first).
620 // We delete a file or folder unless it is a parent/child of the installer
621 // directory. For parents of the installer directory, we will later recurse
622 // and delete all the children (that are not also parents/children of the
623 // installer directory).
624 base::FileEnumerator file_enumerator(target_path, true,
625 base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
627 base::FilePath to_delete(file_enumerator.Next());
628 if (to_delete.empty())
630 if (to_delete.BaseName().value() == installer::kChromeAppHostExe)
632 if (!installer_directory.empty() &&
633 (to_delete == installer_directory ||
634 installer_directory.IsParent(to_delete) ||
635 to_delete.IsParent(installer_directory))) {
639 VLOG(1) << "Deleting install path " << to_delete.value();
640 if (!base::DeleteFile(to_delete, true)) {
641 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value();
642 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) {
643 // We don't try killing Chrome processes for Chrome Frame builds since
644 // that is unlikely to help. Instead, schedule files for deletion and
645 // return a value that will trigger a reboot prompt.
646 base::FileEnumerator::FileInfo find_info = file_enumerator.GetInfo();
647 if (find_info.IsDirectory())
648 ScheduleDirectoryForDeletion(to_delete);
650 ScheduleFileSystemEntityForDeletion(to_delete);
651 result = DELETE_REQUIRES_REBOOT;
653 // Try closing any running Chrome processes and deleting files once
655 CloseAllChromeProcesses();
656 if (!base::DeleteFile(to_delete, true)) {
657 LOG(ERROR) << "Failed to delete path (2nd try): "
658 << to_delete.value();
659 result = DELETE_FAILED;
669 // This method checks if Chrome is currently running or if the user has
670 // cancelled the uninstall operation by clicking Cancel on the confirmation
671 // box that Chrome pops up.
672 InstallStatus IsChromeActiveOrUserCancelled(
673 const InstallerState& installer_state,
674 const Product& product) {
675 int32 exit_code = content::RESULT_CODE_NORMAL_EXIT;
676 CommandLine options(CommandLine::NO_PROGRAM);
677 options.AppendSwitch(installer::switches::kUninstall);
679 // Here we want to save user from frustration (in case of Chrome crashes)
680 // and continue with the uninstallation as long as chrome.exe process exit
681 // code is NOT one of the following:
682 // - UNINSTALL_CHROME_ALIVE - chrome.exe is currently running
683 // - UNINSTALL_USER_CANCEL - User cancelled uninstallation
684 // - HUNG - chrome.exe was killed by HuntForZombieProcesses() (until we can
685 // give this method some brains and not kill chrome.exe launched
686 // by us, we will not uninstall if we get this return code).
687 VLOG(1) << "Launching Chrome to do uninstall tasks.";
688 if (product.LaunchChromeAndWait(installer_state.target_path(), options,
690 VLOG(1) << "chrome.exe launched for uninstall confirmation returned: "
692 if ((exit_code == chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE) ||
693 (exit_code == chrome::RESULT_CODE_UNINSTALL_USER_CANCEL) ||
694 (exit_code == content::RESULT_CODE_HUNG))
695 return installer::UNINSTALL_CANCELLED;
697 if (exit_code == chrome::RESULT_CODE_UNINSTALL_DELETE_PROFILE)
698 return installer::UNINSTALL_DELETE_PROFILE;
700 PLOG(ERROR) << "Failed to launch chrome.exe for uninstall confirmation.";
703 return installer::UNINSTALL_CONFIRMED;
706 bool ShouldDeleteProfile(const InstallerState& installer_state,
707 const CommandLine& cmd_line, InstallStatus status,
708 const Product& product) {
709 bool should_delete = false;
711 // Chrome Frame uninstallations always want to delete the profile (we have no
712 // UI to prompt otherwise and the profile stores no useful data anyway)
713 // unless they are managed by MSI. MSI uninstalls will explicitly include
714 // the --delete-profile flag to distinguish them from MSI upgrades.
715 if (product.is_chrome_frame() && !installer_state.is_msi()) {
716 should_delete = true;
719 status == installer::UNINSTALL_DELETE_PROFILE ||
720 cmd_line.HasSwitch(installer::switches::kDeleteProfile);
723 return should_delete;
726 // Removes XP-era filetype registration making Chrome the default browser.
727 // MSDN (see http://msdn.microsoft.com/library/windows/desktop/cc144148.aspx)
728 // tells us not to do this, but certain applications break following
729 // uninstallation if we don't.
730 void RemoveFiletypeRegistration(const InstallerState& installer_state,
732 const string16& browser_entry_suffix) {
733 string16 classes_path(ShellUtil::kRegClasses);
734 classes_path.push_back(base::FilePath::kSeparators[0]);
736 BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
737 const string16 prog_id(
738 distribution->GetBrowserProgIdPrefix() + browser_entry_suffix);
740 // Delete each filetype association if it references this Chrome. Take care
741 // not to delete the association if it references a system-level install of
742 // Chrome (only a risk if the suffix is empty). Don't delete the whole key
743 // since other apps may have stored data there.
744 std::vector<const wchar_t*> cleared_assocs;
745 if (installer_state.system_install() ||
746 !browser_entry_suffix.empty() ||
747 !base::win::RegKey(HKEY_LOCAL_MACHINE, (classes_path + prog_id).c_str(),
748 KEY_QUERY_VALUE).Valid()) {
749 InstallUtil::ValueEquals prog_id_pred(prog_id);
750 for (const wchar_t* const* filetype =
751 &ShellUtil::kPotentialFileAssociations[0]; *filetype != NULL;
753 if (InstallUtil::DeleteRegistryValueIf(
754 root, (classes_path + *filetype).c_str(), NULL,
755 prog_id_pred) == InstallUtil::DELETED) {
756 cleared_assocs.push_back(*filetype);
761 // For all filetype associations in HKLM that have just been removed, attempt
762 // to restore some reasonable value. We have no definitive way of knowing
763 // what handlers are the most appropriate, so we use a fixed mapping based on
764 // the default values for a fresh install of Windows.
765 if (root == HKEY_LOCAL_MACHINE) {
767 base::win::RegKey key;
769 for (size_t i = 0; i < cleared_assocs.size(); ++i) {
770 const wchar_t* replacement_prog_id = NULL;
771 assoc.assign(cleared_assocs[i]);
773 // Inelegant, but simpler than a pure data-driven approach.
774 if (assoc == L".htm" || assoc == L".html")
775 replacement_prog_id = L"htmlfile";
776 else if (assoc == L".xht" || assoc == L".xhtml")
777 replacement_prog_id = L"xhtmlfile";
779 if (!replacement_prog_id) {
780 LOG(WARNING) << "No known replacement ProgID for " << assoc
782 } else if (key.Open(HKEY_LOCAL_MACHINE,
783 (classes_path + replacement_prog_id).c_str(),
784 KEY_QUERY_VALUE) == ERROR_SUCCESS &&
785 (key.Open(HKEY_LOCAL_MACHINE, (classes_path + assoc).c_str(),
786 KEY_SET_VALUE) != ERROR_SUCCESS ||
787 key.WriteValue(NULL, replacement_prog_id) != ERROR_SUCCESS)) {
788 // The replacement ProgID is registered on the computer but the attempt
789 // to set it for the filetype failed.
790 LOG(ERROR) << "Failed to restore system-level filetype association "
791 << assoc << " = " << replacement_prog_id;
797 bool DeleteChromeRegistrationKeys(const InstallerState& installer_state,
798 BrowserDistribution* dist,
800 const string16& browser_entry_suffix,
801 InstallStatus* exit_code) {
803 if (dist->GetDefaultBrowserControlPolicy() ==
804 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
805 // We should have never set those keys.
809 base::FilePath chrome_exe(installer_state.target_path().Append(kChromeExe));
811 // Delete Software\Classes\ChromeHTML.
812 const string16 prog_id(
813 dist->GetBrowserProgIdPrefix() + browser_entry_suffix);
814 string16 reg_prog_id(ShellUtil::kRegClasses);
815 reg_prog_id.push_back(base::FilePath::kSeparators[0]);
816 reg_prog_id.append(prog_id);
817 InstallUtil::DeleteRegistryKey(root, reg_prog_id);
819 // Delete Software\Classes\Chrome.
820 string16 reg_app_id(ShellUtil::kRegClasses);
821 reg_app_id.push_back(base::FilePath::kSeparators[0]);
822 // Append the requested suffix manually here (as ShellUtil::GetBrowserModelId
823 // would otherwise try to figure out the currently installed suffix).
824 reg_app_id.append(dist->GetBaseAppId() + browser_entry_suffix);
825 InstallUtil::DeleteRegistryKey(root, reg_app_id);
827 // Delete all Start Menu Internet registrations that refer to this Chrome.
829 using base::win::RegistryKeyIterator;
830 InstallUtil::ProgramCompare open_command_pred(chrome_exe);
831 string16 client_name;
834 for (RegistryKeyIterator iter(root, ShellUtil::kRegStartMenuInternet);
835 iter.Valid(); ++iter) {
836 client_name.assign(iter.Name());
837 client_key.assign(ShellUtil::kRegStartMenuInternet)
839 .append(client_name);
840 open_key.assign(client_key).append(ShellUtil::kRegShellOpen);
841 if (InstallUtil::DeleteRegistryKeyIf(root, client_key, open_key, NULL,
842 open_command_pred) != InstallUtil::NOT_FOUND) {
843 // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
844 // references this Chrome (i.e., if it was made the default browser).
845 InstallUtil::DeleteRegistryValueIf(
846 root, ShellUtil::kRegStartMenuInternet, NULL,
847 InstallUtil::ValueEquals(client_name));
848 // Also delete the value for the default user if we're operating in
850 if (root == HKEY_LOCAL_MACHINE) {
851 InstallUtil::DeleteRegistryValueIf(
853 string16(L".DEFAULT\\").append(
854 ShellUtil::kRegStartMenuInternet).c_str(),
855 NULL, InstallUtil::ValueEquals(client_name));
861 // Delete Software\RegisteredApplications\Chromium
862 InstallUtil::DeleteRegistryValue(
863 root, ShellUtil::kRegRegisteredApplications,
864 dist->GetBaseAppName() + browser_entry_suffix);
866 // Delete the App Paths and Applications keys that let Explorer find Chrome:
867 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
868 string16 app_key(ShellUtil::kRegClasses);
869 app_key.push_back(base::FilePath::kSeparators[0]);
870 app_key.append(L"Applications");
871 app_key.push_back(base::FilePath::kSeparators[0]);
872 app_key.append(installer::kChromeExe);
873 InstallUtil::DeleteRegistryKey(root, app_key);
875 string16 app_path_key(ShellUtil::kAppPathsRegistryKey);
876 app_path_key.push_back(base::FilePath::kSeparators[0]);
877 app_path_key.append(installer::kChromeExe);
878 InstallUtil::DeleteRegistryKey(root, app_path_key);
880 // Cleanup OpenWithList and OpenWithProgids:
881 // http://msdn.microsoft.com/en-us/library/bb166549
882 string16 file_assoc_key;
883 string16 open_with_list_key;
884 string16 open_with_progids_key;
885 for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; ++i) {
886 file_assoc_key.assign(ShellUtil::kRegClasses);
887 file_assoc_key.push_back(base::FilePath::kSeparators[0]);
888 file_assoc_key.append(ShellUtil::kPotentialFileAssociations[i]);
889 file_assoc_key.push_back(base::FilePath::kSeparators[0]);
891 open_with_list_key.assign(file_assoc_key);
892 open_with_list_key.append(L"OpenWithList");
893 open_with_list_key.push_back(base::FilePath::kSeparators[0]);
894 open_with_list_key.append(installer::kChromeExe);
895 InstallUtil::DeleteRegistryKey(root, open_with_list_key);
897 open_with_progids_key.assign(file_assoc_key);
898 open_with_progids_key.append(ShellUtil::kRegOpenWithProgids);
899 InstallUtil::DeleteRegistryValue(root, open_with_progids_key, prog_id);
902 // Cleanup in case Chrome had been made the default browser.
904 // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
905 // references this Chrome. Do this explicitly here for the case where HKCU is
906 // being processed; the iteration above will have no hits since registration
908 InstallUtil::DeleteRegistryValueIf(
909 root, ShellUtil::kRegStartMenuInternet, NULL,
910 InstallUtil::ValueEquals(dist->GetBaseAppName() + browser_entry_suffix));
912 // Delete each protocol association if it references this Chrome.
913 InstallUtil::ProgramCompare open_command_pred(chrome_exe);
914 string16 parent_key(ShellUtil::kRegClasses);
915 parent_key.push_back(base::FilePath::kSeparators[0]);
916 const string16::size_type base_length = parent_key.size();
918 for (const wchar_t* const* proto =
919 &ShellUtil::kPotentialProtocolAssociations[0];
922 parent_key.resize(base_length);
923 parent_key.append(*proto);
924 child_key.assign(parent_key).append(ShellUtil::kRegShellOpen);
925 InstallUtil::DeleteRegistryKeyIf(root, parent_key, child_key, NULL,
929 RemoveFiletypeRegistration(installer_state, root, browser_entry_suffix);
931 *exit_code = installer::UNINSTALL_SUCCESSFUL;
935 void RemoveChromeLegacyRegistryKeys(BrowserDistribution* dist,
936 const string16& chrome_exe) {
937 // We used to register Chrome to handle crx files, but this turned out
938 // to be not worth the hassle. Remove these old registry entries if
939 // they exist. See: http://codereview.chromium.org/210007
941 #if defined(GOOGLE_CHROME_BUILD)
942 const wchar_t kChromeExtProgId[] = L"ChromeExt";
944 const wchar_t kChromeExtProgId[] = L"ChromiumExt";
947 HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
948 for (size_t i = 0; i < arraysize(roots); ++i) {
950 if (roots[i] == HKEY_LOCAL_MACHINE)
951 suffix = ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe);
953 // Delete Software\Classes\ChromeExt,
954 string16 ext_prog_id(ShellUtil::kRegClasses);
955 ext_prog_id.push_back(base::FilePath::kSeparators[0]);
956 ext_prog_id.append(kChromeExtProgId);
957 ext_prog_id.append(suffix);
958 InstallUtil::DeleteRegistryKey(roots[i], ext_prog_id);
960 // Delete Software\Classes\.crx,
961 string16 ext_association(ShellUtil::kRegClasses);
962 ext_association.append(L"\\");
963 ext_association.append(extensions::kExtensionFileExtension);
964 InstallUtil::DeleteRegistryKey(roots[i], ext_association);
968 // Builds and executes a work item list to remove DelegateExecute verb handler
969 // work items for |product|. This will be a noop for products whose
970 // corresponding BrowserDistribution implementations do not publish a CLSID via
971 // GetCommandExecuteImplClsid.
972 bool ProcessDelegateExecuteWorkItems(const InstallerState& installer_state,
973 const Product& product) {
974 scoped_ptr<WorkItemList> item_list(WorkItem::CreateNoRollbackWorkItemList());
975 AddDelegateExecuteWorkItems(installer_state, base::FilePath(), Version(),
976 product, item_list.get());
977 return item_list->Do();
980 // Removes Active Setup entries from the registry. This cannot be done through
981 // a work items list as usual because of different paths based on conditionals,
982 // but otherwise respects the no rollback/best effort uninstall mentality.
983 // This will only apply for system-level installs of Chrome/Chromium and will be
984 // a no-op for all other types of installs.
985 void UninstallActiveSetupEntries(const InstallerState& installer_state,
986 const Product& product) {
987 VLOG(1) << "Uninstalling registry entries for ActiveSetup.";
988 BrowserDistribution* distribution = product.distribution();
990 if (!product.is_chrome() || !installer_state.system_install()) {
991 const char* install_level =
992 installer_state.system_install() ? "system" : "user";
993 VLOG(1) << "No Active Setup processing to do for " << install_level
994 << "-level " << distribution->GetDisplayName();
998 const string16 active_setup_path(
999 InstallUtil::GetActiveSetupPath(distribution));
1000 InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, active_setup_path);
1002 // Windows leaves keys behind in HKCU\\Software\\(Wow6432Node\\)?Microsoft\\
1003 // Active Setup\\Installed Components\\{guid}
1004 // for every user that logged in since system-level Chrome was installed.
1005 // This is a problem because Windows compares the value of the Version subkey
1006 // in there with the value of the Version subkey in the matching HKLM entries
1007 // before running Chrome's Active Setup so if Chrome was to be reinstalled
1008 // with a lesser version (e.g. switching back to a more stable channel), the
1009 // affected users would not have Chrome's Active Setup called until Chrome
1010 // eventually updated passed that user's registered Version.
1012 // It is however very hard to delete those values as the registry hives for
1013 // other users are not loaded by default under HKEY_USERS (unless a user is
1014 // logged on or has a process impersonating him).
1016 // Following our best effort uninstall practices, try to delete the value in
1017 // all users hives. If a given user's hive is not loaded, try to load it to
1018 // proceed with the deletion (failure to do so is ignored).
1020 static const wchar_t kProfileList[] =
1021 L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\";
1023 // Windows automatically adds Wow6432Node when creating/deleting the HKLM key,
1024 // but doesn't seem to do so when manually deleting the user-level keys it
1026 string16 alternate_active_setup_path(active_setup_path);
1027 alternate_active_setup_path.insert(arraysize("Software\\") - 1,
1030 // These two privileges are required by RegLoadKey() and RegUnloadKey() below.
1031 ScopedTokenPrivilege se_restore_name_privilege(SE_RESTORE_NAME);
1032 ScopedTokenPrivilege se_backup_name_privilege(SE_BACKUP_NAME);
1033 if (!se_restore_name_privilege.is_enabled() ||
1034 !se_backup_name_privilege.is_enabled()) {
1035 // This is not a critical failure as those privileges aren't required to
1036 // clean hives that are already loaded, but attempts to LoadRegKey() below
1038 LOG(WARNING) << "Failed to enable privileges required to load registry "
1042 for (base::win::RegistryKeyIterator it(HKEY_LOCAL_MACHINE, kProfileList);
1044 const wchar_t* profile_sid = it.Name();
1046 // First check if this user's registry hive needs to be loaded in
1048 base::win::RegKey user_reg_root_probe(
1049 HKEY_USERS, profile_sid, KEY_READ);
1050 bool loaded_hive = false;
1051 if (!user_reg_root_probe.Valid()) {
1052 VLOG(1) << "Attempting to load registry hive for " << profile_sid;
1054 string16 reg_profile_info_path(kProfileList);
1055 reg_profile_info_path.append(profile_sid);
1056 base::win::RegKey reg_profile_info_key(
1057 HKEY_LOCAL_MACHINE, reg_profile_info_path.c_str(), KEY_READ);
1059 string16 profile_path;
1060 LONG result = reg_profile_info_key.ReadValue(L"ProfileImagePath",
1062 if (result != ERROR_SUCCESS) {
1063 LOG(ERROR) << "Error reading ProfileImagePath: " << result;
1066 base::FilePath registry_hive_file(profile_path);
1067 registry_hive_file = registry_hive_file.AppendASCII("NTUSER.DAT");
1069 result = RegLoadKey(HKEY_USERS, profile_sid,
1070 registry_hive_file.value().c_str());
1071 if (result != ERROR_SUCCESS) {
1072 LOG(ERROR) << "Error loading registry hive: " << result;
1076 VLOG(1) << "Loaded registry hive for " << profile_sid;
1080 base::win::RegKey user_reg_root(
1081 HKEY_USERS, profile_sid, KEY_ALL_ACCESS);
1083 LONG result = user_reg_root.DeleteKey(active_setup_path.c_str());
1084 if (result != ERROR_SUCCESS) {
1085 result = user_reg_root.DeleteKey(alternate_active_setup_path.c_str());
1086 if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
1087 LOG(ERROR) << "Failed to delete key at " << active_setup_path
1088 << " and at " << alternate_active_setup_path
1089 << ", result: " << result;
1094 user_reg_root.Close();
1095 if (RegUnLoadKey(HKEY_USERS, profile_sid) == ERROR_SUCCESS)
1096 VLOG(1) << "Unloaded registry hive for " << profile_sid;
1098 LOG(ERROR) << "Error unloading registry hive for " << profile_sid;
1103 bool ProcessChromeFrameWorkItems(const InstallationState& original_state,
1104 const InstallerState& installer_state,
1105 const base::FilePath& setup_path,
1106 const Product& product) {
1107 if (!product.is_chrome_frame())
1110 scoped_ptr<WorkItemList> item_list(WorkItem::CreateNoRollbackWorkItemList());
1111 AddChromeFrameWorkItems(original_state, installer_state, setup_path,
1112 Version(), product, item_list.get());
1113 return item_list->Do();
1116 InstallStatus UninstallProduct(const InstallationState& original_state,
1117 const InstallerState& installer_state,
1118 const base::FilePath& setup_path,
1119 const Product& product,
1121 bool force_uninstall,
1122 const CommandLine& cmd_line) {
1123 InstallStatus status = installer::UNINSTALL_CONFIRMED;
1124 BrowserDistribution* browser_dist = product.distribution();
1125 const string16 chrome_exe(
1126 installer_state.target_path().Append(installer::kChromeExe).value());
1128 bool is_chrome = product.is_chrome();
1130 VLOG(1) << "UninstallProduct: " << browser_dist->GetDisplayName();
1132 if (force_uninstall) {
1133 // Since --force-uninstall command line option is used, we are going to
1134 // do silent uninstall. Try to close all running Chrome instances.
1135 // NOTE: We don't do this for Chrome Frame.
1137 CloseAllChromeProcesses();
1138 } else if (is_chrome) {
1139 // no --force-uninstall so lets show some UI dialog boxes.
1140 status = IsChromeActiveOrUserCancelled(installer_state, product);
1141 if (status != installer::UNINSTALL_CONFIRMED &&
1142 status != installer::UNINSTALL_DELETE_PROFILE)
1145 const string16 suffix(ShellUtil::GetCurrentInstallationSuffix(browser_dist,
1148 // Check if we need admin rights to cleanup HKLM (the conditions for
1149 // requiring a cleanup are the same as the conditions to do the actual
1150 // cleanup where DeleteChromeRegistrationKeys() is invoked for
1151 // HKEY_LOCAL_MACHINE below). If we do, try to launch another uninstaller
1152 // (silent) in elevated mode to do HKLM cleanup.
1153 // And continue uninstalling in the current process also to do HKCU cleanup.
1155 ShellUtil::QuickIsChromeRegisteredInHKLM(
1156 browser_dist, chrome_exe, suffix) &&
1157 !::IsUserAnAdmin() &&
1158 base::win::GetVersion() >= base::win::VERSION_VISTA &&
1159 !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
1160 CommandLine new_cmd(CommandLine::NO_PROGRAM);
1161 new_cmd.AppendArguments(cmd_line, true);
1162 // Append --run-as-admin flag to let the new instance of setup.exe know
1163 // that we already tried to launch ourselves as admin.
1164 new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
1165 // Append --remove-chrome-registration to remove registry keys only.
1166 new_cmd.AppendSwitch(installer::switches::kRemoveChromeRegistration);
1167 if (!suffix.empty()) {
1168 new_cmd.AppendSwitchNative(
1169 installer::switches::kRegisterChromeBrowserSuffix, suffix);
1171 DWORD exit_code = installer::UNKNOWN_STATUS;
1172 InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
1177 // Chrome is not in use so lets uninstall Chrome by deleting various files
1178 // and registry entries. Here we will just make best effort and keep going
1179 // in case of errors.
1180 ClearRlzProductState();
1181 // Delete the key that delegate_execute might make.
1182 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
1183 InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER,
1184 chrome::kMetroRegistryPath);
1187 auto_launch_util::DisableAllAutoStartFeatures(
1188 ASCIIToUTF16(chrome::kInitialProfile));
1190 // If user-level chrome is self-destructing as a result of encountering a
1191 // system-level chrome, retarget owned non-default shortcuts (app shortcuts,
1192 // profile shortcuts, etc.) to the system-level chrome.
1193 if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
1194 !installer_state.system_install()) {
1195 const base::FilePath system_chrome_path(
1196 GetChromeInstallPath(true, browser_dist).
1197 Append(installer::kChromeExe));
1198 VLOG(1) << "Retargeting user-generated Chrome shortcuts.";
1199 if (base::PathExists(system_chrome_path)) {
1200 RetargetUserShortcutsWithArgs(installer_state, product,
1201 base::FilePath(chrome_exe),
1202 system_chrome_path);
1204 LOG(ERROR) << "Retarget failed: system-level Chrome not found.";
1208 DeleteShortcuts(installer_state, product, base::FilePath(chrome_exe));
1211 // Delete the registry keys (Uninstall key and Version key).
1212 HKEY reg_root = installer_state.root_key();
1214 // Note that we must retrieve the distribution-specific data before deleting
1215 // product.GetVersionKey().
1216 string16 distribution_data(browser_dist->GetDistributionData(reg_root));
1218 // Remove Control Panel uninstall link.
1219 if (product.ShouldCreateUninstallEntry()) {
1220 InstallUtil::DeleteRegistryKey(reg_root,
1221 browser_dist->GetUninstallRegPath());
1224 // Remove Omaha product key.
1225 InstallUtil::DeleteRegistryKey(reg_root, browser_dist->GetVersionKey());
1227 // Also try to delete the MSI value in the ClientState key (it might not be
1228 // there). This is due to a Google Update behaviour where an uninstall and a
1229 // rapid reinstall might result in stale values from the old ClientState key
1230 // being picked up on reinstall.
1231 product.SetMsiMarker(installer_state.system_install(), false);
1233 InstallStatus ret = installer::UNKNOWN_STATUS;
1236 const string16 suffix(ShellUtil::GetCurrentInstallationSuffix(browser_dist,
1239 // Remove all Chrome registration keys.
1240 // Registration data is put in HKCU for both system level and user level
1242 DeleteChromeRegistrationKeys(installer_state, browser_dist,
1243 HKEY_CURRENT_USER, suffix, &ret);
1245 // If the user's Chrome is registered with a suffix: it is possible that old
1246 // unsuffixed registrations were left in HKCU (e.g. if this install was
1247 // previously installed with no suffix in HKCU (old suffix rules if the user
1248 // is not an admin (or declined UAC at first run)) and later had to be
1249 // suffixed when fully registered in HKLM (e.g. when later making Chrome
1250 // default through the UI)).
1251 // Remove remaining HKCU entries with no suffix if any.
1252 if (!suffix.empty()) {
1253 DeleteChromeRegistrationKeys(installer_state, browser_dist,
1254 HKEY_CURRENT_USER, string16(), &ret);
1256 // For similar reasons it is possible in very few installs (from
1257 // 21.0.1180.0 and fixed shortly after) to be installed with the new-style
1258 // suffix, but have some old-style suffix registrations left behind.
1259 string16 old_style_suffix;
1260 if (ShellUtil::GetOldUserSpecificRegistrySuffix(&old_style_suffix) &&
1261 suffix != old_style_suffix) {
1262 DeleteChromeRegistrationKeys(installer_state, browser_dist,
1263 HKEY_CURRENT_USER, old_style_suffix, &ret);
1267 // Chrome is registered in HKLM for all system-level installs and for
1268 // user-level installs for which Chrome has been made the default browser.
1269 // Always remove the HKLM registration for system-level installs. For
1270 // user-level installs, only remove it if both: 1) this uninstall isn't a
1271 // self destruct following the installation of a system-level Chrome
1272 // (because the system-level Chrome owns the HKLM registration now), and 2)
1273 // this user has made Chrome their default browser (i.e. has shell
1274 // integration entries registered with |suffix| (note: |suffix| will be the
1275 // empty string if required as it is obtained by
1276 // GetCurrentInstallationSuffix() above)).
1277 // TODO(gab): This can still leave parts of a suffixed install behind. To be
1278 // able to remove them we would need to be able to remove only suffixed
1279 // entries (as it is now some of the registry entries (e.g. App Paths) are
1280 // unsuffixed; thus removing suffixed installs is prohibited in HKLM if
1281 // !|remove_all| for now).
1282 if (installer_state.system_install() ||
1284 ShellUtil::QuickIsChromeRegisteredInHKLM(
1285 browser_dist, chrome_exe, suffix))) {
1286 DeleteChromeRegistrationKeys(installer_state, browser_dist,
1287 HKEY_LOCAL_MACHINE, suffix, &ret);
1290 ProcessDelegateExecuteWorkItems(installer_state, product);
1292 ProcessOnOsUpgradeWorkItems(installer_state, product);
1294 UninstallActiveSetupEntries(installer_state, product);
1296 // Notify the shell that associations have changed since Chrome was likely
1298 SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
1300 // TODO(huangs): Implement actual migration code and remove the hack below.
1301 // Remove the "shadow" App Launcher registry keys.
1302 if (installer_state.is_multi_install()) {
1303 // If we're not uninstalling the legacy App Launcher, and if it was
1304 // not installed in the first place, then delete the "shadow" keys.
1305 chrome_launcher_support::InstallationState level_to_check =
1306 installer_state.system_install() ?
1307 chrome_launcher_support::INSTALLED_AT_SYSTEM_LEVEL :
1308 chrome_launcher_support::INSTALLED_AT_USER_LEVEL;
1309 bool has_legacy_app_launcher = level_to_check ==
1310 chrome_launcher_support::GetAppLauncherInstallationState();
1311 if (!has_legacy_app_launcher) {
1312 BrowserDistribution* shadow_app_launcher_dist =
1313 BrowserDistribution::GetSpecificDistribution(
1314 BrowserDistribution::CHROME_APP_HOST);
1315 InstallUtil::DeleteRegistryKey(reg_root,
1316 shadow_app_launcher_dist->GetVersionKey());
1321 if (product.is_chrome_frame()) {
1322 ProcessChromeFrameWorkItems(original_state, installer_state, setup_path,
1326 if (installer_state.is_multi_install()) {
1327 ProcessGoogleUpdateItems(original_state, installer_state, product);
1329 ProcessQuickEnableWorkItems(installer_state, original_state);
1332 // Get the state of the installed product (if any)
1333 const ProductState* product_state =
1334 original_state.GetProductState(installer_state.system_install(),
1335 browser_dist->GetType());
1337 // Delete shared registry keys as well (these require admin rights) if
1338 // remove_all option is specified.
1340 if (!InstallUtil::IsChromeSxSProcess() && is_chrome) {
1341 // Delete media player registry key that exists only in HKLM.
1342 // We don't delete this key in SxS uninstall or Chrome Frame uninstall
1343 // as we never set the key for those products.
1344 string16 reg_path(installer::kMediaPlayerRegPath);
1345 reg_path.push_back(base::FilePath::kSeparators[0]);
1346 reg_path.append(installer::kChromeExe);
1347 InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path);
1350 // Unregister any dll servers that we may have registered for this
1352 if (product_state != NULL) {
1353 std::vector<base::FilePath> com_dll_list;
1354 product.AddComDllList(&com_dll_list);
1355 base::FilePath dll_folder = installer_state.target_path().AppendASCII(
1356 product_state->version().GetString());
1358 scoped_ptr<WorkItemList> unreg_work_item_list(
1359 WorkItem::CreateWorkItemList());
1361 AddRegisterComDllWorkItems(dll_folder,
1363 installer_state.system_install(),
1364 false, // Unregister
1366 unreg_work_item_list.get());
1367 unreg_work_item_list->Do();
1370 if (product.is_chrome_frame())
1371 ProcessIELowRightsPolicyWorkItems(installer_state);
1374 // Close any Chrome Frame helper processes that may be running.
1375 if (product.is_chrome_frame()) {
1376 VLOG(1) << "Closing the Chrome Frame helper process";
1377 CloseChromeFrameHelperProcess();
1380 if (product_state == NULL)
1381 return installer::UNINSTALL_SUCCESSFUL;
1383 // Finally delete all the files from Chrome folder after moving setup.exe
1384 // and the user's Local State to a temp location.
1385 bool delete_profile = ShouldDeleteProfile(installer_state, cmd_line, status,
1387 ret = installer::UNINSTALL_SUCCESSFUL;
1389 // When deleting files, we must make sure that we're either a "single"
1390 // (aka non-multi) installation or we are the Chrome Binaries.
1392 std::vector<base::FilePath> local_state_folders;
1393 GetLocalStateFolders(product, &local_state_folders);
1394 base::FilePath backup_state_file(BackupLocalStateFile(local_state_folders));
1396 if (product.is_chrome_app_host()) {
1397 DeleteAppHostFilesAndFolders(installer_state, product_state->version());
1398 } else if (!installer_state.is_multi_install() ||
1399 product.is_chrome_binaries()) {
1400 base::FilePath setup_exe(base::MakeAbsoluteFilePath(cmd_line.GetProgram()));
1401 DeleteResult delete_result = DeleteChromeFilesAndFolders(
1402 installer_state, setup_exe);
1403 if (delete_result == DELETE_FAILED) {
1404 ret = installer::UNINSTALL_FAILED;
1405 } else if (delete_result == DELETE_REQUIRES_REBOOT) {
1406 ret = installer::UNINSTALL_REQUIRES_REBOOT;
1411 DeleteLocalState(local_state_folders, product.is_chrome_frame());
1413 if (!force_uninstall) {
1414 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations.";
1415 browser_dist->DoPostUninstallOperations(product_state->version(),
1416 backup_state_file, distribution_data);
1419 // Try and delete the preserved local state once the post-install
1420 // operations are complete.
1421 if (!backup_state_file.empty())
1422 base::DeleteFile(backup_state_file, false);
1427 void CleanUpInstallationDirectoryAfterUninstall(
1428 const InstallationState& original_state,
1429 const InstallerState& installer_state,
1430 const CommandLine& cmd_line,
1431 InstallStatus* uninstall_status) {
1432 if (*uninstall_status != UNINSTALL_SUCCESSFUL &&
1433 *uninstall_status != UNINSTALL_REQUIRES_REBOOT) {
1436 const base::FilePath target_path(installer_state.target_path());
1437 if (target_path.empty()) {
1438 LOG(ERROR) << "No installation destination path.";
1439 *uninstall_status = UNINSTALL_FAILED;
1442 base::FilePath setup_exe(base::MakeAbsoluteFilePath(cmd_line.GetProgram()));
1443 if (!target_path.IsParent(setup_exe)) {
1444 LOG(INFO) << "setup.exe is not in target path. Skipping installer cleanup.";
1447 base::FilePath install_directory(setup_exe.DirName());
1449 bool remove_setup = true;
1450 bool remove_archive = true;
1451 CheckShouldRemoveSetupAndArchive(original_state, installer_state,
1452 &remove_setup, &remove_archive);
1453 if (!remove_archive)
1457 // In order to be able to remove the folder in which we're running, we
1458 // need to move setup.exe out of the install folder.
1459 // TODO(tommi): What if the temp folder is on a different volume?
1460 MoveSetupOutOfInstallFolder(installer_state, setup_exe);
1463 // Remove files from "...\<product>\Application\<version>\Installer"
1464 if (!RemoveInstallerFiles(install_directory, remove_setup)) {
1465 *uninstall_status = UNINSTALL_FAILED;
1472 // Try to remove the empty directory hierarchy.
1474 // Delete "...\<product>\Application\<version>\Installer"
1475 if (DeleteEmptyDir(install_directory) != DELETE_SUCCEEDED) {
1476 *uninstall_status = UNINSTALL_FAILED;
1480 // Delete "...\<product>\Application\<version>"
1481 DeleteResult delete_result = DeleteEmptyDir(install_directory.DirName());
1482 if (delete_result == DELETE_FAILED ||
1483 (delete_result == DELETE_NOT_EMPTY &&
1484 *uninstall_status != UNINSTALL_REQUIRES_REBOOT)) {
1485 *uninstall_status = UNINSTALL_FAILED;
1489 if (*uninstall_status == UNINSTALL_REQUIRES_REBOOT) {
1490 // Delete the Application directory at reboot if empty.
1491 ScheduleFileSystemEntityForDeletion(target_path);
1493 // If we need a reboot to continue, schedule the parent directories for
1494 // deletion unconditionally. If they are not empty, the session manager
1495 // will not delete them on reboot.
1496 ScheduleParentAndGrandparentForDeletion(target_path);
1497 } else if (DeleteChromeDirectoriesIfEmpty(target_path) == DELETE_FAILED) {
1498 *uninstall_status = UNINSTALL_FAILED;
1502 } // namespace installer