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