| 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 179 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 190 | 190 |
| 191 // If chrome has been reactivated, clear all events for this brand as well. | 191 // If chrome has been reactivated, clear all events for this brand as well. |
| 192 string16 reactivation_brand_wide; | 192 string16 reactivation_brand_wide; |
| 193 if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand_wide)) { | 193 if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand_wide)) { |
| 194 std::string reactivation_brand(WideToASCII(reactivation_brand_wide)); | 194 std::string reactivation_brand(WideToASCII(reactivation_brand_wide)); |
| 195 rlz_lib::SupplementaryBranding branding(reactivation_brand.c_str()); | 195 rlz_lib::SupplementaryBranding branding(reactivation_brand.c_str()); |
| 196 rlz_lib::ClearProductState(rlz_lib::CHROME, points); | 196 rlz_lib::ClearProductState(rlz_lib::CHROME, points); |
| 197 } | 197 } |
| 198 } | 198 } |
| 199 | 199 |
| 200 // Decides whether setup.exe and the installer archive should be removed based |
| 201 // on the original and installer states: |
| 202 // * non-multi product being uninstalled: remove both |
| 203 // * any multi product left besides App Host: keep both |
| 204 // * only App Host left: keep setup.exe |
| 205 void CheckShouldRemoveSetupAndArchive( |
| 206 const installer::InstallationState& original_state, |
| 207 const installer::InstallerState& installer_state, |
| 208 bool* remove_setup, |
| 209 bool* remove_archive) { |
| 210 *remove_setup = true; |
| 211 *remove_archive = true; |
| 212 |
| 213 // If any multi-install product is left (other than App Host) we must leave |
| 214 // the installer and archive. For the App Host, we only leave the installer. |
| 215 if (!installer_state.is_multi_install()) { |
| 216 VLOG(1) << "Removing all installer files for a non-multi installation."; |
| 217 } else { |
| 218 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { |
| 219 BrowserDistribution::Type dist_type = |
| 220 static_cast<BrowserDistribution::Type>(i); |
| 221 const installer::ProductState* product_state = |
| 222 original_state.GetProductState( |
| 223 installer_state.system_install(), dist_type); |
| 224 if (product_state && product_state->is_multi_install() && |
| 225 !installer_state.FindProduct(dist_type)) { |
| 226 // setup.exe will not be removed as there is a remaining multi-install |
| 227 // product. |
| 228 *remove_setup = false; |
| 229 // As a special case, we can still remove the actual archive if the |
| 230 // only remaining product is the App Host. |
| 231 if (dist_type != BrowserDistribution::CHROME_APP_HOST) { |
| 232 VLOG(1) << "Keeping all installer files due to a remaining " |
| 233 << "multi-install product."; |
| 234 *remove_archive = false; |
| 235 return; |
| 236 } |
| 237 VLOG(1) << "Keeping setup.exe due to a remaining " |
| 238 << "app-host installation."; |
| 239 } |
| 240 } |
| 241 VLOG(1) << "Removing the installer archive."; |
| 242 if (remove_setup) |
| 243 VLOG(1) << "Removing setup.exe."; |
| 244 } |
| 245 } |
| 246 |
| 247 // Removes all files from the installer directory, leaving setup.exe iff |
| 248 // |remove_setup| is false. |
| 249 // Returns false in case of an error. |
| 250 bool RemoveInstallerFiles(const FilePath& install_directory, |
| 251 bool remove_setup) { |
| 252 using file_util::FileEnumerator; |
| 253 FileEnumerator file_enumerator( |
| 254 install_directory, |
| 255 false, |
| 256 FileEnumerator::FILES | FileEnumerator::DIRECTORIES); |
| 257 bool success = true; |
| 258 |
| 259 FilePath setup_exe_base_name(installer::kSetupExe); |
| 260 |
| 261 while (true) { |
| 262 FilePath to_delete(file_enumerator.Next()); |
| 263 if (to_delete.empty()) |
| 264 break; |
| 265 if (!remove_setup && to_delete.BaseName() == setup_exe_base_name) |
| 266 continue; |
| 267 |
| 268 VLOG(1) << "Deleting install path " << to_delete.value(); |
| 269 if (!file_util::Delete(to_delete, true)) { |
| 270 LOG(ERROR) << "Failed to delete path: " << to_delete.value(); |
| 271 success = false; |
| 272 } |
| 273 } |
| 274 |
| 275 return success; |
| 276 } |
| 277 |
| 200 } // namespace | 278 } // namespace |
| 201 | 279 |
| 202 namespace installer { | 280 namespace installer { |
| 203 | 281 |
| 204 // Kills all Chrome processes, immediately. | 282 // Kills all Chrome processes, immediately. |
| 205 void CloseAllChromeProcesses() { | 283 void CloseAllChromeProcesses() { |
| 206 base::CleanupProcesses(installer::kChromeExe, base::TimeDelta(), | 284 base::CleanupProcesses(installer::kChromeExe, base::TimeDelta(), |
| 207 content::RESULT_CODE_HUNG, NULL); | 285 content::RESULT_CODE_HUNG, NULL); |
| 208 base::CleanupProcesses(installer::kNaClExe, base::TimeDelta(), | 286 base::CleanupProcesses(installer::kNaClExe, base::TimeDelta(), |
| 209 content::RESULT_CODE_HUNG, NULL); | 287 content::RESULT_CODE_HUNG, NULL); |
| (...skipping 182 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 392 const FilePath product_dir(user_data_dir.DirName()); | 470 const FilePath product_dir(user_data_dir.DirName()); |
| 393 if (!product_dir.empty()) | 471 if (!product_dir.empty()) |
| 394 DeleteEmptyDir(product_dir); | 472 DeleteEmptyDir(product_dir); |
| 395 } | 473 } |
| 396 } | 474 } |
| 397 | 475 |
| 398 return result; | 476 return result; |
| 399 } | 477 } |
| 400 | 478 |
| 401 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, | 479 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, |
| 402 const FilePath& setup_path, | 480 const FilePath& setup_exe) { |
| 403 const Version& installed_version) { | |
| 404 bool ret = false; | 481 bool ret = false; |
| 405 FilePath setup_exe(installer_state.GetInstallerDirectory(installed_version) | |
| 406 .Append(setup_path.BaseName())); | |
| 407 FilePath temp_file; | 482 FilePath temp_file; |
| 408 if (!file_util::CreateTemporaryFile(&temp_file)) { | 483 if (!file_util::CreateTemporaryFile(&temp_file)) { |
| 409 LOG(ERROR) << "Failed to create temporary file for setup.exe."; | 484 LOG(ERROR) << "Failed to create temporary file for setup.exe."; |
| 410 } else { | 485 } else { |
| 411 VLOG(1) << "Attempting to move setup to: " << temp_file.value(); | 486 VLOG(1) << "Attempting to move setup to: " << temp_file.value(); |
| 412 ret = file_util::Move(setup_exe, temp_file); | 487 ret = file_util::Move(setup_exe, temp_file); |
| 413 PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value(); | 488 PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value(); |
| 414 | 489 |
| 415 // We cannot delete the file right away, but try to delete it some other | 490 // We cannot delete the file right away, but try to delete it some other |
| 416 // way. Either with the help of a different process or the system. | 491 // way. Either with the help of a different process or the system. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 453 } | 528 } |
| 454 | 529 |
| 455 DeleteInstallTempDir(target_path); | 530 DeleteInstallTempDir(target_path); |
| 456 | 531 |
| 457 DeleteResult result = DELETE_SUCCEEDED; | 532 DeleteResult result = DELETE_SUCCEEDED; |
| 458 | 533 |
| 459 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); | 534 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); |
| 460 if (!file_util::Delete(app_host_exe, false)) { | 535 if (!file_util::Delete(app_host_exe, false)) { |
| 461 result = DELETE_FAILED; | 536 result = DELETE_FAILED; |
| 462 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); | 537 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); |
| 463 } else { | |
| 464 result = DeleteApplicationProductAndVendorDirectories(target_path); | |
| 465 } | 538 } |
| 466 | 539 |
| 467 return result; | 540 return result; |
| 468 } | 541 } |
| 469 | 542 |
| 470 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, | 543 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, |
| 471 const Version& installed_version) { | 544 const FilePath& installer_path) { |
| 472 const FilePath& target_path = installer_state.target_path(); | 545 const FilePath& target_path = installer_state.target_path(); |
| 473 if (target_path.empty()) { | 546 if (target_path.empty()) { |
| 474 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " | 547 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " |
| 475 << "path."; | 548 << "path."; |
| 476 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. | 549 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. |
| 477 } | 550 } |
| 478 | 551 |
| 479 DeleteInstallTempDir(target_path); | 552 DeleteInstallTempDir(target_path); |
| 480 | 553 |
| 481 DeleteResult result = DELETE_SUCCEEDED; | 554 DeleteResult result = DELETE_SUCCEEDED; |
| 482 | 555 |
| 556 FilePath installer_directory; |
| 557 if (target_path.IsParent(installer_path)) |
| 558 installer_directory = installer_path.DirName(); |
| 559 |
| 560 // Enumerate all the files in target_path recursively (breadth-first). |
| 561 // We delete a file or folder unless it is a parent/child of the installer |
| 562 // directory. For parents of the installer directory, we will later recurse |
| 563 // and delete all the children (that are not also parents/children of the |
| 564 // installer directory). |
| 483 using file_util::FileEnumerator; | 565 using file_util::FileEnumerator; |
| 484 FileEnumerator file_enumerator(target_path, false, | 566 FileEnumerator file_enumerator( |
| 485 FileEnumerator::FILES | FileEnumerator::DIRECTORIES); | 567 target_path, true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES); |
| 486 while (true) { | 568 while (true) { |
| 487 FilePath to_delete(file_enumerator.Next()); | 569 FilePath to_delete(file_enumerator.Next()); |
| 488 if (to_delete.empty()) | 570 if (to_delete.empty()) |
| 489 break; | 571 break; |
| 490 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) | 572 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) |
| 491 continue; | 573 continue; |
| 574 if (!installer_directory.empty() && |
| 575 (to_delete == installer_directory || |
| 576 installer_directory.IsParent(to_delete) || |
| 577 to_delete.IsParent(installer_directory))) { |
| 578 continue; |
| 579 } |
| 492 | 580 |
| 493 VLOG(1) << "Deleting install path " << to_delete.value(); | 581 VLOG(1) << "Deleting install path " << to_delete.value(); |
| 494 if (!file_util::Delete(to_delete, true)) { | 582 if (!file_util::Delete(to_delete, true)) { |
| 495 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); | 583 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); |
| 496 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { | 584 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { |
| 497 // We don't try killing Chrome processes for Chrome Frame builds since | 585 // We don't try killing Chrome processes for Chrome Frame builds since |
| 498 // that is unlikely to help. Instead, schedule files for deletion and | 586 // that is unlikely to help. Instead, schedule files for deletion and |
| 499 // return a value that will trigger a reboot prompt. | 587 // return a value that will trigger a reboot prompt. |
| 500 FileEnumerator::FindInfo find_info; | 588 FileEnumerator::FindInfo find_info; |
| 501 file_enumerator.GetFindInfo(&find_info); | 589 file_enumerator.GetFindInfo(&find_info); |
| 502 if (FileEnumerator::IsDirectory(find_info)) | 590 if (FileEnumerator::IsDirectory(find_info)) |
| 503 ScheduleDirectoryForDeletion(to_delete.value().c_str()); | 591 ScheduleDirectoryForDeletion(to_delete.value().c_str()); |
| 504 else | 592 else |
| 505 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); | 593 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); |
| 506 result = DELETE_REQUIRES_REBOOT; | 594 result = DELETE_REQUIRES_REBOOT; |
| 507 } else { | 595 } else { |
| 508 // Try closing any running Chrome processes and deleting files once | 596 // Try closing any running Chrome processes and deleting files once |
| 509 // again. | 597 // again. |
| 510 CloseAllChromeProcesses(); | 598 CloseAllChromeProcesses(); |
| 511 if (!file_util::Delete(to_delete, true)) { | 599 if (!file_util::Delete(to_delete, true)) { |
| 512 LOG(ERROR) << "Failed to delete path (2nd try): " | 600 LOG(ERROR) << "Failed to delete path (2nd try): " |
| 513 << to_delete.value(); | 601 << to_delete.value(); |
| 514 result = DELETE_FAILED; | 602 result = DELETE_FAILED; |
| 515 break; | 603 break; |
| 516 } | 604 } |
| 517 } | 605 } |
| 518 } | 606 } |
| 519 } | 607 } |
| 520 | 608 |
| 521 if (result == DELETE_REQUIRES_REBOOT) { | |
| 522 // Delete the Application directory at reboot if empty. | |
| 523 ScheduleFileSystemEntityForDeletion(target_path.value().c_str()); | |
| 524 | |
| 525 // If we need a reboot to continue, schedule the parent directories for | |
| 526 // deletion unconditionally. If they are not empty, the session manager | |
| 527 // will not delete them on reboot. | |
| 528 ScheduleParentAndGrandparentForDeletion(target_path); | |
| 529 } else { | |
| 530 result = DeleteApplicationProductAndVendorDirectories(target_path); | |
| 531 } | |
| 532 return result; | 609 return result; |
| 533 } | 610 } |
| 534 | 611 |
| 535 // This method checks if Chrome is currently running or if the user has | 612 // This method checks if Chrome is currently running or if the user has |
| 536 // cancelled the uninstall operation by clicking Cancel on the confirmation | 613 // cancelled the uninstall operation by clicking Cancel on the confirmation |
| 537 // box that Chrome pops up. | 614 // box that Chrome pops up. |
| 538 InstallStatus IsChromeActiveOrUserCancelled( | 615 InstallStatus IsChromeActiveOrUserCancelled( |
| 539 const InstallerState& installer_state, | 616 const InstallerState& installer_state, |
| 540 const Product& product) { | 617 const Product& product) { |
| 541 int32 exit_code = content::RESULT_CODE_NORMAL_EXIT; | 618 int32 exit_code = content::RESULT_CODE_NORMAL_EXIT; |
| (...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 1216 product); | 1293 product); |
| 1217 ret = installer::UNINSTALL_SUCCESSFUL; | 1294 ret = installer::UNINSTALL_SUCCESSFUL; |
| 1218 | 1295 |
| 1219 // When deleting files, we must make sure that we're either a "single" | 1296 // When deleting files, we must make sure that we're either a "single" |
| 1220 // (aka non-multi) installation or we are the Chrome Binaries. | 1297 // (aka non-multi) installation or we are the Chrome Binaries. |
| 1221 | 1298 |
| 1222 std::vector<FilePath> local_state_folders; | 1299 std::vector<FilePath> local_state_folders; |
| 1223 GetLocalStateFolders(product, &local_state_folders); | 1300 GetLocalStateFolders(product, &local_state_folders); |
| 1224 FilePath backup_state_file(BackupLocalStateFile(local_state_folders)); | 1301 FilePath backup_state_file(BackupLocalStateFile(local_state_folders)); |
| 1225 | 1302 |
| 1226 DeleteResult delete_result = DELETE_SUCCEEDED; | |
| 1227 | |
| 1228 if (product.is_chrome_app_host()) { | 1303 if (product.is_chrome_app_host()) { |
| 1229 DeleteAppHostFilesAndFolders(installer_state, product_state->version()); | 1304 DeleteAppHostFilesAndFolders(installer_state, product_state->version()); |
| 1230 } else if (!installer_state.is_multi_install() || | 1305 } else if (!installer_state.is_multi_install() || |
| 1231 product.is_chrome_binaries()) { | 1306 product.is_chrome_binaries()) { |
| 1232 | 1307 DeleteResult delete_result = DeleteChromeFilesAndFolders( |
| 1233 // In order to be able to remove the folder in which we're running, we | 1308 installer_state, cmd_line.GetProgram()); |
| 1234 // need to move setup.exe out of the install folder. | 1309 if (delete_result == DELETE_FAILED) { |
| 1235 // TODO(tommi): What if the temp folder is on a different volume? | 1310 ret = installer::UNINSTALL_FAILED; |
| 1236 MoveSetupOutOfInstallFolder(installer_state, setup_path, | 1311 } else if (delete_result == DELETE_REQUIRES_REBOOT) { |
| 1237 product_state->version()); | 1312 ret = installer::UNINSTALL_REQUIRES_REBOOT; |
| 1238 delete_result = DeleteChromeFilesAndFolders(installer_state, | 1313 } |
| 1239 product_state->version()); | |
| 1240 } | 1314 } |
| 1241 | 1315 |
| 1242 if (delete_profile) | 1316 if (delete_profile) |
| 1243 DeleteLocalState(local_state_folders, product.is_chrome_frame()); | 1317 DeleteLocalState(local_state_folders, product.is_chrome_frame()); |
| 1244 | 1318 |
| 1245 if (delete_result == DELETE_FAILED) { | |
| 1246 ret = installer::UNINSTALL_FAILED; | |
| 1247 } else if (delete_result == DELETE_REQUIRES_REBOOT) { | |
| 1248 ret = installer::UNINSTALL_REQUIRES_REBOOT; | |
| 1249 } | |
| 1250 | |
| 1251 if (!force_uninstall) { | 1319 if (!force_uninstall) { |
| 1252 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations."; | 1320 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations."; |
| 1253 browser_dist->DoPostUninstallOperations(product_state->version(), | 1321 browser_dist->DoPostUninstallOperations(product_state->version(), |
| 1254 backup_state_file, distribution_data); | 1322 backup_state_file, distribution_data); |
| 1255 } | 1323 } |
| 1256 | 1324 |
| 1257 // Try and delete the preserved local state once the post-install | 1325 // Try and delete the preserved local state once the post-install |
| 1258 // operations are complete. | 1326 // operations are complete. |
| 1259 if (!backup_state_file.empty()) | 1327 if (!backup_state_file.empty()) |
| 1260 file_util::Delete(backup_state_file, false); | 1328 file_util::Delete(backup_state_file, false); |
| 1261 | 1329 |
| 1262 // If user-level Chrome is being uninstalled and system-level Chrome is | 1330 // If user-level Chrome is being uninstalled and system-level Chrome is |
| 1263 // present, launch the system-level Active Setup command to do post-install | 1331 // present, launch the system-level Active Setup command to do post-install |
| 1264 // tasks for this user (i.e., create shortcuts). | 1332 // tasks for this user (i.e., create shortcuts). |
| 1265 if (product.is_chrome() && !installer_state.system_install() && | 1333 if (product.is_chrome() && !installer_state.system_install() && |
| 1266 original_state.GetProductState(true, browser_dist->GetType())) { | 1334 original_state.GetProductState(true, browser_dist->GetType())) { |
| 1267 InstallUtil::TriggerActiveSetupCommand(); | 1335 InstallUtil::TriggerActiveSetupCommand(); |
| 1268 } | 1336 } |
| 1269 | 1337 |
| 1270 return ret; | 1338 return ret; |
| 1271 } | 1339 } |
| 1272 | 1340 |
| 1341 void CleanUpInstallationDirectoryAfterUninstall( |
| 1342 const InstallationState& original_state, |
| 1343 const InstallerState& installer_state, |
| 1344 const CommandLine& cmd_line, |
| 1345 installer::InstallStatus* uninstall_status) { |
| 1346 if (*uninstall_status != installer::UNINSTALL_SUCCESSFUL && |
| 1347 *uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT) { |
| 1348 return; |
| 1349 } |
| 1350 const FilePath target_path(installer_state.target_path()); |
| 1351 if (target_path.empty()) { |
| 1352 LOG(ERROR) << "No installation destination path."; |
| 1353 *uninstall_status = installer::UNINSTALL_FAILED; |
| 1354 return; |
| 1355 } |
| 1356 FilePath setup_exe(cmd_line.GetProgram()); |
| 1357 file_util::AbsolutePath(&setup_exe); |
| 1358 if (!target_path.IsParent(setup_exe)) { |
| 1359 LOG(INFO) << "setup.exe is not in target path. Skipping installer cleanup."; |
| 1360 return; |
| 1361 } |
| 1362 FilePath install_directory(setup_exe.DirName()); |
| 1363 |
| 1364 bool remove_setup = true; |
| 1365 bool remove_archive = true; |
| 1366 CheckShouldRemoveSetupAndArchive(original_state, installer_state, |
| 1367 &remove_setup, &remove_archive); |
| 1368 if (!remove_archive) |
| 1369 return; |
| 1370 |
| 1371 if (remove_setup) { |
| 1372 // In order to be able to remove the folder in which we're running, we |
| 1373 // need to move setup.exe out of the install folder. |
| 1374 // TODO(tommi): What if the temp folder is on a different volume? |
| 1375 MoveSetupOutOfInstallFolder(installer_state, setup_exe); |
| 1376 } |
| 1377 |
| 1378 // Remove files from "...\<product>\Application\<version>\Installer" |
| 1379 if (!RemoveInstallerFiles(install_directory, remove_setup)) { |
| 1380 *uninstall_status = installer::UNINSTALL_FAILED; |
| 1381 return; |
| 1382 } |
| 1383 |
| 1384 if (!remove_setup) |
| 1385 return; |
| 1386 |
| 1387 // Try to remove the empty directory hierarchy. |
| 1388 |
| 1389 // Delete "...\<product>\Application\<version>\Installer" |
| 1390 if (DeleteEmptyDir(install_directory) != DELETE_SUCCEEDED) { |
| 1391 *uninstall_status = installer::UNINSTALL_FAILED; |
| 1392 return; |
| 1393 } |
| 1394 |
| 1395 // Delete "...\<product>\Application\<version>" |
| 1396 DeleteResult delete_result = DeleteEmptyDir(install_directory.DirName()); |
| 1397 if (delete_result == DELETE_FAILED || |
| 1398 (delete_result == DELETE_NOT_EMPTY && |
| 1399 *uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT)) { |
| 1400 *uninstall_status = installer::UNINSTALL_FAILED; |
| 1401 return; |
| 1402 } |
| 1403 |
| 1404 if (*uninstall_status == installer::UNINSTALL_REQUIRES_REBOOT) { |
| 1405 // Delete the Application directory at reboot if empty. |
| 1406 ScheduleFileSystemEntityForDeletion(target_path.value().c_str()); |
| 1407 |
| 1408 // If we need a reboot to continue, schedule the parent directories for |
| 1409 // deletion unconditionally. If they are not empty, the session manager |
| 1410 // will not delete them on reboot. |
| 1411 ScheduleParentAndGrandparentForDeletion(target_path); |
| 1412 } else if (DeleteApplicationProductAndVendorDirectories(target_path) == |
| 1413 installer::DELETE_FAILED) { |
| 1414 *uninstall_status = installer::UNINSTALL_FAILED; |
| 1415 } |
| 1416 } |
| 1417 |
| 1273 } // namespace installer | 1418 } // namespace installer |
| OLD | NEW |