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) { | |
gab
2012/11/21 22:55:55
Could add (*remove_setup || *remove_archive) as on
erikwright (departed)
2012/11/23 18:54:13
Redundant with the return on 215.
| |
203 BrowserDistribution::Type dist_type = | |
204 static_cast<BrowserDistribution::Type>(i); | |
gab
2012/11/21 22:55:55
I'm not a big fan of looping over all the potentia
erikwright (departed)
2012/11/23 18:54:13
Product is from InstallerState.
See the Installat
| |
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 " | |
gab
2012/11/21 22:55:55
Put the log beside *remove_setup = false; to keep
erikwright (departed)
2012/11/23 18:54:13
The thing is, it's only true if the condition on 2
gab
2012/11/30 15:19:45
Ah ok I see, the logic is essentially:
i
| |
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 // Determines the version that this uninstall operation is operating on. | |
228 Version GetUninstallVersion( | |
229 const installer::InstallationState& original_state, | |
230 bool system_level) { | |
231 // Find a product version - any product should do. | |
232 for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) { | |
233 const installer::ProductState* product_state = | |
234 original_state.GetProductState( | |
235 system_level, static_cast<BrowserDistribution::Type>(i)); | |
gab
2012/11/21 22:55:55
Same comment about getting installed products, if
erikwright (departed)
2012/11/23 18:54:13
Removed this instance.
| |
236 if (product_state) | |
237 return product_state->version(); | |
238 } | |
239 NOTREACHED(); | |
240 return Version(); | |
gab
2012/11/21 22:55:55
How about:
Version version;
for (...) {
...
erikwright (departed)
2012/11/23 18:54:13
Removed this.
| |
241 } | |
242 | |
243 // Removes all files from the installer directory, leaving setup.exe iff | |
gab
2012/11/21 22:55:55
s/installer/Installer
erikwright (departed)
2012/11/23 18:54:13
IMHO this is a generic, not the specific name 'Ins
| |
244 // |remove_setup| is true. | |
grt (UTC plus 2)
2012/11/21 20:40:26
true -> false?
erikwright (departed)
2012/11/23 18:54:13
Done.
| |
245 // Returns false in case of an error. | |
246 bool RemoveInstallerFiles(const FilePath& install_directory, | |
247 bool remove_setup) { | |
248 using file_util::FileEnumerator; | |
249 FileEnumerator file_enumerator( | |
250 install_directory, | |
251 false, | |
252 FileEnumerator::FILES | FileEnumerator::DIRECTORIES); | |
253 while (true) { | |
254 FilePath to_delete(file_enumerator.Next()); | |
255 if (to_delete.empty()) | |
256 break; | |
257 if (!remove_setup && to_delete.BaseName().value() == installer::kSetupExe) | |
gab
2012/11/21 22:55:55
I prefer comparing FilePaths to string16s, i.e.
to
erikwright (departed)
2012/11/23 18:54:13
That's awesome.
| |
258 continue; | |
259 | |
260 VLOG(1) << "Deleting install path " << to_delete.value(); | |
261 if (!file_util::Delete(to_delete, true)) { | |
262 LOG(ERROR) << "Failed to delete path: " << to_delete.value(); | |
263 return false; | |
gab
2012/11/21 22:55:55
Is an early exit intended here? We usually operate
| |
264 } | |
265 } | |
266 return true; | |
267 } | |
268 | |
184 } // namespace | 269 } // namespace |
185 | 270 |
186 namespace installer { | 271 namespace installer { |
187 | 272 |
188 // This functions checks for any Chrome instances that are | 273 // This functions checks for any Chrome instances that are |
189 // running and first asks them to close politely by sending a Windows message. | 274 // 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 | 275 // 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 | 276 // procesess active after the message has been sent, this function will try |
192 // to kill them. | 277 // to kill them. |
193 void CloseAllChromeProcesses() { | 278 void CloseAllChromeProcesses() { |
(...skipping 270 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
464 } | 549 } |
465 | 550 |
466 DeleteInstallTempDir(target_path); | 551 DeleteInstallTempDir(target_path); |
467 | 552 |
468 DeleteResult result = DELETE_SUCCEEDED; | 553 DeleteResult result = DELETE_SUCCEEDED; |
469 | 554 |
470 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); | 555 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); |
471 if (!file_util::Delete(app_host_exe, false)) { | 556 if (!file_util::Delete(app_host_exe, false)) { |
472 result = DELETE_FAILED; | 557 result = DELETE_FAILED; |
473 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); | 558 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); |
474 } else { | |
475 result = DeleteApplicationProductAndVendorDirectories(target_path); | |
476 } | 559 } |
477 | 560 |
478 return result; | 561 return result; |
479 } | 562 } |
480 | 563 |
481 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, | 564 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, |
482 const Version& installed_version) { | 565 const Version& installed_version) { |
483 const FilePath& target_path = installer_state.target_path(); | 566 const FilePath& target_path = installer_state.target_path(); |
484 if (target_path.empty()) { | 567 if (target_path.empty()) { |
485 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " | 568 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " |
486 << "path."; | 569 << "path."; |
487 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. | 570 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. |
488 } | 571 } |
489 | 572 |
490 DeleteInstallTempDir(target_path); | 573 DeleteInstallTempDir(target_path); |
491 | 574 |
492 DeleteResult result = DELETE_SUCCEEDED; | 575 DeleteResult result = DELETE_SUCCEEDED; |
493 | 576 |
494 using file_util::FileEnumerator; | 577 using file_util::FileEnumerator; |
495 FileEnumerator file_enumerator(target_path, false, | 578 FileEnumerator file_enumerator( |
496 FileEnumerator::FILES | FileEnumerator::DIRECTORIES); | 579 target_path, true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES); |
grt (UTC plus 2)
2012/11/21 20:40:26
i find this change impenetrable. could you explain
erikwright (departed)
2012/11/21 21:51:10
Previously, we went through the top-level entries
| |
580 | |
581 const FilePath installer_directory = | |
582 installer_state.GetInstallerDirectory(installed_version); | |
497 while (true) { | 583 while (true) { |
498 FilePath to_delete(file_enumerator.Next()); | 584 FilePath to_delete(file_enumerator.Next()); |
499 if (to_delete.empty()) | 585 if (to_delete.empty()) |
500 break; | 586 break; |
501 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) | 587 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) |
502 continue; | 588 continue; |
589 if (to_delete == installer_directory || | |
590 installer_directory.IsParent(to_delete) || | |
591 to_delete.IsParent(installer_directory)) { | |
592 continue; | |
593 } | |
503 | 594 |
504 VLOG(1) << "Deleting install path " << to_delete.value(); | 595 VLOG(1) << "Deleting install path " << to_delete.value(); |
505 if (!file_util::Delete(to_delete, true)) { | 596 if (!file_util::Delete(to_delete, true)) { |
506 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); | 597 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); |
507 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { | 598 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { |
508 // We don't try killing Chrome processes for Chrome Frame builds since | 599 // We don't try killing Chrome processes for Chrome Frame builds since |
509 // that is unlikely to help. Instead, schedule files for deletion and | 600 // that is unlikely to help. Instead, schedule files for deletion and |
510 // return a value that will trigger a reboot prompt. | 601 // return a value that will trigger a reboot prompt. |
511 FileEnumerator::FindInfo find_info; | 602 FileEnumerator::FindInfo find_info; |
512 file_enumerator.GetFindInfo(&find_info); | 603 file_enumerator.GetFindInfo(&find_info); |
513 if (FileEnumerator::IsDirectory(find_info)) | 604 if (FileEnumerator::IsDirectory(find_info)) |
514 ScheduleDirectoryForDeletion(to_delete.value().c_str()); | 605 ScheduleDirectoryForDeletion(to_delete.value().c_str()); |
515 else | 606 else |
516 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); | 607 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); |
517 result = DELETE_REQUIRES_REBOOT; | 608 result = DELETE_REQUIRES_REBOOT; |
518 } else { | 609 } else { |
519 // Try closing any running Chrome processes and deleting files once | 610 // Try closing any running Chrome processes and deleting files once |
520 // again. | 611 // again. |
521 CloseAllChromeProcesses(); | 612 CloseAllChromeProcesses(); |
522 if (!file_util::Delete(to_delete, true)) { | 613 if (!file_util::Delete(to_delete, true)) { |
523 LOG(ERROR) << "Failed to delete path (2nd try): " | 614 LOG(ERROR) << "Failed to delete path (2nd try): " |
524 << to_delete.value(); | 615 << to_delete.value(); |
525 result = DELETE_FAILED; | 616 result = DELETE_FAILED; |
526 break; | 617 break; |
527 } | 618 } |
528 } | 619 } |
529 } | 620 } |
530 } | 621 } |
531 | 622 |
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; | 623 return result; |
544 } | 624 } |
545 | 625 |
546 // This method checks if Chrome is currently running or if the user has | 626 // This method checks if Chrome is currently running or if the user has |
547 // cancelled the uninstall operation by clicking Cancel on the confirmation | 627 // cancelled the uninstall operation by clicking Cancel on the confirmation |
548 // box that Chrome pops up. | 628 // box that Chrome pops up. |
549 InstallStatus IsChromeActiveOrUserCancelled( | 629 InstallStatus IsChromeActiveOrUserCancelled( |
550 const InstallerState& installer_state, | 630 const InstallerState& installer_state, |
551 const Product& product) { | 631 const Product& product) { |
552 int32 exit_code = content::RESULT_CODE_NORMAL_EXIT; | 632 int32 exit_code = content::RESULT_CODE_NORMAL_EXIT; |
(...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1227 product); | 1307 product); |
1228 ret = installer::UNINSTALL_SUCCESSFUL; | 1308 ret = installer::UNINSTALL_SUCCESSFUL; |
1229 | 1309 |
1230 // When deleting files, we must make sure that we're either a "single" | 1310 // When deleting files, we must make sure that we're either a "single" |
1231 // (aka non-multi) installation or we are the Chrome Binaries. | 1311 // (aka non-multi) installation or we are the Chrome Binaries. |
1232 | 1312 |
1233 std::vector<FilePath> local_state_folders; | 1313 std::vector<FilePath> local_state_folders; |
1234 GetLocalStateFolders(product, &local_state_folders); | 1314 GetLocalStateFolders(product, &local_state_folders); |
1235 FilePath backup_state_file(BackupLocalStateFile(local_state_folders)); | 1315 FilePath backup_state_file(BackupLocalStateFile(local_state_folders)); |
1236 | 1316 |
1237 DeleteResult delete_result = DELETE_SUCCEEDED; | |
1238 | |
1239 if (product.is_chrome_app_host()) { | 1317 if (product.is_chrome_app_host()) { |
1240 DeleteAppHostFilesAndFolders(installer_state, product_state->version()); | 1318 DeleteAppHostFilesAndFolders(installer_state, product_state->version()); |
1241 } else if (!installer_state.is_multi_install() || | 1319 } else if (!installer_state.is_multi_install() || |
1242 product.is_chrome_binaries()) { | 1320 product.is_chrome_binaries()) { |
1243 | 1321 DeleteResult delete_result = DeleteChromeFilesAndFolders( |
1244 // In order to be able to remove the folder in which we're running, we | 1322 installer_state, product_state->version()); |
1245 // need to move setup.exe out of the install folder. | 1323 if (delete_result == DELETE_FAILED) { |
1246 // TODO(tommi): What if the temp folder is on a different volume? | 1324 ret = installer::UNINSTALL_FAILED; |
1247 MoveSetupOutOfInstallFolder(installer_state, setup_path, | 1325 } else if (delete_result == DELETE_REQUIRES_REBOOT) { |
1248 product_state->version()); | 1326 ret = installer::UNINSTALL_REQUIRES_REBOOT; |
1249 delete_result = DeleteChromeFilesAndFolders(installer_state, | 1327 } |
1250 product_state->version()); | |
1251 } | 1328 } |
1252 | 1329 |
1253 if (delete_profile) | 1330 if (delete_profile) |
1254 DeleteLocalState(local_state_folders, product.is_chrome_frame()); | 1331 DeleteLocalState(local_state_folders, product.is_chrome_frame()); |
1255 | 1332 |
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) { | 1333 if (!force_uninstall) { |
1263 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations."; | 1334 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations."; |
1264 browser_dist->DoPostUninstallOperations(product_state->version(), | 1335 browser_dist->DoPostUninstallOperations(product_state->version(), |
1265 backup_state_file, distribution_data); | 1336 backup_state_file, distribution_data); |
1266 } | 1337 } |
1267 | 1338 |
1268 // Try and delete the preserved local state once the post-install | 1339 // Try and delete the preserved local state once the post-install |
1269 // operations are complete. | 1340 // operations are complete. |
1270 if (!backup_state_file.empty()) | 1341 if (!backup_state_file.empty()) |
1271 file_util::Delete(backup_state_file, false); | 1342 file_util::Delete(backup_state_file, false); |
1272 | 1343 |
1273 return ret; | 1344 return ret; |
1274 } | 1345 } |
1275 | 1346 |
1347 installer::InstallStatus CleanUpInstallationDirectoryAfterUninstall( | |
1348 const InstallationState& original_state, | |
1349 const InstallerState& installer_state, | |
1350 const CommandLine& cmd_line, | |
1351 installer::InstallStatus uninstall_status) { | |
1352 if (uninstall_status != installer::UNINSTALL_SUCCESSFUL && | |
1353 uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT) { | |
1354 return uninstall_status; | |
gab
2012/11/21 22:55:55
Is the goal on UNINSTALL_FAILED to halt the uninst
erikwright (departed)
2012/11/23 18:54:13
Previously, if we failed to delete a file in Delet
| |
1355 } | |
1356 | |
1357 bool remove_setup = true; | |
1358 bool remove_archive = true; | |
1359 CheckShouldRemoveSetupAndArchive(original_state, installer_state, | |
1360 &remove_setup, &remove_archive); | |
1361 if (!remove_archive) | |
1362 return uninstall_status; | |
1363 | |
1364 Version uninstall_version( | |
grt (UTC plus 2)
2012/11/21 20:40:26
it seems safer to me to get the path to setup.exe
erikwright (departed)
2012/11/21 21:51:10
Done naively, that actually seems more dangerous t
| |
1365 GetUninstallVersion(original_state, installer_state.system_install())); | |
1366 if (!uninstall_version.IsValid()) | |
1367 return installer::UNINSTALL_FAILED; | |
1368 | |
1369 if (remove_setup) { | |
1370 // In order to be able to remove the folder in which we're running, we | |
1371 // need to move setup.exe out of the install folder. | |
1372 // TODO(tommi): What if the temp folder is on a different volume? | |
1373 MoveSetupOutOfInstallFolder(installer_state, | |
1374 cmd_line.GetProgram(), | |
1375 uninstall_version); | |
1376 } | |
1377 | |
1378 FilePath install_directory( | |
1379 installer_state.GetInstallerDirectory(uninstall_version)); | |
1380 if (!RemoveInstallerFiles(install_directory, remove_setup)) | |
1381 return installer::UNINSTALL_FAILED; | |
1382 | |
1383 if (remove_setup) { | |
1384 const FilePath& target_path = installer_state.target_path(); | |
1385 if (target_path.empty()) { | |
1386 LOG(ERROR) << "No installation destination path."; | |
1387 return installer::UNINSTALL_FAILED; | |
1388 } | |
1389 | |
1390 if (DELETE_SUCCEEDED != DeleteEmptyDir(install_directory)) | |
grt (UTC plus 2)
2012/11/21 20:40:26
i think chromium style says to swap these
erikwright (departed)
2012/11/23 18:54:13
Done.
| |
1391 return installer::UNINSTALL_FAILED; | |
1392 FilePath to_delete = install_directory.DirName(); | |
1393 while (to_delete != target_path) { | |
1394 DeleteResult delete_result = DeleteEmptyDir(to_delete); | |
1395 if (delete_result == DELETE_SUCCEEDED) { | |
1396 to_delete = to_delete.DirName(); | |
1397 continue; | |
1398 } | |
1399 if (delete_result == DELETE_NOT_EMPTY && | |
gab
2012/11/21 22:55:55
else if?
This doesn't actually change the flow, b
| |
1400 uninstall_status == installer::UNINSTALL_REQUIRES_REBOOT) { | |
1401 break; | |
1402 } | |
1403 return installer::UNINSTALL_FAILED; | |
1404 | |
1405 } | |
1406 | |
1407 | |
1408 if (uninstall_status == installer::UNINSTALL_REQUIRES_REBOOT) { | |
1409 // Delete the Application directory at reboot if empty. | |
1410 ScheduleFileSystemEntityForDeletion(target_path.value().c_str()); | |
1411 | |
1412 // If we need a reboot to continue, schedule the parent directories for | |
1413 // deletion unconditionally. If they are not empty, the session manager | |
1414 // will not delete them on reboot. | |
1415 ScheduleParentAndGrandparentForDeletion(target_path); | |
1416 return installer::UNINSTALL_REQUIRES_REBOOT; | |
1417 } | |
1418 | |
1419 if (installer::DELETE_FAILED == | |
grt (UTC plus 2)
2012/11/21 20:40:26
swap here, too
erikwright (departed)
2012/11/23 18:54:13
Done.
| |
1420 DeleteApplicationProductAndVendorDirectories(target_path)) { | |
1421 return installer::UNINSTALL_FAILED; | |
1422 } | |
1423 } | |
1424 | |
1425 return installer::UNINSTALL_SUCCESSFUL; | |
1426 } | |
1427 | |
1276 } // namespace installer | 1428 } // namespace installer |
OLD | NEW |