Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 // | 4 // |
| 5 // This file defines the methods useful for uninstalling Chrome. | 5 // This file defines the methods useful for uninstalling Chrome. |
| 6 | 6 |
| 7 #include "chrome/installer/setup/uninstall.h" | 7 #include "chrome/installer/setup/uninstall.h" |
| 8 | 8 |
| 9 #include <windows.h> | 9 #include <windows.h> |
| 10 | 10 |
| (...skipping 300 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 311 FilePath grandparent_dir(parent_dir.DirName()); | 311 FilePath grandparent_dir(parent_dir.DirName()); |
| 312 ret = ScheduleFileSystemEntityForDeletion(grandparent_dir.value().c_str()); | 312 ret = ScheduleFileSystemEntityForDeletion(grandparent_dir.value().c_str()); |
| 313 if (!ret) { | 313 if (!ret) { |
| 314 LOG(ERROR) << "Failed to schedule grandparent dir for deletion: " | 314 LOG(ERROR) << "Failed to schedule grandparent dir for deletion: " |
| 315 << grandparent_dir.value(); | 315 << grandparent_dir.value(); |
| 316 } | 316 } |
| 317 } | 317 } |
| 318 return ret; | 318 return ret; |
| 319 } | 319 } |
| 320 | 320 |
| 321 // Deletes empty parent & empty grandparent dir of given path. | 321 enum DeleteResult { |
| 322 bool DeleteEmptyParentDir(const FilePath& path) { | 322 DELETE_SUCCEEDED, |
| 323 bool ret = true; | 323 DELETE_NOT_EMPTY, |
| 324 FilePath parent_dir = path.DirName(); | 324 DELETE_FAILED, |
| 325 if (!parent_dir.empty() && file_util::IsDirectoryEmpty(parent_dir)) { | 325 DELETE_REQUIRES_REBOOT, |
| 326 if (!file_util::Delete(parent_dir, true)) { | 326 }; |
| 327 ret = false; | |
| 328 LOG(ERROR) << "Failed to delete folder: " << parent_dir.value(); | |
| 329 } | |
| 330 | 327 |
| 331 parent_dir = parent_dir.DirName(); | 328 // Deletes the given directory if it is empty. Returns DELETE_SUCCEEDED if the |
| 332 if (!parent_dir.empty() && file_util::IsDirectoryEmpty(parent_dir)) { | 329 // directory is deleted, DELETE_NOT_EMPTY if it is not empty, and DELETE_FAILED |
| 333 if (!file_util::Delete(parent_dir, true)) { | 330 // otherwise. |
| 334 ret = false; | 331 DeleteResult DeleteEmptyDir(const FilePath& path) { |
| 335 LOG(ERROR) << "Failed to delete folder: " << parent_dir.value(); | 332 if (!file_util::IsDirectoryEmpty(path)) |
| 336 } | 333 return DELETE_NOT_EMPTY; |
| 337 } | 334 |
| 338 } | 335 if (file_util::Delete(path, true)) |
| 339 return ret; | 336 return DELETE_SUCCEEDED; |
| 337 | |
| 338 LOG(ERROR) << "Failed to delete folder: " << path.value(); | |
| 339 return DELETE_FAILED; | |
| 340 } | 340 } |
| 341 | 341 |
| 342 void GetLocalStateFolders(const Product& product, | 342 void GetLocalStateFolders(const Product& product, |
| 343 std::vector<FilePath>* paths) { | 343 std::vector<FilePath>* paths) { |
| 344 // Obtain the location of the user profile data. | 344 // Obtain the location of the user profile data. |
| 345 product.GetUserDataPaths(paths); | 345 product.GetUserDataPaths(paths); |
| 346 LOG_IF(ERROR, paths->empty()) | 346 LOG_IF(ERROR, paths->empty()) |
| 347 << "Could not retrieve user's profile directory."; | 347 << "Could not retrieve user's profile directory."; |
| 348 } | 348 } |
| 349 | 349 |
| (...skipping 10 matching lines...) Expand all Loading... | |
| 360 continue; | 360 continue; |
| 361 if (!file_util::CreateTemporaryFile(&backup)) | 361 if (!file_util::CreateTemporaryFile(&backup)) |
| 362 LOG(ERROR) << "Failed to create temporary file for Local State."; | 362 LOG(ERROR) << "Failed to create temporary file for Local State."; |
| 363 else | 363 else |
| 364 file_util::CopyFile(state_file, backup); | 364 file_util::CopyFile(state_file, backup); |
| 365 break; | 365 break; |
| 366 } | 366 } |
| 367 return backup; | 367 return backup; |
| 368 } | 368 } |
| 369 | 369 |
| 370 enum DeleteResult { | |
| 371 DELETE_SUCCEEDED, | |
| 372 DELETE_FAILED, | |
| 373 DELETE_REQUIRES_REBOOT, | |
| 374 }; | |
| 375 | |
| 376 // Deletes all user data directories for a product. | 370 // Deletes all user data directories for a product. |
| 377 DeleteResult DeleteLocalState(const std::vector<FilePath>& local_state_folders, | 371 DeleteResult DeleteLocalState(const std::vector<FilePath>& local_state_folders, |
| 378 bool schedule_on_failure) { | 372 bool schedule_on_failure) { |
| 379 if (local_state_folders.empty()) | 373 if (local_state_folders.empty()) |
| 380 return DELETE_SUCCEEDED; | 374 return DELETE_SUCCEEDED; |
| 381 | 375 |
| 382 DeleteResult result = DELETE_SUCCEEDED; | 376 DeleteResult result = DELETE_SUCCEEDED; |
| 383 for (size_t i = 0; i < local_state_folders.size(); ++i) { | 377 for (size_t i = 0; i < local_state_folders.size(); ++i) { |
| 384 const FilePath& user_local_state = local_state_folders[i]; | 378 const FilePath& user_local_state = local_state_folders[i]; |
| 385 VLOG(1) << "Deleting user profile " << user_local_state.value(); | 379 VLOG(1) << "Deleting user profile " << user_local_state.value(); |
| 386 if (!file_util::Delete(user_local_state, true)) { | 380 if (!file_util::Delete(user_local_state, true)) { |
| 387 LOG(ERROR) << "Failed to delete user profile dir: " | 381 LOG(ERROR) << "Failed to delete user profile dir: " |
| 388 << user_local_state.value(); | 382 << user_local_state.value(); |
| 389 if (schedule_on_failure) { | 383 if (schedule_on_failure) { |
| 390 ScheduleDirectoryForDeletion(user_local_state.value().c_str()); | 384 ScheduleDirectoryForDeletion(user_local_state.value().c_str()); |
| 391 result = DELETE_REQUIRES_REBOOT; | 385 result = DELETE_REQUIRES_REBOOT; |
| 392 } else { | 386 } else { |
| 393 result = DELETE_FAILED; | 387 result = DELETE_FAILED; |
| 394 } | 388 } |
| 395 } | 389 } |
| 396 } | 390 } |
| 397 | 391 |
| 398 if (result == DELETE_REQUIRES_REBOOT) { | 392 if (result == DELETE_REQUIRES_REBOOT) { |
| 399 ScheduleParentAndGrandparentForDeletion(local_state_folders[0]); | 393 ScheduleParentAndGrandparentForDeletion(local_state_folders[0]); |
| 400 } else { | 394 } else { |
| 401 DeleteEmptyParentDir(local_state_folders[0]); | 395 const FilePath user_data_dir(local_state_folders[0].DirName()); |
| 396 if (!user_data_dir.empty() && | |
| 397 DeleteEmptyDir(user_data_dir) == DELETE_SUCCEEDED) { | |
| 398 const FilePath product_dir(user_data_dir.DirName()); | |
| 399 if (!product_dir.empty()) | |
| 400 DeleteEmptyDir(product_dir); | |
| 401 } | |
| 402 } | 402 } |
| 403 | 403 |
| 404 return result; | 404 return result; |
| 405 } | 405 } |
| 406 | 406 |
| 407 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, | 407 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, |
| 408 const FilePath& setup_path, | 408 const FilePath& setup_path, |
| 409 const Version& installed_version) { | 409 const Version& installed_version) { |
| 410 bool ret = false; | 410 bool ret = false; |
| 411 FilePath setup_exe(installer_state.GetInstallerDirectory(installed_version) | 411 FilePath setup_exe(installer_state.GetInstallerDirectory(installed_version) |
| 412 .Append(setup_path.BaseName())); | 412 .Append(setup_path.BaseName())); |
| 413 FilePath temp_file; | 413 FilePath temp_file; |
| 414 if (!file_util::CreateTemporaryFile(&temp_file)) { | 414 if (!file_util::CreateTemporaryFile(&temp_file)) { |
| 415 LOG(ERROR) << "Failed to create temporary file for setup.exe."; | 415 LOG(ERROR) << "Failed to create temporary file for setup.exe."; |
| 416 } else { | 416 } else { |
| 417 VLOG(1) << "Attempting to move setup to: " << temp_file.value(); | 417 VLOG(1) << "Attempting to move setup to: " << temp_file.value(); |
| 418 ret = file_util::Move(setup_exe, temp_file); | 418 ret = file_util::Move(setup_exe, temp_file); |
| 419 PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value(); | 419 PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value(); |
| 420 | 420 |
| 421 // We cannot delete the file right away, but try to delete it some other | 421 // We cannot delete the file right away, but try to delete it some other |
| 422 // way. Either with the help of a different process or the system. | 422 // way. Either with the help of a different process or the system. |
| 423 if (ret && !file_util::DeleteAfterReboot(temp_file)) { | 423 if (ret && !file_util::DeleteAfterReboot(temp_file)) { |
| 424 static const uint32 kDeleteAfterMs = 10 * 1000; | 424 static const uint32 kDeleteAfterMs = 10 * 1000; |
| 425 installer::DeleteFileFromTempProcess(temp_file, kDeleteAfterMs); | 425 installer::DeleteFileFromTempProcess(temp_file, kDeleteAfterMs); |
| 426 } | 426 } |
| 427 } | 427 } |
| 428 return ret; | 428 return ret; |
| 429 } | 429 } |
| 430 | 430 |
| 431 DeleteResult DeleteApplicationProductAndVendorDirectories( | |
| 432 const FilePath& application_directory) { | |
| 433 DeleteResult result(DeleteEmptyDir(application_directory)); | |
|
gab
2012/08/13 19:33:54
nit: DeleteResult is a POD (i.e. enum), I think th
erikwright (departed)
2012/08/13 19:37:37
Found no mention of this for PODS or non-PODS. The
gab
2012/08/13 19:46:16
Ah interesting, maybe we should define "grt-style"
| |
| 434 if (result == DELETE_SUCCEEDED) { | |
| 435 // Now check and delete if the parent directories are empty | |
| 436 // For example Google\Chrome or Chromium | |
| 437 const FilePath product_directory(application_directory.DirName()); | |
| 438 if (!product_directory.empty()) { | |
| 439 result = DeleteEmptyDir(product_directory); | |
| 440 if (result == DELETE_SUCCEEDED) { | |
| 441 const FilePath vendor_directory(product_directory.DirName()); | |
| 442 if (!vendor_directory.empty()) | |
| 443 result = DeleteEmptyDir(vendor_directory); | |
| 444 } | |
| 445 } | |
| 446 } | |
| 447 if (result == DELETE_NOT_EMPTY) | |
| 448 result = DELETE_SUCCEEDED; | |
| 449 return result; | |
| 450 } | |
| 451 | |
| 431 DeleteResult DeleteAppHostFilesAndFolders(const InstallerState& installer_state, | 452 DeleteResult DeleteAppHostFilesAndFolders(const InstallerState& installer_state, |
| 432 const Version& installed_version) { | 453 const Version& installed_version) { |
| 433 const FilePath& target_path = installer_state.target_path(); | 454 const FilePath& target_path = installer_state.target_path(); |
| 434 if (target_path.empty()) { | 455 if (target_path.empty()) { |
| 435 LOG(ERROR) << "DeleteAppHostFilesAndFolders: no installation destination " | 456 LOG(ERROR) << "DeleteAppHostFilesAndFolders: no installation destination " |
| 436 << "path."; | 457 << "path."; |
| 437 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. | 458 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. |
| 438 } | 459 } |
| 439 | 460 |
| 440 DeleteInstallTempDir(target_path); | 461 DeleteInstallTempDir(target_path); |
| 441 | 462 |
| 442 DeleteResult result = DELETE_SUCCEEDED; | 463 DeleteResult result = DELETE_SUCCEEDED; |
| 443 | 464 |
| 444 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); | 465 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); |
| 445 if (!file_util::Delete(app_host_exe, false)) { | 466 if (!file_util::Delete(app_host_exe, false)) { |
| 446 result = DELETE_FAILED; | 467 result = DELETE_FAILED; |
| 447 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); | 468 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); |
| 448 } else { | 469 } else { |
| 449 DeleteEmptyParentDir(target_path); | 470 result = DeleteApplicationProductAndVendorDirectories(target_path); |
| 450 } | 471 } |
| 451 | 472 |
| 452 return result; | 473 return result; |
| 453 } | 474 } |
| 454 | 475 |
| 455 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, | 476 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, |
| 456 const Version& installed_version) { | 477 const Version& installed_version) { |
| 457 const FilePath& target_path = installer_state.target_path(); | 478 const FilePath& target_path = installer_state.target_path(); |
| 458 if (target_path.empty()) { | 479 if (target_path.empty()) { |
| 459 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " | 480 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 471 false, | 492 false, |
| 472 static_cast<FileEnumerator::FileType>(FileEnumerator::FILES | | 493 static_cast<FileEnumerator::FileType>(FileEnumerator::FILES | |
| 473 FileEnumerator::DIRECTORIES)); | 494 FileEnumerator::DIRECTORIES)); |
| 474 while (true) { | 495 while (true) { |
| 475 FilePath to_delete(file_enumerator.Next()); | 496 FilePath to_delete(file_enumerator.Next()); |
| 476 if (to_delete.empty()) | 497 if (to_delete.empty()) |
| 477 break; | 498 break; |
| 478 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) | 499 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) |
| 479 continue; | 500 continue; |
| 480 | 501 |
| 481 VLOG(1) << "Deleting install path " << target_path.value(); | 502 VLOG(1) << "Deleting install path " << to_delete.value(); |
| 482 if (!file_util::Delete(to_delete, true)) { | 503 if (!file_util::Delete(to_delete, true)) { |
| 483 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); | 504 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); |
| 484 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { | 505 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { |
| 485 // We don't try killing Chrome processes for Chrome Frame builds since | 506 // We don't try killing Chrome processes for Chrome Frame builds since |
| 486 // that is unlikely to help. Instead, schedule files for deletion and | 507 // that is unlikely to help. Instead, schedule files for deletion and |
| 487 // return a value that will trigger a reboot prompt. | 508 // return a value that will trigger a reboot prompt. |
| 488 FileEnumerator::FindInfo find_info; | 509 FileEnumerator::FindInfo find_info; |
| 489 file_enumerator.GetFindInfo(&find_info); | 510 file_enumerator.GetFindInfo(&find_info); |
| 490 if (FileEnumerator::IsDirectory(find_info)) | 511 if (FileEnumerator::IsDirectory(find_info)) |
| 491 ScheduleDirectoryForDeletion(to_delete.value().c_str()); | 512 ScheduleDirectoryForDeletion(to_delete.value().c_str()); |
| 492 else | 513 else |
| 493 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); | 514 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); |
| 494 result = DELETE_REQUIRES_REBOOT; | 515 result = DELETE_REQUIRES_REBOOT; |
| 495 } else { | 516 } else { |
| 496 // Try closing any running Chrome processes and deleting files once | 517 // Try closing any running Chrome processes and deleting files once |
| 497 // again. | 518 // again. |
| 498 CloseAllChromeProcesses(); | 519 CloseAllChromeProcesses(); |
| 499 if (!file_util::Delete(to_delete, true)) { | 520 if (!file_util::Delete(to_delete, true)) { |
| 500 LOG(ERROR) << "Failed to delete path (2nd try): " | 521 LOG(ERROR) << "Failed to delete path (2nd try): " |
| 501 << to_delete.value(); | 522 << to_delete.value(); |
| 502 result = DELETE_FAILED; | 523 result = DELETE_FAILED; |
| 503 break; | 524 break; |
| 504 } | 525 } |
| 505 } | 526 } |
| 506 } | 527 } |
| 507 } | 528 } |
| 508 | 529 |
| 509 if (result == DELETE_REQUIRES_REBOOT) { | 530 if (result == DELETE_REQUIRES_REBOOT) { |
| 531 // Delete the Application directory at reboot if empty. | |
| 532 ScheduleFileSystemEntityForDeletion(target_path.value().c_str()); | |
| 533 | |
| 510 // If we need a reboot to continue, schedule the parent directories for | 534 // If we need a reboot to continue, schedule the parent directories for |
| 511 // deletion unconditionally. If they are not empty, the session manager | 535 // deletion unconditionally. If they are not empty, the session manager |
| 512 // will not delete them on reboot. | 536 // will not delete them on reboot. |
| 513 ScheduleParentAndGrandparentForDeletion(target_path); | 537 ScheduleParentAndGrandparentForDeletion(target_path); |
| 514 } else { | 538 } else { |
| 515 // Now check and delete if the parent directories are empty | 539 result = DeleteApplicationProductAndVendorDirectories(target_path); |
| 516 // For example Google\Chrome or Chromium | |
| 517 DeleteEmptyParentDir(target_path); | |
| 518 } | 540 } |
| 519 return result; | 541 return result; |
| 520 } | 542 } |
| 521 | 543 |
| 522 // This method checks if Chrome is currently running or if the user has | 544 // This method checks if Chrome is currently running or if the user has |
| 523 // cancelled the uninstall operation by clicking Cancel on the confirmation | 545 // cancelled the uninstall operation by clicking Cancel on the confirmation |
| 524 // box that Chrome pops up. | 546 // box that Chrome pops up. |
| 525 InstallStatus IsChromeActiveOrUserCancelled( | 547 InstallStatus IsChromeActiveOrUserCancelled( |
| 526 const InstallerState& installer_state, | 548 const InstallerState& installer_state, |
| 527 const Product& product) { | 549 const Product& product) { |
| (...skipping 633 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1161 | 1183 |
| 1162 // Try and delete the preserved local state once the post-install | 1184 // Try and delete the preserved local state once the post-install |
| 1163 // operations are complete. | 1185 // operations are complete. |
| 1164 if (!backup_state_file.empty()) | 1186 if (!backup_state_file.empty()) |
| 1165 file_util::Delete(backup_state_file, false); | 1187 file_util::Delete(backup_state_file, false); |
| 1166 | 1188 |
| 1167 return ret; | 1189 return ret; |
| 1168 } | 1190 } |
| 1169 | 1191 |
| 1170 } // namespace installer | 1192 } // namespace installer |
| OLD | NEW |