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

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

Issue 11412015: Copy setup when quick-enabling app host to user-level from system-level. (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: review comments. Created 8 years 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
« no previous file with comments | « chrome/installer/setup/uninstall.h ('k') | 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 163 matching lines...) Expand 10 before | Expand all | Expand 10 after
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
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
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
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
OLDNEW
« no previous file with comments | « chrome/installer/setup/uninstall.h ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698