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 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
174 | 174 |
175 // If chrome has been reactivated, clear all events for this brand as well. | 175 // If chrome has been reactivated, clear all events for this brand as well. |
176 string16 reactivation_brand_wide; | 176 string16 reactivation_brand_wide; |
177 if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand_wide)) { | 177 if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand_wide)) { |
178 std::string reactivation_brand(WideToASCII(reactivation_brand_wide)); | 178 std::string reactivation_brand(WideToASCII(reactivation_brand_wide)); |
179 rlz_lib::SupplementaryBranding branding(reactivation_brand.c_str()); | 179 rlz_lib::SupplementaryBranding branding(reactivation_brand.c_str()); |
180 rlz_lib::ClearProductState(rlz_lib::CHROME, points); | 180 rlz_lib::ClearProductState(rlz_lib::CHROME, points); |
181 } | 181 } |
182 } | 182 } |
183 | 183 |
184 // Decides whether setup.exe and the installer archive should be removed based | |
185 // on the original and installer states: | |
186 // * non-multi product being uninstalled: remove both | |
187 // * any multi product left besides App Host: keep both | |
188 // * only App Host left: keep setup.exe | |
189 void CheckShouldRemoveSetupAndArchive( | |
190 const installer::InstallationState& original_state, | |
191 const installer::InstallerState& installer_state, | |
192 bool* remove_setup, | |
193 bool* remove_archive) { | |
194 *remove_setup = true; | |
195 *remove_archive = true; | |
196 | |
197 // If any multi-install product is left (other than App Host) we must leave | |
198 // the installer and archive. For the App Host, we only leave the installer. | |
199 if (!installer_state.is_multi_install()) { | |
200 VLOG(1) << "Removing all installer files for a non-multi installation."; | |
201 } else { | |
202 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { | |
203 BrowserDistribution::Type dist_type = | |
204 static_cast<BrowserDistribution::Type>(i); | |
205 const installer::ProductState* product_state = | |
206 original_state.GetProductState( | |
207 installer_state.system_install(), dist_type); | |
208 if (product_state && product_state->is_multi_install() && | |
209 !installer_state.FindProduct(dist_type)) { | |
210 *remove_setup = false; | |
211 if (dist_type != BrowserDistribution::CHROME_APP_HOST) { | |
212 VLOG(1) << "Keeping all installer files due to a remaining " | |
213 << "multi-install product."; | |
214 *remove_archive = false; | |
215 return; | |
216 } | |
217 VLOG(1) << "Keeping setup.exe due to a remaining " | |
218 << "app-host installation."; | |
219 } | |
220 } | |
221 VLOG(1) << "Removing the installer archive."; | |
222 if (remove_setup) | |
223 VLOG(1) << "Removing setup.exe."; | |
224 } | |
225 } | |
226 | |
227 // Removes all files from the installer directory, leaving setup.exe iff | |
228 // |remove_setup| is false. | |
229 // Returns false in case of an error. | |
230 bool RemoveInstallerFiles(const FilePath& install_directory, | |
231 bool remove_setup) { | |
232 using file_util::FileEnumerator; | |
233 FileEnumerator file_enumerator( | |
234 install_directory, | |
235 false, | |
236 FileEnumerator::FILES | FileEnumerator::DIRECTORIES); | |
237 bool success = true; | |
238 | |
239 while (true) { | |
240 FilePath to_delete(file_enumerator.Next()); | |
241 if (to_delete.empty()) | |
242 break; | |
243 if (!remove_setup && to_delete.BaseName() == FilePath(installer::kSetupExe)) | |
grt (UTC plus 2)
2012/11/23 21:00:55
nit: create this FilePath instance out of the loop
erikwright (departed)
2012/11/29 07:40:36
Done.
| |
244 continue; | |
245 | |
246 VLOG(1) << "Deleting install path " << to_delete.value(); | |
247 if (!file_util::Delete(to_delete, true)) { | |
248 LOG(ERROR) << "Failed to delete path: " << to_delete.value(); | |
249 success = false; | |
250 } | |
251 } | |
252 | |
253 return success; | |
254 } | |
255 | |
184 } // namespace | 256 } // namespace |
185 | 257 |
186 namespace installer { | 258 namespace installer { |
187 | 259 |
188 // This functions checks for any Chrome instances that are | 260 // This functions checks for any Chrome instances that are |
189 // running and first asks them to close politely by sending a Windows message. | 261 // running and first asks them to close politely by sending a Windows message. |
190 // If there is an error while sending message or if there are still Chrome | 262 // If there is an error while sending message or if there are still Chrome |
191 // procesess active after the message has been sent, this function will try | 263 // procesess active after the message has been sent, this function will try |
192 // to kill them. | 264 // to kill them. |
193 void CloseAllChromeProcesses() { | 265 void CloseAllChromeProcesses() { |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
403 const FilePath product_dir(user_data_dir.DirName()); | 475 const FilePath product_dir(user_data_dir.DirName()); |
404 if (!product_dir.empty()) | 476 if (!product_dir.empty()) |
405 DeleteEmptyDir(product_dir); | 477 DeleteEmptyDir(product_dir); |
406 } | 478 } |
407 } | 479 } |
408 | 480 |
409 return result; | 481 return result; |
410 } | 482 } |
411 | 483 |
412 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, | 484 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, |
413 const FilePath& setup_path, | 485 const FilePath& setup_exe) { |
414 const Version& installed_version) { | |
415 bool ret = false; | 486 bool ret = false; |
416 FilePath setup_exe(installer_state.GetInstallerDirectory(installed_version) | |
417 .Append(setup_path.BaseName())); | |
418 FilePath temp_file; | 487 FilePath temp_file; |
419 if (!file_util::CreateTemporaryFile(&temp_file)) { | 488 if (!file_util::CreateTemporaryFile(&temp_file)) { |
420 LOG(ERROR) << "Failed to create temporary file for setup.exe."; | 489 LOG(ERROR) << "Failed to create temporary file for setup.exe."; |
421 } else { | 490 } else { |
422 VLOG(1) << "Attempting to move setup to: " << temp_file.value(); | 491 VLOG(1) << "Attempting to move setup to: " << temp_file.value(); |
423 ret = file_util::Move(setup_exe, temp_file); | 492 ret = file_util::Move(setup_exe, temp_file); |
424 PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value(); | 493 PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value(); |
425 | 494 |
426 // We cannot delete the file right away, but try to delete it some other | 495 // We cannot delete the file right away, but try to delete it some other |
427 // way. Either with the help of a different process or the system. | 496 // 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... | |
464 } | 533 } |
465 | 534 |
466 DeleteInstallTempDir(target_path); | 535 DeleteInstallTempDir(target_path); |
467 | 536 |
468 DeleteResult result = DELETE_SUCCEEDED; | 537 DeleteResult result = DELETE_SUCCEEDED; |
469 | 538 |
470 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); | 539 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); |
471 if (!file_util::Delete(app_host_exe, false)) { | 540 if (!file_util::Delete(app_host_exe, false)) { |
472 result = DELETE_FAILED; | 541 result = DELETE_FAILED; |
473 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); | 542 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); |
474 } else { | |
475 result = DeleteApplicationProductAndVendorDirectories(target_path); | |
476 } | 543 } |
477 | 544 |
478 return result; | 545 return result; |
479 } | 546 } |
480 | 547 |
481 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, | 548 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, |
482 const Version& installed_version) { | 549 const FilePath& installer_path) { |
483 const FilePath& target_path = installer_state.target_path(); | 550 const FilePath& target_path = installer_state.target_path(); |
484 if (target_path.empty()) { | 551 if (target_path.empty()) { |
485 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " | 552 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " |
486 << "path."; | 553 << "path."; |
487 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. | 554 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. |
488 } | 555 } |
489 | 556 |
490 DeleteInstallTempDir(target_path); | 557 DeleteInstallTempDir(target_path); |
491 | 558 |
492 DeleteResult result = DELETE_SUCCEEDED; | 559 DeleteResult result = DELETE_SUCCEEDED; |
493 | 560 |
561 FilePath installer_directory; | |
562 if (target_path.IsParent(installer_path)) | |
563 installer_directory = installer_path.DirName(); | |
564 | |
565 // Enumerate all the files in target_path recursively (breadth-first). | |
566 // We delete a file or folder unless it is a parent/child of the installer | |
567 // directory. For parents of the installer directory, we will later recurse | |
568 // and delete all the children (that are not also parents/children of the | |
569 // installer directory). | |
494 using file_util::FileEnumerator; | 570 using file_util::FileEnumerator; |
495 FileEnumerator file_enumerator(target_path, false, | 571 FileEnumerator file_enumerator( |
496 FileEnumerator::FILES | FileEnumerator::DIRECTORIES); | 572 target_path, true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES); |
497 while (true) { | 573 while (true) { |
498 FilePath to_delete(file_enumerator.Next()); | 574 FilePath to_delete(file_enumerator.Next()); |
499 if (to_delete.empty()) | 575 if (to_delete.empty()) |
500 break; | 576 break; |
501 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) | 577 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) |
502 continue; | 578 continue; |
579 if (!installer_directory.empty() && | |
580 (to_delete == installer_directory || | |
581 installer_directory.IsParent(to_delete) || | |
582 to_delete.IsParent(installer_directory))) { | |
583 continue; | |
584 } | |
503 | 585 |
504 VLOG(1) << "Deleting install path " << to_delete.value(); | 586 VLOG(1) << "Deleting install path " << to_delete.value(); |
505 if (!file_util::Delete(to_delete, true)) { | 587 if (!file_util::Delete(to_delete, true)) { |
506 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); | 588 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); |
507 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { | 589 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { |
508 // We don't try killing Chrome processes for Chrome Frame builds since | 590 // We don't try killing Chrome processes for Chrome Frame builds since |
509 // that is unlikely to help. Instead, schedule files for deletion and | 591 // that is unlikely to help. Instead, schedule files for deletion and |
510 // return a value that will trigger a reboot prompt. | 592 // return a value that will trigger a reboot prompt. |
511 FileEnumerator::FindInfo find_info; | 593 FileEnumerator::FindInfo find_info; |
512 file_enumerator.GetFindInfo(&find_info); | 594 file_enumerator.GetFindInfo(&find_info); |
513 if (FileEnumerator::IsDirectory(find_info)) | 595 if (FileEnumerator::IsDirectory(find_info)) |
514 ScheduleDirectoryForDeletion(to_delete.value().c_str()); | 596 ScheduleDirectoryForDeletion(to_delete.value().c_str()); |
515 else | 597 else |
516 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); | 598 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); |
517 result = DELETE_REQUIRES_REBOOT; | 599 result = DELETE_REQUIRES_REBOOT; |
518 } else { | 600 } else { |
519 // Try closing any running Chrome processes and deleting files once | 601 // Try closing any running Chrome processes and deleting files once |
520 // again. | 602 // again. |
521 CloseAllChromeProcesses(); | 603 CloseAllChromeProcesses(); |
522 if (!file_util::Delete(to_delete, true)) { | 604 if (!file_util::Delete(to_delete, true)) { |
523 LOG(ERROR) << "Failed to delete path (2nd try): " | 605 LOG(ERROR) << "Failed to delete path (2nd try): " |
524 << to_delete.value(); | 606 << to_delete.value(); |
525 result = DELETE_FAILED; | 607 result = DELETE_FAILED; |
526 break; | 608 break; |
527 } | 609 } |
528 } | 610 } |
529 } | 611 } |
530 } | 612 } |
531 | 613 |
532 if (result == DELETE_REQUIRES_REBOOT) { | |
533 // Delete the Application directory at reboot if empty. | |
534 ScheduleFileSystemEntityForDeletion(target_path.value().c_str()); | |
535 | |
536 // If we need a reboot to continue, schedule the parent directories for | |
537 // deletion unconditionally. If they are not empty, the session manager | |
538 // will not delete them on reboot. | |
539 ScheduleParentAndGrandparentForDeletion(target_path); | |
540 } else { | |
541 result = DeleteApplicationProductAndVendorDirectories(target_path); | |
542 } | |
543 return result; | 614 return result; |
544 } | 615 } |
545 | 616 |
546 // This method checks if Chrome is currently running or if the user has | 617 // This method checks if Chrome is currently running or if the user has |
547 // cancelled the uninstall operation by clicking Cancel on the confirmation | 618 // cancelled the uninstall operation by clicking Cancel on the confirmation |
548 // box that Chrome pops up. | 619 // box that Chrome pops up. |
549 InstallStatus IsChromeActiveOrUserCancelled( | 620 InstallStatus IsChromeActiveOrUserCancelled( |
550 const InstallerState& installer_state, | 621 const InstallerState& installer_state, |
551 const Product& product) { | 622 const Product& product) { |
552 int32 exit_code = content::RESULT_CODE_NORMAL_EXIT; | 623 int32 exit_code = content::RESULT_CODE_NORMAL_EXIT; |
(...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1227 product); | 1298 product); |
1228 ret = installer::UNINSTALL_SUCCESSFUL; | 1299 ret = installer::UNINSTALL_SUCCESSFUL; |
1229 | 1300 |
1230 // When deleting files, we must make sure that we're either a "single" | 1301 // When deleting files, we must make sure that we're either a "single" |
1231 // (aka non-multi) installation or we are the Chrome Binaries. | 1302 // (aka non-multi) installation or we are the Chrome Binaries. |
1232 | 1303 |
1233 std::vector<FilePath> local_state_folders; | 1304 std::vector<FilePath> local_state_folders; |
1234 GetLocalStateFolders(product, &local_state_folders); | 1305 GetLocalStateFolders(product, &local_state_folders); |
1235 FilePath backup_state_file(BackupLocalStateFile(local_state_folders)); | 1306 FilePath backup_state_file(BackupLocalStateFile(local_state_folders)); |
1236 | 1307 |
1237 DeleteResult delete_result = DELETE_SUCCEEDED; | |
1238 | |
1239 if (product.is_chrome_app_host()) { | 1308 if (product.is_chrome_app_host()) { |
1240 DeleteAppHostFilesAndFolders(installer_state, product_state->version()); | 1309 DeleteAppHostFilesAndFolders(installer_state, product_state->version()); |
1241 } else if (!installer_state.is_multi_install() || | 1310 } else if (!installer_state.is_multi_install() || |
1242 product.is_chrome_binaries()) { | 1311 product.is_chrome_binaries()) { |
1243 | 1312 DeleteResult delete_result = DeleteChromeFilesAndFolders( |
1244 // In order to be able to remove the folder in which we're running, we | 1313 installer_state, cmd_line.GetProgram()); |
1245 // need to move setup.exe out of the install folder. | 1314 if (delete_result == DELETE_FAILED) { |
1246 // TODO(tommi): What if the temp folder is on a different volume? | 1315 ret = installer::UNINSTALL_FAILED; |
1247 MoveSetupOutOfInstallFolder(installer_state, setup_path, | 1316 } else if (delete_result == DELETE_REQUIRES_REBOOT) { |
1248 product_state->version()); | 1317 ret = installer::UNINSTALL_REQUIRES_REBOOT; |
1249 delete_result = DeleteChromeFilesAndFolders(installer_state, | 1318 } |
1250 product_state->version()); | |
1251 } | 1319 } |
1252 | 1320 |
1253 if (delete_profile) | 1321 if (delete_profile) |
1254 DeleteLocalState(local_state_folders, product.is_chrome_frame()); | 1322 DeleteLocalState(local_state_folders, product.is_chrome_frame()); |
1255 | 1323 |
1256 if (delete_result == DELETE_FAILED) { | |
1257 ret = installer::UNINSTALL_FAILED; | |
1258 } else if (delete_result == DELETE_REQUIRES_REBOOT) { | |
1259 ret = installer::UNINSTALL_REQUIRES_REBOOT; | |
1260 } | |
1261 | |
1262 if (!force_uninstall) { | 1324 if (!force_uninstall) { |
1263 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations."; | 1325 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations."; |
1264 browser_dist->DoPostUninstallOperations(product_state->version(), | 1326 browser_dist->DoPostUninstallOperations(product_state->version(), |
1265 backup_state_file, distribution_data); | 1327 backup_state_file, distribution_data); |
1266 } | 1328 } |
1267 | 1329 |
1268 // Try and delete the preserved local state once the post-install | 1330 // Try and delete the preserved local state once the post-install |
1269 // operations are complete. | 1331 // operations are complete. |
1270 if (!backup_state_file.empty()) | 1332 if (!backup_state_file.empty()) |
1271 file_util::Delete(backup_state_file, false); | 1333 file_util::Delete(backup_state_file, false); |
1272 | 1334 |
1273 return ret; | 1335 return ret; |
1274 } | 1336 } |
1275 | 1337 |
1338 void CleanUpInstallationDirectoryAfterUninstall( | |
1339 const InstallationState& original_state, | |
1340 const InstallerState& installer_state, | |
1341 const CommandLine& cmd_line, | |
1342 installer::InstallStatus* uninstall_status) { | |
1343 if (*uninstall_status != installer::UNINSTALL_SUCCESSFUL && | |
1344 *uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT) { | |
1345 return; | |
1346 } | |
1347 const FilePath target_path(installer_state.target_path()); | |
1348 if (target_path.empty()) { | |
1349 LOG(ERROR) << "No installation destination path."; | |
1350 *uninstall_status = installer::UNINSTALL_FAILED; | |
1351 return; | |
1352 } | |
1353 const FilePath setup_exe = cmd_line.GetProgram(); | |
grt (UTC plus 2)
2012/11/23 21:00:55
wdyt about using file_util::AbsolutePath here in c
erikwright (departed)
2012/11/29 07:40:36
From my reading of GetCommandLine (which is what C
grt (UTC plus 2)
2012/11/30 13:40:30
I believe gab@ has run into cases where it isn't f
gab
2012/11/30 15:19:45
Right, I think it's the path setup.exe was invoked
| |
1354 if (!target_path.IsParent(setup_exe)) { | |
1355 LOG(INFO) << "setup.exe is not in target path. Skipping installer cleanup."; | |
1356 return; | |
1357 } | |
1358 FilePath install_directory(setup_exe.DirName()); | |
1359 | |
1360 bool remove_setup = true; | |
1361 bool remove_archive = true; | |
1362 CheckShouldRemoveSetupAndArchive(original_state, installer_state, | |
1363 &remove_setup, &remove_archive); | |
1364 if (!remove_archive) | |
1365 return; | |
1366 | |
1367 if (remove_setup) { | |
1368 // In order to be able to remove the folder in which we're running, we | |
1369 // need to move setup.exe out of the install folder. | |
1370 // TODO(tommi): What if the temp folder is on a different volume? | |
1371 MoveSetupOutOfInstallFolder(installer_state, setup_exe); | |
1372 } | |
1373 | |
grt (UTC plus 2)
2012/11/23 21:00:55
// Remove files from "...\<product>\Application\<v
erikwright (departed)
2012/11/29 07:40:36
Done.
| |
1374 if (!RemoveInstallerFiles(install_directory, remove_setup)) { | |
1375 *uninstall_status = installer::UNINSTALL_FAILED; | |
1376 return; | |
1377 } | |
1378 | |
1379 if (!remove_setup) | |
1380 return; | |
1381 | |
1382 // Try to remove the empty directory hierarchy. | |
1383 | |
grt (UTC plus 2)
2012/11/23 21:00:55
// Delete "...\<product>\Application\<version>\Ins
erikwright (departed)
2012/11/29 07:40:36
Done.
| |
1384 if (DeleteEmptyDir(install_directory) != DELETE_SUCCEEDED) { | |
1385 *uninstall_status = installer::UNINSTALL_FAILED; | |
1386 return; | |
1387 } | |
1388 | |
grt (UTC plus 2)
2012/11/23 21:00:55
// Delete "...\<product>\Application\<version>"
erikwright (departed)
2012/11/29 07:40:36
Done.
| |
1389 FilePath to_delete = install_directory.DirName(); | |
1390 while (to_delete != target_path) { | |
grt (UTC plus 2)
2012/11/23 21:00:55
isn't the loop overkill? it looks like the versio
erikwright (departed)
2012/11/29 07:40:36
I guess I was trying not to code that dependency i
| |
1391 DeleteResult delete_result = DeleteEmptyDir(to_delete); | |
1392 if (delete_result == DELETE_SUCCEEDED) { | |
1393 to_delete = to_delete.DirName(); | |
1394 continue; | |
1395 } | |
1396 if (delete_result == DELETE_NOT_EMPTY && | |
1397 *uninstall_status == installer::UNINSTALL_REQUIRES_REBOOT) { | |
1398 break; | |
1399 } | |
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 | |
1276 } // namespace installer | 1418 } // namespace installer |
OLD | NEW |