Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(860)

Side by Side Diff: chrome/installer/setup/uninstall.cc

Issue 10832210: Delete the installation folder after deleting its contents. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Review Comments. Created 8 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698