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