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