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