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