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