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