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)) { | |
gab
2012/11/30 15:19:45
Does !installer_state.FindProduct(dist_type) here
gab
2012/12/21 21:55:53
Ping.
erikwright (departed)
2013/01/04 20:07:50
Done.
| |
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, | |
gab
2012/11/30 15:19:45
s/install_directory/installer_directory
imo, the
gab
2012/12/21 21:55:53
Ping.
erikwright (departed)
2013/01/04 20:07:50
Done.
| |
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 FilePath setup_exe_base_name(installer::kSetupExe); | |
240 | |
241 while (true) { | |
242 FilePath to_delete(file_enumerator.Next()); | |
243 if (to_delete.empty()) | |
244 break; | |
245 if (!remove_setup && to_delete.BaseName() == setup_exe_base_name) | |
246 continue; | |
247 | |
248 VLOG(1) << "Deleting install path " << to_delete.value(); | |
gab
2012/11/30 15:19:45
s/install/installer (same comment as above).
gab
2012/12/21 21:55:53
Ping.
erikwright (departed)
2013/01/04 20:07:50
Done.
| |
249 if (!file_util::Delete(to_delete, true)) { | |
250 LOG(ERROR) << "Failed to delete path: " << to_delete.value(); | |
251 success = false; | |
252 } | |
253 } | |
254 | |
255 return success; | |
256 } | |
257 | |
184 } // namespace | 258 } // namespace |
185 | 259 |
186 namespace installer { | 260 namespace installer { |
187 | 261 |
188 // This functions checks for any Chrome instances that are | 262 // This functions checks for any Chrome instances that are |
189 // running and first asks them to close politely by sending a Windows message. | 263 // 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 | 264 // 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 | 265 // procesess active after the message has been sent, this function will try |
192 // to kill them. | 266 // to kill them. |
193 void CloseAllChromeProcesses() { | 267 void CloseAllChromeProcesses() { |
(...skipping 209 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
403 const FilePath product_dir(user_data_dir.DirName()); | 477 const FilePath product_dir(user_data_dir.DirName()); |
404 if (!product_dir.empty()) | 478 if (!product_dir.empty()) |
405 DeleteEmptyDir(product_dir); | 479 DeleteEmptyDir(product_dir); |
406 } | 480 } |
407 } | 481 } |
408 | 482 |
409 return result; | 483 return result; |
410 } | 484 } |
411 | 485 |
412 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, | 486 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state, |
413 const FilePath& setup_path, | 487 const FilePath& setup_exe) { |
414 const Version& installed_version) { | |
415 bool ret = false; | 488 bool ret = false; |
416 FilePath setup_exe(installer_state.GetInstallerDirectory(installed_version) | |
417 .Append(setup_path.BaseName())); | |
418 FilePath temp_file; | 489 FilePath temp_file; |
419 if (!file_util::CreateTemporaryFile(&temp_file)) { | 490 if (!file_util::CreateTemporaryFile(&temp_file)) { |
420 LOG(ERROR) << "Failed to create temporary file for setup.exe."; | 491 LOG(ERROR) << "Failed to create temporary file for setup.exe."; |
421 } else { | 492 } else { |
422 VLOG(1) << "Attempting to move setup to: " << temp_file.value(); | 493 VLOG(1) << "Attempting to move setup to: " << temp_file.value(); |
423 ret = file_util::Move(setup_exe, temp_file); | 494 ret = file_util::Move(setup_exe, temp_file); |
424 PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value(); | 495 PLOG_IF(ERROR, !ret) << "Failed to move setup to " << temp_file.value(); |
425 | 496 |
426 // We cannot delete the file right away, but try to delete it some other | 497 // 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. | 498 // 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 } | 535 } |
465 | 536 |
466 DeleteInstallTempDir(target_path); | 537 DeleteInstallTempDir(target_path); |
467 | 538 |
468 DeleteResult result = DELETE_SUCCEEDED; | 539 DeleteResult result = DELETE_SUCCEEDED; |
469 | 540 |
470 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); | 541 FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe)); |
471 if (!file_util::Delete(app_host_exe, false)) { | 542 if (!file_util::Delete(app_host_exe, false)) { |
472 result = DELETE_FAILED; | 543 result = DELETE_FAILED; |
473 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); | 544 LOG(ERROR) << "Failed to delete path: " << app_host_exe.value(); |
474 } else { | |
475 result = DeleteApplicationProductAndVendorDirectories(target_path); | |
476 } | 545 } |
477 | 546 |
478 return result; | 547 return result; |
479 } | 548 } |
480 | 549 |
481 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, | 550 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state, |
482 const Version& installed_version) { | 551 const FilePath& installer_path) { |
gab
2012/11/30 15:19:45
s/installer_path/setup_exe to respect naming conve
gab
2012/12/21 21:55:53
Ping.
erikwright (departed)
2013/01/04 20:07:50
I chose not to change what is passed in, as that w
| |
483 const FilePath& target_path = installer_state.target_path(); | 552 const FilePath& target_path = installer_state.target_path(); |
484 if (target_path.empty()) { | 553 if (target_path.empty()) { |
485 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " | 554 LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination " |
486 << "path."; | 555 << "path."; |
487 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. | 556 return DELETE_FAILED; // Nothing else we can do to uninstall, so we return. |
488 } | 557 } |
489 | 558 |
490 DeleteInstallTempDir(target_path); | 559 DeleteInstallTempDir(target_path); |
491 | 560 |
492 DeleteResult result = DELETE_SUCCEEDED; | 561 DeleteResult result = DELETE_SUCCEEDED; |
493 | 562 |
563 FilePath installer_directory; | |
564 if (target_path.IsParent(installer_path)) | |
565 installer_directory = installer_path.DirName(); | |
gab
2012/11/30 15:19:45
This feels weird, isn't this always expected to be
gab
2012/12/21 21:55:53
Ping.
erikwright (departed)
2013/01/04 20:07:50
Not necessarily. For example, if you execute the i
| |
566 | |
567 // Enumerate all the files in target_path recursively (breadth-first). | |
568 // We delete a file or folder unless it is a parent/child of the installer | |
569 // directory. For parents of the installer directory, we will later recurse | |
570 // and delete all the children (that are not also parents/children of the | |
571 // installer directory). | |
494 using file_util::FileEnumerator; | 572 using file_util::FileEnumerator; |
495 FileEnumerator file_enumerator(target_path, false, | 573 FileEnumerator file_enumerator( |
496 FileEnumerator::FILES | FileEnumerator::DIRECTORIES); | 574 target_path, true, FileEnumerator::FILES | FileEnumerator::DIRECTORIES); |
497 while (true) { | 575 while (true) { |
498 FilePath to_delete(file_enumerator.Next()); | 576 FilePath to_delete(file_enumerator.Next()); |
499 if (to_delete.empty()) | 577 if (to_delete.empty()) |
500 break; | 578 break; |
501 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) | 579 if (to_delete.BaseName().value() == installer::kChromeAppHostExe) |
502 continue; | 580 continue; |
581 if (!installer_directory.empty() && | |
582 (to_delete == installer_directory || | |
583 installer_directory.IsParent(to_delete) || | |
584 to_delete.IsParent(installer_directory))) { | |
585 continue; | |
586 } | |
503 | 587 |
504 VLOG(1) << "Deleting install path " << to_delete.value(); | 588 VLOG(1) << "Deleting install path " << to_delete.value(); |
505 if (!file_util::Delete(to_delete, true)) { | 589 if (!file_util::Delete(to_delete, true)) { |
506 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); | 590 LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value(); |
507 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { | 591 if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) { |
508 // We don't try killing Chrome processes for Chrome Frame builds since | 592 // We don't try killing Chrome processes for Chrome Frame builds since |
509 // that is unlikely to help. Instead, schedule files for deletion and | 593 // that is unlikely to help. Instead, schedule files for deletion and |
510 // return a value that will trigger a reboot prompt. | 594 // return a value that will trigger a reboot prompt. |
511 FileEnumerator::FindInfo find_info; | 595 FileEnumerator::FindInfo find_info; |
512 file_enumerator.GetFindInfo(&find_info); | 596 file_enumerator.GetFindInfo(&find_info); |
513 if (FileEnumerator::IsDirectory(find_info)) | 597 if (FileEnumerator::IsDirectory(find_info)) |
514 ScheduleDirectoryForDeletion(to_delete.value().c_str()); | 598 ScheduleDirectoryForDeletion(to_delete.value().c_str()); |
515 else | 599 else |
516 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); | 600 ScheduleFileSystemEntityForDeletion(to_delete.value().c_str()); |
517 result = DELETE_REQUIRES_REBOOT; | 601 result = DELETE_REQUIRES_REBOOT; |
518 } else { | 602 } else { |
519 // Try closing any running Chrome processes and deleting files once | 603 // Try closing any running Chrome processes and deleting files once |
520 // again. | 604 // again. |
521 CloseAllChromeProcesses(); | 605 CloseAllChromeProcesses(); |
522 if (!file_util::Delete(to_delete, true)) { | 606 if (!file_util::Delete(to_delete, true)) { |
523 LOG(ERROR) << "Failed to delete path (2nd try): " | 607 LOG(ERROR) << "Failed to delete path (2nd try): " |
524 << to_delete.value(); | 608 << to_delete.value(); |
525 result = DELETE_FAILED; | 609 result = DELETE_FAILED; |
526 break; | 610 break; |
527 } | 611 } |
528 } | 612 } |
529 } | 613 } |
530 } | 614 } |
531 | 615 |
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; | 616 return result; |
544 } | 617 } |
545 | 618 |
546 // This method checks if Chrome is currently running or if the user has | 619 // This method checks if Chrome is currently running or if the user has |
547 // cancelled the uninstall operation by clicking Cancel on the confirmation | 620 // cancelled the uninstall operation by clicking Cancel on the confirmation |
548 // box that Chrome pops up. | 621 // box that Chrome pops up. |
549 InstallStatus IsChromeActiveOrUserCancelled( | 622 InstallStatus IsChromeActiveOrUserCancelled( |
550 const InstallerState& installer_state, | 623 const InstallerState& installer_state, |
551 const Product& product) { | 624 const Product& product) { |
552 int32 exit_code = content::RESULT_CODE_NORMAL_EXIT; | 625 int32 exit_code = content::RESULT_CODE_NORMAL_EXIT; |
(...skipping 674 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1227 product); | 1300 product); |
1228 ret = installer::UNINSTALL_SUCCESSFUL; | 1301 ret = installer::UNINSTALL_SUCCESSFUL; |
1229 | 1302 |
1230 // When deleting files, we must make sure that we're either a "single" | 1303 // When deleting files, we must make sure that we're either a "single" |
1231 // (aka non-multi) installation or we are the Chrome Binaries. | 1304 // (aka non-multi) installation or we are the Chrome Binaries. |
1232 | 1305 |
1233 std::vector<FilePath> local_state_folders; | 1306 std::vector<FilePath> local_state_folders; |
1234 GetLocalStateFolders(product, &local_state_folders); | 1307 GetLocalStateFolders(product, &local_state_folders); |
1235 FilePath backup_state_file(BackupLocalStateFile(local_state_folders)); | 1308 FilePath backup_state_file(BackupLocalStateFile(local_state_folders)); |
1236 | 1309 |
1237 DeleteResult delete_result = DELETE_SUCCEEDED; | |
1238 | |
1239 if (product.is_chrome_app_host()) { | 1310 if (product.is_chrome_app_host()) { |
1240 DeleteAppHostFilesAndFolders(installer_state, product_state->version()); | 1311 DeleteAppHostFilesAndFolders(installer_state, product_state->version()); |
1241 } else if (!installer_state.is_multi_install() || | 1312 } else if (!installer_state.is_multi_install() || |
1242 product.is_chrome_binaries()) { | 1313 product.is_chrome_binaries()) { |
1243 | 1314 DeleteResult delete_result = DeleteChromeFilesAndFolders( |
1244 // In order to be able to remove the folder in which we're running, we | 1315 installer_state, cmd_line.GetProgram()); |
gab
2012/11/30 15:19:45
cmd_line.GetProgram() could be relative path here.
gab
2012/12/21 21:55:53
Ping++
erikwright (departed)
2013/01/04 20:07:50
Done.
| |
1245 // need to move setup.exe out of the install folder. | 1316 if (delete_result == DELETE_FAILED) { |
1246 // TODO(tommi): What if the temp folder is on a different volume? | 1317 ret = installer::UNINSTALL_FAILED; |
1247 MoveSetupOutOfInstallFolder(installer_state, setup_path, | 1318 } else if (delete_result == DELETE_REQUIRES_REBOOT) { |
1248 product_state->version()); | 1319 ret = installer::UNINSTALL_REQUIRES_REBOOT; |
1249 delete_result = DeleteChromeFilesAndFolders(installer_state, | 1320 } |
1250 product_state->version()); | |
1251 } | 1321 } |
1252 | 1322 |
1253 if (delete_profile) | 1323 if (delete_profile) |
1254 DeleteLocalState(local_state_folders, product.is_chrome_frame()); | 1324 DeleteLocalState(local_state_folders, product.is_chrome_frame()); |
1255 | 1325 |
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) { | 1326 if (!force_uninstall) { |
1263 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations."; | 1327 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations."; |
1264 browser_dist->DoPostUninstallOperations(product_state->version(), | 1328 browser_dist->DoPostUninstallOperations(product_state->version(), |
1265 backup_state_file, distribution_data); | 1329 backup_state_file, distribution_data); |
1266 } | 1330 } |
1267 | 1331 |
1268 // Try and delete the preserved local state once the post-install | 1332 // Try and delete the preserved local state once the post-install |
1269 // operations are complete. | 1333 // operations are complete. |
1270 if (!backup_state_file.empty()) | 1334 if (!backup_state_file.empty()) |
1271 file_util::Delete(backup_state_file, false); | 1335 file_util::Delete(backup_state_file, false); |
1272 | 1336 |
1273 return ret; | 1337 return ret; |
1274 } | 1338 } |
1275 | 1339 |
1340 void CleanUpInstallationDirectoryAfterUninstall( | |
1341 const InstallationState& original_state, | |
1342 const InstallerState& installer_state, | |
1343 const CommandLine& cmd_line, | |
1344 installer::InstallStatus* uninstall_status) { | |
1345 if (*uninstall_status != installer::UNINSTALL_SUCCESSFUL && | |
1346 *uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT) { | |
1347 return; | |
1348 } | |
1349 const FilePath target_path(installer_state.target_path()); | |
1350 if (target_path.empty()) { | |
1351 LOG(ERROR) << "No installation destination path."; | |
1352 *uninstall_status = installer::UNINSTALL_FAILED; | |
1353 return; | |
1354 } | |
1355 FilePath setup_exe(cmd_line.GetProgram()); | |
1356 file_util::AbsolutePath(&setup_exe); | |
1357 if (!target_path.IsParent(setup_exe)) { | |
1358 LOG(INFO) << "setup.exe is not in target path. Skipping installer cleanup."; | |
1359 return; | |
1360 } | |
1361 FilePath install_directory(setup_exe.DirName()); | |
1362 | |
1363 bool remove_setup = true; | |
1364 bool remove_archive = true; | |
1365 CheckShouldRemoveSetupAndArchive(original_state, installer_state, | |
1366 &remove_setup, &remove_archive); | |
1367 if (!remove_archive) | |
1368 return; | |
1369 | |
1370 if (remove_setup) { | |
1371 // In order to be able to remove the folder in which we're running, we | |
1372 // need to move setup.exe out of the install folder. | |
1373 // TODO(tommi): What if the temp folder is on a different volume? | |
1374 MoveSetupOutOfInstallFolder(installer_state, setup_exe); | |
1375 } | |
1376 | |
1377 // Remove files from "...\<product>\Application\<version>\Installer" | |
1378 if (!RemoveInstallerFiles(install_directory, remove_setup)) { | |
1379 *uninstall_status = installer::UNINSTALL_FAILED; | |
1380 return; | |
1381 } | |
1382 | |
1383 if (!remove_setup) | |
1384 return; | |
1385 | |
1386 // Try to remove the empty directory hierarchy. | |
1387 | |
1388 // Delete "...\<product>\Application\<version>\Installer" | |
1389 if (DeleteEmptyDir(install_directory) != DELETE_SUCCEEDED) { | |
1390 *uninstall_status = installer::UNINSTALL_FAILED; | |
1391 return; | |
1392 } | |
1393 | |
1394 // Delete "...\<product>\Application\<version>" | |
1395 DeleteResult delete_result = DeleteEmptyDir(install_directory.DirName()); | |
1396 if (delete_result == DELETE_FAILED || | |
1397 (delete_result == DELETE_NOT_EMPTY && | |
1398 *uninstall_status != installer::UNINSTALL_REQUIRES_REBOOT)) { | |
1399 *uninstall_status = installer::UNINSTALL_FAILED; | |
1400 return; | |
1401 } | |
1402 | |
1403 if (*uninstall_status == installer::UNINSTALL_REQUIRES_REBOOT) { | |
1404 // Delete the Application directory at reboot if empty. | |
1405 ScheduleFileSystemEntityForDeletion(target_path.value().c_str()); | |
1406 | |
1407 // If we need a reboot to continue, schedule the parent directories for | |
1408 // deletion unconditionally. If they are not empty, the session manager | |
1409 // will not delete them on reboot. | |
1410 ScheduleParentAndGrandparentForDeletion(target_path); | |
1411 } else if (DeleteApplicationProductAndVendorDirectories(target_path) == | |
1412 installer::DELETE_FAILED) { | |
1413 *uninstall_status = installer::UNINSTALL_FAILED; | |
1414 } | |
1415 } | |
1416 | |
1276 } // namespace installer | 1417 } // namespace installer |
OLD | NEW |