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