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