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