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
3 // found in the LICENSE file.
5 // This file defines the methods useful for uninstalling Chrome.
7 #include "chrome/installer/setup/uninstall.h"
13 #include "base/file_util.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/path_service.h"
16 #include "base/process/kill.h"
17 #include "base/strings/string16.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "base/strings/utf_string_conversions.h"
21 #include "base/win/registry.h"
22 #include "base/win/scoped_handle.h"
23 #include "base/win/shortcut.h"
24 #include "base/win/windows_version.h"
25 #include "chrome/common/chrome_constants.h"
26 #include "chrome/common/chrome_paths_internal.h"
27 #include "chrome/common/chrome_result_codes.h"
28 #include "chrome/installer/launcher_support/chrome_launcher_support.h"
29 #include "chrome/installer/setup/install.h"
30 #include "chrome/installer/setup/install_worker.h"
31 #include "chrome/installer/setup/setup_constants.h"
32 #include "chrome/installer/setup/setup_util.h"
33 #include "chrome/installer/util/auto_launch_util.h"
34 #include "chrome/installer/util/browser_distribution.h"
35 #include "chrome/installer/util/channel_info.h"
36 #include "chrome/installer/util/delete_after_reboot_helper.h"
37 #include "chrome/installer/util/google_update_constants.h"
38 #include "chrome/installer/util/google_update_settings.h"
39 #include "chrome/installer/util/helper.h"
40 #include "chrome/installer/util/install_util.h"
41 #include "chrome/installer/util/installation_state.h"
42 #include "chrome/installer/util/installer_state.h"
43 #include "chrome/installer/util/logging_installer.h"
44 #include "chrome/installer/util/self_cleaning_temp_dir.h"
45 #include "chrome/installer/util/shell_util.h"
46 #include "chrome/installer/util/util_constants.h"
47 #include "content/public/common/result_codes.h"
48 #include "extensions/common/constants.h"
49 #include "rlz/lib/rlz_lib.h"
51 // Build-time generated include file.
52 #include "registered_dlls.h" // NOLINT
54 using base::win::RegKey
;
55 using installer::InstallStatus
;
56 using installer::MasterPreferences
;
60 // Avoid leaving behind a Temp dir. If one exists, ask SelfCleaningTempDir to
61 // clean it up for us. This may involve scheduling it for deletion after
62 // reboot. Don't report that a reboot is required in this case, however.
63 // TODO(erikwright): Shouldn't this still lead to
64 // ScheduleParentAndGrandparentForDeletion?
65 void DeleteInstallTempDir(const base::FilePath
& target_path
) {
66 base::FilePath
temp_path(target_path
.DirName().Append(
67 installer::kInstallTempDir
));
68 if (base::DirectoryExists(temp_path
)) {
69 installer::SelfCleaningTempDir temp_dir
;
70 if (!temp_dir
.Initialize(target_path
.DirName(),
71 installer::kInstallTempDir
) ||
73 LOG(ERROR
) << "Failed to delete temp dir " << temp_path
.value();
78 // Iterates over the list of distribution types in |dist_types|, and
79 // adds to |update_list| the work item to update the corresponding "ap"
80 // registry value specified in |channel_info|.
81 void AddChannelValueUpdateWorkItems(
82 const installer::InstallationState
& original_state
,
83 const installer::InstallerState
& installer_state
,
84 const installer::ChannelInfo
& channel_info
,
85 const std::vector
<BrowserDistribution::Type
>& dist_types
,
86 WorkItemList
* update_list
) {
87 const bool system_level
= installer_state
.system_install();
88 const HKEY reg_root
= installer_state
.root_key();
89 for (size_t i
= 0; i
< dist_types
.size(); ++i
) {
90 BrowserDistribution::Type dist_type
= dist_types
[i
];
91 const installer::ProductState
* product_state
=
92 original_state
.GetProductState(system_level
, dist_type
);
93 // Only modify other products if they're installed and multi.
94 if (product_state
!= NULL
&&
95 product_state
->is_multi_install() &&
96 !product_state
->channel().Equals(channel_info
)) {
97 BrowserDistribution
* other_dist
=
98 BrowserDistribution::GetSpecificDistribution(dist_type
);
99 update_list
->AddSetRegValueWorkItem(reg_root
, other_dist
->GetStateKey(),
100 google_update::kRegApField
, channel_info
.value(), true);
103 product_state
!= NULL
&& product_state
->is_multi_install())
104 << "Channel value for "
105 << BrowserDistribution::GetSpecificDistribution(
106 dist_type
)->GetDisplayName()
107 << " is somehow already set to the desired new value of "
108 << channel_info
.value();
113 // Makes appropriate changes to the Google Update "ap" value in the registry.
114 // Specifically, removes the flags associated with this product ("-chrome" or
115 // "-chromeframe") from the "ap" values for all other installed products and for
116 // the multi-installer package.
117 void ProcessGoogleUpdateItems(
118 const installer::InstallationState
& original_state
,
119 const installer::InstallerState
& installer_state
,
120 const installer::Product
& product
) {
121 DCHECK(installer_state
.is_multi_install());
122 const bool system_level
= installer_state
.system_install();
123 BrowserDistribution
* distribution
= product
.distribution();
124 const installer::ProductState
* product_state
=
125 original_state
.GetProductState(system_level
, distribution
->GetType());
126 DCHECK(product_state
!= NULL
);
127 installer::ChannelInfo channel_info
;
129 // Remove product's flags from the channel value.
130 channel_info
.set_value(product_state
->channel().value());
131 const bool modified
= product
.SetChannelFlags(false, &channel_info
);
133 // Apply the new channel value to all other products and to the multi package.
135 scoped_ptr
<WorkItemList
>
136 update_list(WorkItem::CreateNoRollbackWorkItemList());
137 std::vector
<BrowserDistribution::Type
> dist_types
;
138 for (size_t i
= 0; i
< BrowserDistribution::NUM_TYPES
; ++i
) {
139 BrowserDistribution::Type other_dist_type
=
140 static_cast<BrowserDistribution::Type
>(i
);
141 if (distribution
->GetType() != other_dist_type
)
142 dist_types
.push_back(other_dist_type
);
144 AddChannelValueUpdateWorkItems(original_state
, installer_state
,
145 channel_info
, dist_types
,
147 bool success
= update_list
->Do();
148 LOG_IF(ERROR
, !success
) << "Failed updating channel values.";
152 void ProcessOnOsUpgradeWorkItems(
153 const installer::InstallerState
& installer_state
,
154 const installer::Product
& product
) {
155 scoped_ptr
<WorkItemList
> work_item_list(
156 WorkItem::CreateNoRollbackWorkItemList());
157 AddOsUpgradeWorkItems(installer_state
, base::FilePath(), Version(), product
,
158 work_item_list
.get());
159 if (!work_item_list
->Do())
160 LOG(ERROR
) << "Failed to remove on-os-upgrade command.";
163 void ProcessIELowRightsPolicyWorkItems(
164 const installer::InstallerState
& installer_state
) {
165 scoped_ptr
<WorkItemList
> work_items(WorkItem::CreateNoRollbackWorkItemList());
166 AddDeleteOldIELowRightsPolicyWorkItems(installer_state
, work_items
.get());
168 installer::RefreshElevationPolicy();
171 void ClearRlzProductState() {
172 const rlz_lib::AccessPoint points
[] = {rlz_lib::CHROME_OMNIBOX
,
173 rlz_lib::CHROME_HOME_PAGE
,
174 rlz_lib::NO_ACCESS_POINT
};
176 rlz_lib::ClearProductState(rlz_lib::CHROME
, points
);
178 // If chrome has been reactivated, clear all events for this brand as well.
179 base::string16 reactivation_brand_wide
;
180 if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand_wide
)) {
181 std::string
reactivation_brand(WideToASCII(reactivation_brand_wide
));
182 rlz_lib::SupplementaryBranding
branding(reactivation_brand
.c_str());
183 rlz_lib::ClearProductState(rlz_lib::CHROME
, points
);
187 // Decides whether setup.exe and the installer archive should be removed based
188 // on the original and installer states:
189 // * non-multi product being uninstalled: remove both
190 // * any multi product left besides App Host: keep both
191 // * only App Host left: keep setup.exe
192 void CheckShouldRemoveSetupAndArchive(
193 const installer::InstallationState
& original_state
,
194 const installer::InstallerState
& installer_state
,
196 bool* remove_archive
) {
197 *remove_setup
= true;
198 *remove_archive
= true;
200 // If any multi-install product is left (other than App Host) we must leave
201 // the installer and archive. For the App Host, we only leave the installer.
202 if (!installer_state
.is_multi_install()) {
203 VLOG(1) << "Removing all installer files for a non-multi installation.";
205 // Loop through all known products...
206 for (size_t i
= 0; i
< BrowserDistribution::NUM_TYPES
; ++i
) {
207 BrowserDistribution::Type dist_type
=
208 static_cast<BrowserDistribution::Type
>(i
);
209 const installer::ProductState
* product_state
=
210 original_state
.GetProductState(
211 installer_state
.system_install(), dist_type
);
212 // If the product is installed, in multi mode, and is not part of the
213 // active uninstallation...
214 if (product_state
&& product_state
->is_multi_install() &&
215 !installer_state
.FindProduct(dist_type
)) {
216 // setup.exe will not be removed as there is a remaining multi-install
218 *remove_setup
= false;
219 // As a special case, we can still remove the actual archive if the
220 // only remaining product is the App Host.
221 if (dist_type
!= BrowserDistribution::CHROME_APP_HOST
) {
222 VLOG(1) << "Keeping all installer files due to a remaining "
223 << "multi-install product.";
224 *remove_archive
= false;
227 VLOG(1) << "Keeping setup.exe due to a remaining "
228 << "app-host installation.";
231 VLOG(1) << "Removing the installer archive.";
233 VLOG(1) << "Removing setup.exe.";
237 // Removes all files from the installer directory, leaving setup.exe iff
238 // |remove_setup| is false.
239 // Returns false in case of an error.
240 bool RemoveInstallerFiles(const base::FilePath
& installer_directory
,
242 base::FileEnumerator
file_enumerator(
245 base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
);
248 base::FilePath
setup_exe_base_name(installer::kSetupExe
);
251 base::FilePath
to_delete(file_enumerator
.Next());
252 if (to_delete
.empty())
254 if (!remove_setup
&& to_delete
.BaseName() == setup_exe_base_name
)
257 VLOG(1) << "Deleting installer path " << to_delete
.value();
258 if (!base::DeleteFile(to_delete
, true)) {
259 LOG(ERROR
) << "Failed to delete path: " << to_delete
.value();
269 namespace installer
{
271 // Kills all Chrome processes, immediately.
272 void CloseAllChromeProcesses() {
273 base::CleanupProcesses(installer::kChromeExe
, base::TimeDelta(),
274 content::RESULT_CODE_HUNG
, NULL
);
275 base::CleanupProcesses(installer::kNaClExe
, base::TimeDelta(),
276 content::RESULT_CODE_HUNG
, NULL
);
279 // Attempts to close the Chrome Frame helper process by sending WM_CLOSE
280 // messages to its window, or just killing it if that doesn't work.
281 void CloseChromeFrameHelperProcess() {
282 HWND window
= FindWindow(installer::kChromeFrameHelperWndClass
, NULL
);
283 if (!::IsWindow(window
))
286 const DWORD kWaitMs
= 3000;
289 ::GetWindowThreadProcessId(window
, &pid
);
291 base::win::ScopedHandle
process(::OpenProcess(SYNCHRONIZE
, FALSE
, pid
));
292 PLOG_IF(INFO
, !process
) << "Failed to open process: " << pid
;
295 if (SendMessageTimeout(window
, WM_CLOSE
, 0, 0, SMTO_BLOCK
, kWaitMs
, NULL
) &&
297 VLOG(1) << "Waiting for " << installer::kChromeFrameHelperExe
;
298 DWORD wait
= ::WaitForSingleObject(process
, kWaitMs
);
299 if (wait
!= WAIT_OBJECT_0
) {
300 LOG(WARNING
) << "Wait for " << installer::kChromeFrameHelperExe
301 << " to exit failed or timed out.";
304 VLOG(1) << installer::kChromeFrameHelperExe
<< " exited normally.";
309 VLOG(1) << installer::kChromeFrameHelperExe
<< " hung. Killing.";
310 base::CleanupProcesses(installer::kChromeFrameHelperExe
, base::TimeDelta(),
311 content::RESULT_CODE_HUNG
, NULL
);
315 // Updates shortcuts to |old_target_exe| that have non-empty args, making them
316 // target |new_target_exe| instead. The non-empty args requirement is a
317 // heuristic to determine whether a shortcut is "user-generated". This routine
318 // can only be called for user-level installs.
319 void RetargetUserShortcutsWithArgs(const InstallerState
& installer_state
,
320 const Product
& product
,
321 const base::FilePath
& old_target_exe
,
322 const base::FilePath
& new_target_exe
) {
323 if (installer_state
.system_install()) {
327 BrowserDistribution
* dist
= product
.distribution();
328 ShellUtil::ShellChange install_level
= ShellUtil::CURRENT_USER
;
330 // Retarget all shortcuts that point to |old_target_exe| from all
331 // ShellUtil::ShortcutLocations.
332 VLOG(1) << "Retargeting shortcuts.";
333 for (int location
= ShellUtil::SHORTCUT_LOCATION_FIRST
;
334 location
< ShellUtil::NUM_SHORTCUT_LOCATIONS
; ++location
) {
335 if (!ShellUtil::RetargetShortcutsWithArgs(
336 static_cast<ShellUtil::ShortcutLocation
>(location
), dist
,
337 install_level
, old_target_exe
, new_target_exe
)) {
338 LOG(WARNING
) << "Failed to retarget shortcuts in ShortcutLocation: "
344 // Deletes shortcuts at |install_level| from Start menu, Desktop,
345 // Quick Launch, taskbar, and secondary tiles on the Start Screen (Win8+).
346 // Only shortcuts pointing to |target_exe| will be removed.
347 void DeleteShortcuts(const InstallerState
& installer_state
,
348 const Product
& product
,
349 const base::FilePath
& target_exe
) {
350 BrowserDistribution
* dist
= product
.distribution();
352 // The per-user shortcut for this user, if present on a system-level install,
353 // has already been deleted in chrome_browser_main_win.cc::DoUninstallTasks().
354 ShellUtil::ShellChange install_level
= installer_state
.system_install() ?
355 ShellUtil::SYSTEM_LEVEL
: ShellUtil::CURRENT_USER
;
357 // Delete and unpin all shortcuts that point to |target_exe| from all
358 // ShellUtil::ShortcutLocations.
359 VLOG(1) << "Deleting shortcuts.";
360 for (int location
= ShellUtil::SHORTCUT_LOCATION_FIRST
;
361 location
< ShellUtil::NUM_SHORTCUT_LOCATIONS
; ++location
) {
362 if (!ShellUtil::RemoveShortcuts(
363 static_cast<ShellUtil::ShortcutLocation
>(location
), dist
,
364 install_level
, target_exe
)) {
365 LOG(WARNING
) << "Failed to delete shortcuts in ShortcutLocation:"
371 bool ScheduleParentAndGrandparentForDeletion(const base::FilePath
& path
) {
372 base::FilePath parent_dir
= path
.DirName();
373 bool ret
= ScheduleFileSystemEntityForDeletion(parent_dir
);
375 LOG(ERROR
) << "Failed to schedule parent dir for deletion: "
376 << parent_dir
.value();
378 base::FilePath
grandparent_dir(parent_dir
.DirName());
379 ret
= ScheduleFileSystemEntityForDeletion(grandparent_dir
);
381 LOG(ERROR
) << "Failed to schedule grandparent dir for deletion: "
382 << grandparent_dir
.value();
388 // Deletes the given directory if it is empty. Returns DELETE_SUCCEEDED if the
389 // directory is deleted, DELETE_NOT_EMPTY if it is not empty, and DELETE_FAILED
391 DeleteResult
DeleteEmptyDir(const base::FilePath
& path
) {
392 if (!base::IsDirectoryEmpty(path
))
393 return DELETE_NOT_EMPTY
;
395 if (base::DeleteFile(path
, true))
396 return DELETE_SUCCEEDED
;
398 LOG(ERROR
) << "Failed to delete folder: " << path
.value();
399 return DELETE_FAILED
;
402 void GetLocalStateFolders(const Product
& product
,
403 std::vector
<base::FilePath
>* paths
) {
404 // Obtain the location of the user profile data.
405 product
.GetUserDataPaths(paths
);
406 LOG_IF(ERROR
, paths
->empty())
407 << "Could not retrieve user's profile directory.";
410 // Creates a copy of the local state file and returns a path to the copy.
411 base::FilePath
BackupLocalStateFile(
412 const std::vector
<base::FilePath
>& local_state_folders
) {
413 base::FilePath backup
;
415 // Copy the first local state file that is found.
416 for (size_t i
= 0; i
< local_state_folders
.size(); ++i
) {
417 const base::FilePath
& local_state_folder
= local_state_folders
[i
];
418 base::FilePath
state_file(
419 local_state_folder
.Append(chrome::kLocalStateFilename
));
420 if (!base::PathExists(state_file
))
422 if (!base::CreateTemporaryFile(&backup
))
423 LOG(ERROR
) << "Failed to create temporary file for Local State.";
425 base::CopyFile(state_file
, backup
);
431 // Deletes all user data directories for a product.
432 DeleteResult
DeleteLocalState(
433 const std::vector
<base::FilePath
>& local_state_folders
,
434 bool schedule_on_failure
) {
435 if (local_state_folders
.empty())
436 return DELETE_SUCCEEDED
;
438 DeleteResult result
= DELETE_SUCCEEDED
;
439 for (size_t i
= 0; i
< local_state_folders
.size(); ++i
) {
440 const base::FilePath
& user_local_state
= local_state_folders
[i
];
441 VLOG(1) << "Deleting user profile " << user_local_state
.value();
442 if (!base::DeleteFile(user_local_state
, true)) {
443 LOG(ERROR
) << "Failed to delete user profile dir: "
444 << user_local_state
.value();
445 if (schedule_on_failure
) {
446 ScheduleDirectoryForDeletion(user_local_state
);
447 result
= DELETE_REQUIRES_REBOOT
;
449 result
= DELETE_FAILED
;
454 if (result
== DELETE_REQUIRES_REBOOT
) {
455 ScheduleParentAndGrandparentForDeletion(local_state_folders
[0]);
457 const base::FilePath
user_data_dir(local_state_folders
[0].DirName());
458 if (!user_data_dir
.empty() &&
459 DeleteEmptyDir(user_data_dir
) == DELETE_SUCCEEDED
) {
460 const base::FilePath
product_dir(user_data_dir
.DirName());
461 if (!product_dir
.empty())
462 DeleteEmptyDir(product_dir
);
469 // Moves setup to a temporary file, outside of the install folder. Also attempts
470 // to change the current directory to the TMP directory. On Windows, each
471 // process has a handle to its CWD. If setup.exe's CWD happens to be within the
472 // install directory, deletion will fail as a result of the open handle.
473 bool MoveSetupOutOfInstallFolder(const InstallerState
& installer_state
,
474 const base::FilePath
& setup_exe
) {
476 base::FilePath tmp_dir
;
477 base::FilePath temp_file
;
478 if (!PathService::Get(base::DIR_TEMP
, &tmp_dir
)) {
480 } else if (!base::CreateTemporaryFileInDir(tmp_dir
, &temp_file
)) {
481 LOG(ERROR
) << "Failed to create temporary file for setup.exe.";
483 VLOG(1) << "Changing current directory to: " << tmp_dir
.value();
484 if (!file_util::SetCurrentDirectory(tmp_dir
))
485 PLOG(ERROR
) << "Failed to change the current directory.";
487 VLOG(1) << "Attempting to move setup to: " << temp_file
.value();
488 ret
= base::Move(setup_exe
, temp_file
);
489 PLOG_IF(ERROR
, !ret
) << "Failed to move setup to " << temp_file
.value();
491 // We cannot delete the file right away, but try to delete it some other
492 // way. Either with the help of a different process or the system.
493 if (ret
&& !base::DeleteFileAfterReboot(temp_file
)) {
494 static const uint32 kDeleteAfterMs
= 10 * 1000;
495 installer::DeleteFileFromTempProcess(temp_file
, kDeleteAfterMs
);
501 DeleteResult
DeleteChromeDirectoriesIfEmpty(
502 const base::FilePath
& application_directory
) {
503 DeleteResult
result(DeleteEmptyDir(application_directory
));
504 if (result
== DELETE_SUCCEEDED
) {
505 // Now check and delete if the parent directories are empty
506 // For example Google\Chrome or Chromium
507 const base::FilePath
product_directory(application_directory
.DirName());
508 if (!product_directory
.empty()) {
509 result
= DeleteEmptyDir(product_directory
);
510 if (result
== DELETE_SUCCEEDED
) {
511 const base::FilePath
vendor_directory(product_directory
.DirName());
512 if (!vendor_directory
.empty())
513 result
= DeleteEmptyDir(vendor_directory
);
517 if (result
== DELETE_NOT_EMPTY
)
518 result
= DELETE_SUCCEEDED
;
522 DeleteResult
DeleteAppHostFilesAndFolders(const InstallerState
& installer_state
,
523 const Version
& installed_version
) {
524 const base::FilePath
& target_path
= installer_state
.target_path();
525 if (target_path
.empty()) {
526 LOG(ERROR
) << "DeleteAppHostFilesAndFolders: no installation destination "
528 return DELETE_FAILED
; // Nothing else we can do to uninstall, so we return.
531 DeleteInstallTempDir(target_path
);
533 DeleteResult result
= DELETE_SUCCEEDED
;
535 base::FilePath
app_host_exe(target_path
.Append(installer::kChromeAppHostExe
));
536 if (!base::DeleteFile(app_host_exe
, false)) {
537 result
= DELETE_FAILED
;
538 LOG(ERROR
) << "Failed to delete path: " << app_host_exe
.value();
544 DeleteResult
DeleteChromeFilesAndFolders(const InstallerState
& installer_state
,
545 const base::FilePath
& setup_exe
) {
546 const base::FilePath
& target_path
= installer_state
.target_path();
547 if (target_path
.empty()) {
548 LOG(ERROR
) << "DeleteChromeFilesAndFolders: no installation destination "
550 return DELETE_FAILED
; // Nothing else we can do to uninstall, so we return.
553 DeleteInstallTempDir(target_path
);
555 DeleteResult result
= DELETE_SUCCEEDED
;
557 base::FilePath installer_directory
;
558 if (target_path
.IsParent(setup_exe
))
559 installer_directory
= setup_exe
.DirName();
561 // Enumerate all the files in target_path recursively (breadth-first).
562 // We delete a file or folder unless it is a parent/child of the installer
563 // directory. For parents of the installer directory, we will later recurse
564 // and delete all the children (that are not also parents/children of the
565 // installer directory).
566 base::FileEnumerator
file_enumerator(target_path
, true,
567 base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
);
569 base::FilePath
to_delete(file_enumerator
.Next());
570 if (to_delete
.empty())
572 if (to_delete
.BaseName().value() == installer::kChromeAppHostExe
)
574 if (!installer_directory
.empty() &&
575 (to_delete
== installer_directory
||
576 installer_directory
.IsParent(to_delete
) ||
577 to_delete
.IsParent(installer_directory
))) {
581 VLOG(1) << "Deleting install path " << to_delete
.value();
582 if (!base::DeleteFile(to_delete
, true)) {
583 LOG(ERROR
) << "Failed to delete path (1st try): " << to_delete
.value();
584 if (installer_state
.FindProduct(BrowserDistribution::CHROME_FRAME
)) {
585 // We don't try killing Chrome processes for Chrome Frame builds since
586 // that is unlikely to help. Instead, schedule files for deletion and
587 // return a value that will trigger a reboot prompt.
588 base::FileEnumerator::FileInfo find_info
= file_enumerator
.GetInfo();
589 if (find_info
.IsDirectory())
590 ScheduleDirectoryForDeletion(to_delete
);
592 ScheduleFileSystemEntityForDeletion(to_delete
);
593 result
= DELETE_REQUIRES_REBOOT
;
595 // Try closing any running Chrome processes and deleting files once
597 CloseAllChromeProcesses();
598 if (!base::DeleteFile(to_delete
, true)) {
599 LOG(ERROR
) << "Failed to delete path (2nd try): "
600 << to_delete
.value();
601 result
= DELETE_FAILED
;
611 // This method checks if Chrome is currently running or if the user has
612 // cancelled the uninstall operation by clicking Cancel on the confirmation
613 // box that Chrome pops up.
614 InstallStatus
IsChromeActiveOrUserCancelled(
615 const InstallerState
& installer_state
,
616 const Product
& product
) {
617 int32 exit_code
= content::RESULT_CODE_NORMAL_EXIT
;
618 CommandLine
options(CommandLine::NO_PROGRAM
);
619 options
.AppendSwitch(installer::switches::kUninstall
);
621 // Here we want to save user from frustration (in case of Chrome crashes)
622 // and continue with the uninstallation as long as chrome.exe process exit
623 // code is NOT one of the following:
624 // - UNINSTALL_CHROME_ALIVE - chrome.exe is currently running
625 // - UNINSTALL_USER_CANCEL - User cancelled uninstallation
626 // - HUNG - chrome.exe was killed by HuntForZombieProcesses() (until we can
627 // give this method some brains and not kill chrome.exe launched
628 // by us, we will not uninstall if we get this return code).
629 VLOG(1) << "Launching Chrome to do uninstall tasks.";
630 if (product
.LaunchChromeAndWait(installer_state
.target_path(), options
,
632 VLOG(1) << "chrome.exe launched for uninstall confirmation returned: "
634 if ((exit_code
== chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE
) ||
635 (exit_code
== chrome::RESULT_CODE_UNINSTALL_USER_CANCEL
) ||
636 (exit_code
== content::RESULT_CODE_HUNG
))
637 return installer::UNINSTALL_CANCELLED
;
639 if (exit_code
== chrome::RESULT_CODE_UNINSTALL_DELETE_PROFILE
)
640 return installer::UNINSTALL_DELETE_PROFILE
;
642 PLOG(ERROR
) << "Failed to launch chrome.exe for uninstall confirmation.";
645 return installer::UNINSTALL_CONFIRMED
;
648 bool ShouldDeleteProfile(const InstallerState
& installer_state
,
649 const CommandLine
& cmd_line
, InstallStatus status
,
650 const Product
& product
) {
651 bool should_delete
= false;
653 // Chrome Frame uninstallations always want to delete the profile (we have no
654 // UI to prompt otherwise and the profile stores no useful data anyway)
655 // unless they are managed by MSI. MSI uninstalls will explicitly include
656 // the --delete-profile flag to distinguish them from MSI upgrades.
657 if (product
.is_chrome_frame() && !installer_state
.is_msi()) {
658 should_delete
= true;
661 status
== installer::UNINSTALL_DELETE_PROFILE
||
662 cmd_line
.HasSwitch(installer::switches::kDeleteProfile
);
665 return should_delete
;
668 // Removes XP-era filetype registration making Chrome the default browser.
669 // MSDN (see http://msdn.microsoft.com/library/windows/desktop/cc144148.aspx)
670 // tells us not to do this, but certain applications break following
671 // uninstallation if we don't.
672 void RemoveFiletypeRegistration(const InstallerState
& installer_state
,
674 const base::string16
& browser_entry_suffix
) {
675 base::string16
classes_path(ShellUtil::kRegClasses
);
676 classes_path
.push_back(base::FilePath::kSeparators
[0]);
678 BrowserDistribution
* distribution
= BrowserDistribution::GetDistribution();
679 const base::string16
prog_id(
680 distribution
->GetBrowserProgIdPrefix() + browser_entry_suffix
);
682 // Delete each filetype association if it references this Chrome. Take care
683 // not to delete the association if it references a system-level install of
684 // Chrome (only a risk if the suffix is empty). Don't delete the whole key
685 // since other apps may have stored data there.
686 std::vector
<const wchar_t*> cleared_assocs
;
687 if (installer_state
.system_install() ||
688 !browser_entry_suffix
.empty() ||
689 !base::win::RegKey(HKEY_LOCAL_MACHINE
, (classes_path
+ prog_id
).c_str(),
690 KEY_QUERY_VALUE
).Valid()) {
691 InstallUtil::ValueEquals
prog_id_pred(prog_id
);
692 for (const wchar_t* const* filetype
=
693 &ShellUtil::kPotentialFileAssociations
[0]; *filetype
!= NULL
;
695 if (InstallUtil::DeleteRegistryValueIf(
696 root
, (classes_path
+ *filetype
).c_str(), NULL
,
697 prog_id_pred
) == InstallUtil::DELETED
) {
698 cleared_assocs
.push_back(*filetype
);
703 // For all filetype associations in HKLM that have just been removed, attempt
704 // to restore some reasonable value. We have no definitive way of knowing
705 // what handlers are the most appropriate, so we use a fixed mapping based on
706 // the default values for a fresh install of Windows.
707 if (root
== HKEY_LOCAL_MACHINE
) {
708 base::string16 assoc
;
709 base::win::RegKey key
;
711 for (size_t i
= 0; i
< cleared_assocs
.size(); ++i
) {
712 const wchar_t* replacement_prog_id
= NULL
;
713 assoc
.assign(cleared_assocs
[i
]);
715 // Inelegant, but simpler than a pure data-driven approach.
716 if (assoc
== L
".htm" || assoc
== L
".html")
717 replacement_prog_id
= L
"htmlfile";
718 else if (assoc
== L
".xht" || assoc
== L
".xhtml")
719 replacement_prog_id
= L
"xhtmlfile";
721 if (!replacement_prog_id
) {
722 LOG(WARNING
) << "No known replacement ProgID for " << assoc
724 } else if (key
.Open(HKEY_LOCAL_MACHINE
,
725 (classes_path
+ replacement_prog_id
).c_str(),
726 KEY_QUERY_VALUE
) == ERROR_SUCCESS
&&
727 (key
.Open(HKEY_LOCAL_MACHINE
, (classes_path
+ assoc
).c_str(),
728 KEY_SET_VALUE
) != ERROR_SUCCESS
||
729 key
.WriteValue(NULL
, replacement_prog_id
) != ERROR_SUCCESS
)) {
730 // The replacement ProgID is registered on the computer but the attempt
731 // to set it for the filetype failed.
732 LOG(ERROR
) << "Failed to restore system-level filetype association "
733 << assoc
<< " = " << replacement_prog_id
;
739 bool DeleteChromeRegistrationKeys(const InstallerState
& installer_state
,
740 BrowserDistribution
* dist
,
742 const base::string16
& browser_entry_suffix
,
743 InstallStatus
* exit_code
) {
745 if (dist
->GetDefaultBrowserControlPolicy() ==
746 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED
) {
747 // We should have never set those keys.
751 base::FilePath
chrome_exe(installer_state
.target_path().Append(kChromeExe
));
753 // Delete Software\Classes\ChromeHTML.
754 const base::string16
prog_id(
755 dist
->GetBrowserProgIdPrefix() + browser_entry_suffix
);
756 base::string16
reg_prog_id(ShellUtil::kRegClasses
);
757 reg_prog_id
.push_back(base::FilePath::kSeparators
[0]);
758 reg_prog_id
.append(prog_id
);
759 InstallUtil::DeleteRegistryKey(root
, reg_prog_id
);
761 // Delete Software\Classes\Chrome.
762 base::string16
reg_app_id(ShellUtil::kRegClasses
);
763 reg_app_id
.push_back(base::FilePath::kSeparators
[0]);
764 // Append the requested suffix manually here (as ShellUtil::GetBrowserModelId
765 // would otherwise try to figure out the currently installed suffix).
766 reg_app_id
.append(dist
->GetBaseAppId() + browser_entry_suffix
);
767 InstallUtil::DeleteRegistryKey(root
, reg_app_id
);
769 // Delete all Start Menu Internet registrations that refer to this Chrome.
771 using base::win::RegistryKeyIterator
;
772 InstallUtil::ProgramCompare
open_command_pred(chrome_exe
);
773 base::string16 client_name
;
774 base::string16 client_key
;
775 base::string16 open_key
;
776 for (RegistryKeyIterator
iter(root
, ShellUtil::kRegStartMenuInternet
);
777 iter
.Valid(); ++iter
) {
778 client_name
.assign(iter
.Name());
779 client_key
.assign(ShellUtil::kRegStartMenuInternet
)
781 .append(client_name
);
782 open_key
.assign(client_key
).append(ShellUtil::kRegShellOpen
);
783 if (InstallUtil::DeleteRegistryKeyIf(root
, client_key
, open_key
, NULL
,
784 open_command_pred
) != InstallUtil::NOT_FOUND
) {
785 // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
786 // references this Chrome (i.e., if it was made the default browser).
787 InstallUtil::DeleteRegistryValueIf(
788 root
, ShellUtil::kRegStartMenuInternet
, NULL
,
789 InstallUtil::ValueEquals(client_name
));
790 // Also delete the value for the default user if we're operating in
792 if (root
== HKEY_LOCAL_MACHINE
) {
793 InstallUtil::DeleteRegistryValueIf(
795 base::string16(L
".DEFAULT\\").append(
796 ShellUtil::kRegStartMenuInternet
).c_str(),
797 NULL
, InstallUtil::ValueEquals(client_name
));
803 // Delete Software\RegisteredApplications\Chromium
804 InstallUtil::DeleteRegistryValue(
805 root
, ShellUtil::kRegRegisteredApplications
,
806 dist
->GetBaseAppName() + browser_entry_suffix
);
808 // Delete the App Paths and Applications keys that let Explorer find Chrome:
809 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
810 base::string16
app_key(ShellUtil::kRegClasses
);
811 app_key
.push_back(base::FilePath::kSeparators
[0]);
812 app_key
.append(L
"Applications");
813 app_key
.push_back(base::FilePath::kSeparators
[0]);
814 app_key
.append(installer::kChromeExe
);
815 InstallUtil::DeleteRegistryKey(root
, app_key
);
817 base::string16
app_path_key(ShellUtil::kAppPathsRegistryKey
);
818 app_path_key
.push_back(base::FilePath::kSeparators
[0]);
819 app_path_key
.append(installer::kChromeExe
);
820 InstallUtil::DeleteRegistryKey(root
, app_path_key
);
822 // Cleanup OpenWithList and OpenWithProgids:
823 // http://msdn.microsoft.com/en-us/library/bb166549
824 base::string16 file_assoc_key
;
825 base::string16 open_with_list_key
;
826 base::string16 open_with_progids_key
;
827 for (int i
= 0; ShellUtil::kPotentialFileAssociations
[i
] != NULL
; ++i
) {
828 file_assoc_key
.assign(ShellUtil::kRegClasses
);
829 file_assoc_key
.push_back(base::FilePath::kSeparators
[0]);
830 file_assoc_key
.append(ShellUtil::kPotentialFileAssociations
[i
]);
831 file_assoc_key
.push_back(base::FilePath::kSeparators
[0]);
833 open_with_list_key
.assign(file_assoc_key
);
834 open_with_list_key
.append(L
"OpenWithList");
835 open_with_list_key
.push_back(base::FilePath::kSeparators
[0]);
836 open_with_list_key
.append(installer::kChromeExe
);
837 InstallUtil::DeleteRegistryKey(root
, open_with_list_key
);
839 open_with_progids_key
.assign(file_assoc_key
);
840 open_with_progids_key
.append(ShellUtil::kRegOpenWithProgids
);
841 InstallUtil::DeleteRegistryValue(root
, open_with_progids_key
, prog_id
);
844 // Cleanup in case Chrome had been made the default browser.
846 // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
847 // references this Chrome. Do this explicitly here for the case where HKCU is
848 // being processed; the iteration above will have no hits since registration
850 InstallUtil::DeleteRegistryValueIf(
851 root
, ShellUtil::kRegStartMenuInternet
, NULL
,
852 InstallUtil::ValueEquals(dist
->GetBaseAppName() + browser_entry_suffix
));
854 // Delete each protocol association if it references this Chrome.
855 InstallUtil::ProgramCompare
open_command_pred(chrome_exe
);
856 base::string16
parent_key(ShellUtil::kRegClasses
);
857 parent_key
.push_back(base::FilePath::kSeparators
[0]);
858 const base::string16::size_type base_length
= parent_key
.size();
859 base::string16 child_key
;
860 for (const wchar_t* const* proto
=
861 &ShellUtil::kPotentialProtocolAssociations
[0];
864 parent_key
.resize(base_length
);
865 parent_key
.append(*proto
);
866 child_key
.assign(parent_key
).append(ShellUtil::kRegShellOpen
);
867 InstallUtil::DeleteRegistryKeyIf(root
, parent_key
, child_key
, NULL
,
871 RemoveFiletypeRegistration(installer_state
, root
, browser_entry_suffix
);
873 *exit_code
= installer::UNINSTALL_SUCCESSFUL
;
877 void RemoveChromeLegacyRegistryKeys(BrowserDistribution
* dist
,
878 const base::string16
& chrome_exe
) {
879 // We used to register Chrome to handle crx files, but this turned out
880 // to be not worth the hassle. Remove these old registry entries if
881 // they exist. See: http://codereview.chromium.org/210007
883 #if defined(GOOGLE_CHROME_BUILD)
884 const wchar_t kChromeExtProgId
[] = L
"ChromeExt";
886 const wchar_t kChromeExtProgId
[] = L
"ChromiumExt";
889 HKEY roots
[] = { HKEY_LOCAL_MACHINE
, HKEY_CURRENT_USER
};
890 for (size_t i
= 0; i
< arraysize(roots
); ++i
) {
891 base::string16 suffix
;
892 if (roots
[i
] == HKEY_LOCAL_MACHINE
)
893 suffix
= ShellUtil::GetCurrentInstallationSuffix(dist
, chrome_exe
);
895 // Delete Software\Classes\ChromeExt,
896 base::string16
ext_prog_id(ShellUtil::kRegClasses
);
897 ext_prog_id
.push_back(base::FilePath::kSeparators
[0]);
898 ext_prog_id
.append(kChromeExtProgId
);
899 ext_prog_id
.append(suffix
);
900 InstallUtil::DeleteRegistryKey(roots
[i
], ext_prog_id
);
902 // Delete Software\Classes\.crx,
903 base::string16
ext_association(ShellUtil::kRegClasses
);
904 ext_association
.append(L
"\\");
905 ext_association
.append(extensions::kExtensionFileExtension
);
906 InstallUtil::DeleteRegistryKey(roots
[i
], ext_association
);
910 // Builds and executes a work item list to remove DelegateExecute verb handler
911 // work items for |product|. This will be a noop for products whose
912 // corresponding BrowserDistribution implementations do not publish a CLSID via
913 // GetCommandExecuteImplClsid.
914 bool ProcessDelegateExecuteWorkItems(const InstallerState
& installer_state
,
915 const Product
& product
) {
916 scoped_ptr
<WorkItemList
> item_list(WorkItem::CreateNoRollbackWorkItemList());
917 AddDelegateExecuteWorkItems(installer_state
, base::FilePath(), Version(),
918 product
, item_list
.get());
919 return item_list
->Do();
922 // Removes Active Setup entries from the registry. This cannot be done through
923 // a work items list as usual because of different paths based on conditionals,
924 // but otherwise respects the no rollback/best effort uninstall mentality.
925 // This will only apply for system-level installs of Chrome/Chromium and will be
926 // a no-op for all other types of installs.
927 void UninstallActiveSetupEntries(const InstallerState
& installer_state
,
928 const Product
& product
) {
929 VLOG(1) << "Uninstalling registry entries for ActiveSetup.";
930 BrowserDistribution
* distribution
= product
.distribution();
932 if (!product
.is_chrome() || !installer_state
.system_install()) {
933 const char* install_level
=
934 installer_state
.system_install() ? "system" : "user";
935 VLOG(1) << "No Active Setup processing to do for " << install_level
936 << "-level " << distribution
->GetDisplayName();
940 const base::string16
active_setup_path(
941 InstallUtil::GetActiveSetupPath(distribution
));
942 InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE
, active_setup_path
);
944 // Windows leaves keys behind in HKCU\\Software\\(Wow6432Node\\)?Microsoft\\
945 // Active Setup\\Installed Components\\{guid}
946 // for every user that logged in since system-level Chrome was installed.
947 // This is a problem because Windows compares the value of the Version subkey
948 // in there with the value of the Version subkey in the matching HKLM entries
949 // before running Chrome's Active Setup so if Chrome was to be reinstalled
950 // with a lesser version (e.g. switching back to a more stable channel), the
951 // affected users would not have Chrome's Active Setup called until Chrome
952 // eventually updated passed that user's registered Version.
954 // It is however very hard to delete those values as the registry hives for
955 // other users are not loaded by default under HKEY_USERS (unless a user is
956 // logged on or has a process impersonating him).
958 // Following our best effort uninstall practices, try to delete the value in
959 // all users hives. If a given user's hive is not loaded, try to load it to
960 // proceed with the deletion (failure to do so is ignored).
962 static const wchar_t kProfileList
[] =
963 L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\";
965 // Windows automatically adds Wow6432Node when creating/deleting the HKLM key,
966 // but doesn't seem to do so when manually deleting the user-level keys it
968 base::string16
alternate_active_setup_path(active_setup_path
);
969 alternate_active_setup_path
.insert(arraysize("Software\\") - 1,
972 // These two privileges are required by RegLoadKey() and RegUnloadKey() below.
973 ScopedTokenPrivilege
se_restore_name_privilege(SE_RESTORE_NAME
);
974 ScopedTokenPrivilege
se_backup_name_privilege(SE_BACKUP_NAME
);
975 if (!se_restore_name_privilege
.is_enabled() ||
976 !se_backup_name_privilege
.is_enabled()) {
977 // This is not a critical failure as those privileges aren't required to
978 // clean hives that are already loaded, but attempts to LoadRegKey() below
980 LOG(WARNING
) << "Failed to enable privileges required to load registry "
984 for (base::win::RegistryKeyIterator
it(HKEY_LOCAL_MACHINE
, kProfileList
);
986 const wchar_t* profile_sid
= it
.Name();
988 // First check if this user's registry hive needs to be loaded in
990 base::win::RegKey
user_reg_root_probe(
991 HKEY_USERS
, profile_sid
, KEY_READ
);
992 bool loaded_hive
= false;
993 if (!user_reg_root_probe
.Valid()) {
994 VLOG(1) << "Attempting to load registry hive for " << profile_sid
;
996 base::string16
reg_profile_info_path(kProfileList
);
997 reg_profile_info_path
.append(profile_sid
);
998 base::win::RegKey
reg_profile_info_key(
999 HKEY_LOCAL_MACHINE
, reg_profile_info_path
.c_str(), KEY_READ
);
1001 base::string16 profile_path
;
1002 LONG result
= reg_profile_info_key
.ReadValue(L
"ProfileImagePath",
1004 if (result
!= ERROR_SUCCESS
) {
1005 LOG(ERROR
) << "Error reading ProfileImagePath: " << result
;
1008 base::FilePath
registry_hive_file(profile_path
);
1009 registry_hive_file
= registry_hive_file
.AppendASCII("NTUSER.DAT");
1011 result
= RegLoadKey(HKEY_USERS
, profile_sid
,
1012 registry_hive_file
.value().c_str());
1013 if (result
!= ERROR_SUCCESS
) {
1014 LOG(ERROR
) << "Error loading registry hive: " << result
;
1018 VLOG(1) << "Loaded registry hive for " << profile_sid
;
1022 base::win::RegKey
user_reg_root(
1023 HKEY_USERS
, profile_sid
, KEY_ALL_ACCESS
);
1025 LONG result
= user_reg_root
.DeleteKey(active_setup_path
.c_str());
1026 if (result
!= ERROR_SUCCESS
) {
1027 result
= user_reg_root
.DeleteKey(alternate_active_setup_path
.c_str());
1028 if (result
!= ERROR_SUCCESS
&& result
!= ERROR_FILE_NOT_FOUND
) {
1029 LOG(ERROR
) << "Failed to delete key at " << active_setup_path
1030 << " and at " << alternate_active_setup_path
1031 << ", result: " << result
;
1036 user_reg_root
.Close();
1037 if (RegUnLoadKey(HKEY_USERS
, profile_sid
) == ERROR_SUCCESS
)
1038 VLOG(1) << "Unloaded registry hive for " << profile_sid
;
1040 LOG(ERROR
) << "Error unloading registry hive for " << profile_sid
;
1045 InstallStatus
UninstallProduct(const InstallationState
& original_state
,
1046 const InstallerState
& installer_state
,
1047 const base::FilePath
& setup_path
,
1048 const Product
& product
,
1050 bool force_uninstall
,
1051 const CommandLine
& cmd_line
) {
1052 InstallStatus status
= installer::UNINSTALL_CONFIRMED
;
1053 BrowserDistribution
* browser_dist
= product
.distribution();
1054 const base::string16
chrome_exe(
1055 installer_state
.target_path().Append(installer::kChromeExe
).value());
1057 bool is_chrome
= product
.is_chrome();
1059 VLOG(1) << "UninstallProduct: " << browser_dist
->GetDisplayName();
1061 if (force_uninstall
) {
1062 // Since --force-uninstall command line option is used, we are going to
1063 // do silent uninstall. Try to close all running Chrome instances.
1064 // NOTE: We don't do this for Chrome Frame.
1066 CloseAllChromeProcesses();
1067 } else if (is_chrome
) {
1068 // no --force-uninstall so lets show some UI dialog boxes.
1069 status
= IsChromeActiveOrUserCancelled(installer_state
, product
);
1070 if (status
!= installer::UNINSTALL_CONFIRMED
&&
1071 status
!= installer::UNINSTALL_DELETE_PROFILE
)
1074 const base::string16
suffix(
1075 ShellUtil::GetCurrentInstallationSuffix(browser_dist
, chrome_exe
));
1077 // Check if we need admin rights to cleanup HKLM (the conditions for
1078 // requiring a cleanup are the same as the conditions to do the actual
1079 // cleanup where DeleteChromeRegistrationKeys() is invoked for
1080 // HKEY_LOCAL_MACHINE below). If we do, try to launch another uninstaller
1081 // (silent) in elevated mode to do HKLM cleanup.
1082 // And continue uninstalling in the current process also to do HKCU cleanup.
1084 ShellUtil::QuickIsChromeRegisteredInHKLM(
1085 browser_dist
, chrome_exe
, suffix
) &&
1086 !::IsUserAnAdmin() &&
1087 base::win::GetVersion() >= base::win::VERSION_VISTA
&&
1088 !cmd_line
.HasSwitch(installer::switches::kRunAsAdmin
)) {
1089 CommandLine
new_cmd(CommandLine::NO_PROGRAM
);
1090 new_cmd
.AppendArguments(cmd_line
, true);
1091 // Append --run-as-admin flag to let the new instance of setup.exe know
1092 // that we already tried to launch ourselves as admin.
1093 new_cmd
.AppendSwitch(installer::switches::kRunAsAdmin
);
1094 // Append --remove-chrome-registration to remove registry keys only.
1095 new_cmd
.AppendSwitch(installer::switches::kRemoveChromeRegistration
);
1096 if (!suffix
.empty()) {
1097 new_cmd
.AppendSwitchNative(
1098 installer::switches::kRegisterChromeBrowserSuffix
, suffix
);
1100 DWORD exit_code
= installer::UNKNOWN_STATUS
;
1101 InstallUtil::ExecuteExeAsAdmin(new_cmd
, &exit_code
);
1106 // Chrome is not in use so lets uninstall Chrome by deleting various files
1107 // and registry entries. Here we will just make best effort and keep going
1108 // in case of errors.
1109 ClearRlzProductState();
1110 // Delete the key that delegate_execute might make.
1111 if (base::win::GetVersion() >= base::win::VERSION_WIN8
) {
1112 InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER
,
1113 chrome::kMetroRegistryPath
);
1116 auto_launch_util::DisableAllAutoStartFeatures(
1117 base::ASCIIToUTF16(chrome::kInitialProfile
));
1119 // If user-level chrome is self-destructing as a result of encountering a
1120 // system-level chrome, retarget owned non-default shortcuts (app shortcuts,
1121 // profile shortcuts, etc.) to the system-level chrome.
1122 if (cmd_line
.HasSwitch(installer::switches::kSelfDestruct
) &&
1123 !installer_state
.system_install()) {
1124 const base::FilePath
system_chrome_path(
1125 GetChromeInstallPath(true, browser_dist
).
1126 Append(installer::kChromeExe
));
1127 VLOG(1) << "Retargeting user-generated Chrome shortcuts.";
1128 if (base::PathExists(system_chrome_path
)) {
1129 RetargetUserShortcutsWithArgs(installer_state
, product
,
1130 base::FilePath(chrome_exe
),
1131 system_chrome_path
);
1133 LOG(ERROR
) << "Retarget failed: system-level Chrome not found.";
1137 DeleteShortcuts(installer_state
, product
, base::FilePath(chrome_exe
));
1140 // Delete the registry keys (Uninstall key and Version key).
1141 HKEY reg_root
= installer_state
.root_key();
1143 // Note that we must retrieve the distribution-specific data before deleting
1144 // product.GetVersionKey().
1145 base::string16
distribution_data(browser_dist
->GetDistributionData(reg_root
));
1147 // Remove Control Panel uninstall link.
1148 if (product
.ShouldCreateUninstallEntry()) {
1149 InstallUtil::DeleteRegistryKey(reg_root
,
1150 browser_dist
->GetUninstallRegPath());
1153 // Remove Omaha product key.
1154 InstallUtil::DeleteRegistryKey(reg_root
, browser_dist
->GetVersionKey());
1156 // Also try to delete the MSI value in the ClientState key (it might not be
1157 // there). This is due to a Google Update behaviour where an uninstall and a
1158 // rapid reinstall might result in stale values from the old ClientState key
1159 // being picked up on reinstall.
1160 product
.SetMsiMarker(installer_state
.system_install(), false);
1162 InstallStatus ret
= installer::UNKNOWN_STATUS
;
1165 const base::string16
suffix(
1166 ShellUtil::GetCurrentInstallationSuffix(browser_dist
, chrome_exe
));
1168 // Remove all Chrome registration keys.
1169 // Registration data is put in HKCU for both system level and user level
1171 DeleteChromeRegistrationKeys(installer_state
, browser_dist
,
1172 HKEY_CURRENT_USER
, suffix
, &ret
);
1174 // If the user's Chrome is registered with a suffix: it is possible that old
1175 // unsuffixed registrations were left in HKCU (e.g. if this install was
1176 // previously installed with no suffix in HKCU (old suffix rules if the user
1177 // is not an admin (or declined UAC at first run)) and later had to be
1178 // suffixed when fully registered in HKLM (e.g. when later making Chrome
1179 // default through the UI)).
1180 // Remove remaining HKCU entries with no suffix if any.
1181 if (!suffix
.empty()) {
1182 DeleteChromeRegistrationKeys(installer_state
, browser_dist
,
1183 HKEY_CURRENT_USER
, base::string16(), &ret
);
1185 // For similar reasons it is possible in very few installs (from
1186 // 21.0.1180.0 and fixed shortly after) to be installed with the new-style
1187 // suffix, but have some old-style suffix registrations left behind.
1188 base::string16 old_style_suffix
;
1189 if (ShellUtil::GetOldUserSpecificRegistrySuffix(&old_style_suffix
) &&
1190 suffix
!= old_style_suffix
) {
1191 DeleteChromeRegistrationKeys(installer_state
, browser_dist
,
1192 HKEY_CURRENT_USER
, old_style_suffix
, &ret
);
1196 // Chrome is registered in HKLM for all system-level installs and for
1197 // user-level installs for which Chrome has been made the default browser.
1198 // Always remove the HKLM registration for system-level installs. For
1199 // user-level installs, only remove it if both: 1) this uninstall isn't a
1200 // self destruct following the installation of a system-level Chrome
1201 // (because the system-level Chrome owns the HKLM registration now), and 2)
1202 // this user has made Chrome their default browser (i.e. has shell
1203 // integration entries registered with |suffix| (note: |suffix| will be the
1204 // empty string if required as it is obtained by
1205 // GetCurrentInstallationSuffix() above)).
1206 // TODO(gab): This can still leave parts of a suffixed install behind. To be
1207 // able to remove them we would need to be able to remove only suffixed
1208 // entries (as it is now some of the registry entries (e.g. App Paths) are
1209 // unsuffixed; thus removing suffixed installs is prohibited in HKLM if
1210 // !|remove_all| for now).
1211 if (installer_state
.system_install() ||
1213 ShellUtil::QuickIsChromeRegisteredInHKLM(
1214 browser_dist
, chrome_exe
, suffix
))) {
1215 DeleteChromeRegistrationKeys(installer_state
, browser_dist
,
1216 HKEY_LOCAL_MACHINE
, suffix
, &ret
);
1219 ProcessDelegateExecuteWorkItems(installer_state
, product
);
1221 ProcessOnOsUpgradeWorkItems(installer_state
, product
);
1223 UninstallActiveSetupEntries(installer_state
, product
);
1225 // Notify the shell that associations have changed since Chrome was likely
1227 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
, NULL
, NULL
);
1229 // TODO(huangs): Implement actual migration code and remove the hack below.
1230 // Remove the "shadow" App Launcher registry keys.
1231 if (installer_state
.is_multi_install()) {
1232 // If we're not uninstalling the legacy App Launcher, and if it was
1233 // not installed in the first place, then delete the "shadow" keys.
1234 chrome_launcher_support::InstallationState level_to_check
=
1235 installer_state
.system_install() ?
1236 chrome_launcher_support::INSTALLED_AT_SYSTEM_LEVEL
:
1237 chrome_launcher_support::INSTALLED_AT_USER_LEVEL
;
1238 bool has_legacy_app_launcher
= level_to_check
==
1239 chrome_launcher_support::GetAppLauncherInstallationState();
1240 if (!has_legacy_app_launcher
) {
1241 BrowserDistribution
* shadow_app_launcher_dist
=
1242 BrowserDistribution::GetSpecificDistribution(
1243 BrowserDistribution::CHROME_APP_HOST
);
1244 InstallUtil::DeleteRegistryKey(reg_root
,
1245 shadow_app_launcher_dist
->GetVersionKey());
1250 if (installer_state
.is_multi_install())
1251 ProcessGoogleUpdateItems(original_state
, installer_state
, product
);
1253 // Get the state of the installed product (if any)
1254 const ProductState
* product_state
=
1255 original_state
.GetProductState(installer_state
.system_install(),
1256 browser_dist
->GetType());
1258 // Delete shared registry keys as well (these require admin rights) if
1259 // remove_all option is specified.
1261 if (!InstallUtil::IsChromeSxSProcess() && is_chrome
) {
1262 // Delete media player registry key that exists only in HKLM.
1263 // We don't delete this key in SxS uninstall or Chrome Frame uninstall
1264 // as we never set the key for those products.
1265 base::string16
reg_path(installer::kMediaPlayerRegPath
);
1266 reg_path
.push_back(base::FilePath::kSeparators
[0]);
1267 reg_path
.append(installer::kChromeExe
);
1268 InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE
, reg_path
);
1271 // Unregister any dll servers that we may have registered for this
1273 if (product_state
!= NULL
) {
1274 std::vector
<base::FilePath
> com_dll_list
;
1275 product
.AddComDllList(&com_dll_list
);
1276 base::FilePath dll_folder
= installer_state
.target_path().AppendASCII(
1277 product_state
->version().GetString());
1279 scoped_ptr
<WorkItemList
> unreg_work_item_list(
1280 WorkItem::CreateWorkItemList());
1282 AddRegisterComDllWorkItems(dll_folder
,
1284 installer_state
.system_install(),
1285 false, // Unregister
1287 unreg_work_item_list
.get());
1288 unreg_work_item_list
->Do();
1291 if (product
.is_chrome_frame())
1292 ProcessIELowRightsPolicyWorkItems(installer_state
);
1295 // Close any Chrome Frame helper processes that may be running.
1296 if (product
.is_chrome_frame()) {
1297 VLOG(1) << "Closing the Chrome Frame helper process";
1298 CloseChromeFrameHelperProcess();
1301 if (product_state
== NULL
)
1302 return installer::UNINSTALL_SUCCESSFUL
;
1304 // Finally delete all the files from Chrome folder after moving setup.exe
1305 // and the user's Local State to a temp location.
1306 bool delete_profile
= ShouldDeleteProfile(installer_state
, cmd_line
, status
,
1308 ret
= installer::UNINSTALL_SUCCESSFUL
;
1310 // When deleting files, we must make sure that we're either a "single"
1311 // (aka non-multi) installation or we are the Chrome Binaries.
1313 std::vector
<base::FilePath
> local_state_folders
;
1314 GetLocalStateFolders(product
, &local_state_folders
);
1315 base::FilePath
backup_state_file(BackupLocalStateFile(local_state_folders
));
1317 if (product
.is_chrome_app_host()) {
1318 DeleteAppHostFilesAndFolders(installer_state
, product_state
->version());
1319 } else if (!installer_state
.is_multi_install() ||
1320 product
.is_chrome_binaries()) {
1321 base::FilePath
setup_exe(base::MakeAbsoluteFilePath(cmd_line
.GetProgram()));
1322 DeleteResult delete_result
= DeleteChromeFilesAndFolders(
1323 installer_state
, setup_exe
);
1324 if (delete_result
== DELETE_FAILED
) {
1325 ret
= installer::UNINSTALL_FAILED
;
1326 } else if (delete_result
== DELETE_REQUIRES_REBOOT
) {
1327 ret
= installer::UNINSTALL_REQUIRES_REBOOT
;
1332 DeleteLocalState(local_state_folders
, product
.is_chrome_frame());
1334 if (!force_uninstall
) {
1335 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations.";
1336 browser_dist
->DoPostUninstallOperations(product_state
->version(),
1337 backup_state_file
, distribution_data
);
1340 // Try and delete the preserved local state once the post-install
1341 // operations are complete.
1342 if (!backup_state_file
.empty())
1343 base::DeleteFile(backup_state_file
, false);
1348 void CleanUpInstallationDirectoryAfterUninstall(
1349 const InstallationState
& original_state
,
1350 const InstallerState
& installer_state
,
1351 const CommandLine
& cmd_line
,
1352 InstallStatus
* uninstall_status
) {
1353 if (*uninstall_status
!= UNINSTALL_SUCCESSFUL
&&
1354 *uninstall_status
!= UNINSTALL_REQUIRES_REBOOT
) {
1357 const base::FilePath
target_path(installer_state
.target_path());
1358 if (target_path
.empty()) {
1359 LOG(ERROR
) << "No installation destination path.";
1360 *uninstall_status
= UNINSTALL_FAILED
;
1363 base::FilePath
setup_exe(base::MakeAbsoluteFilePath(cmd_line
.GetProgram()));
1364 if (!target_path
.IsParent(setup_exe
)) {
1365 VLOG(1) << "setup.exe is not in target path. Skipping installer cleanup.";
1368 base::FilePath
install_directory(setup_exe
.DirName());
1370 bool remove_setup
= true;
1371 bool remove_archive
= true;
1372 CheckShouldRemoveSetupAndArchive(original_state
, installer_state
,
1373 &remove_setup
, &remove_archive
);
1374 if (!remove_archive
)
1378 // In order to be able to remove the folder in which we're running, we
1379 // need to move setup.exe out of the install folder.
1380 // TODO(tommi): What if the temp folder is on a different volume?
1381 MoveSetupOutOfInstallFolder(installer_state
, setup_exe
);
1384 // Remove files from "...\<product>\Application\<version>\Installer"
1385 if (!RemoveInstallerFiles(install_directory
, remove_setup
)) {
1386 *uninstall_status
= UNINSTALL_FAILED
;
1393 // Try to remove the empty directory hierarchy.
1395 // Delete "...\<product>\Application\<version>\Installer"
1396 if (DeleteEmptyDir(install_directory
) != DELETE_SUCCEEDED
) {
1397 *uninstall_status
= UNINSTALL_FAILED
;
1401 // Delete "...\<product>\Application\<version>"
1402 DeleteResult delete_result
= DeleteEmptyDir(install_directory
.DirName());
1403 if (delete_result
== DELETE_FAILED
||
1404 (delete_result
== DELETE_NOT_EMPTY
&&
1405 *uninstall_status
!= UNINSTALL_REQUIRES_REBOOT
)) {
1406 *uninstall_status
= UNINSTALL_FAILED
;
1410 if (*uninstall_status
== UNINSTALL_REQUIRES_REBOOT
) {
1411 // Delete the Application directory at reboot if empty.
1412 ScheduleFileSystemEntityForDeletion(target_path
);
1414 // If we need a reboot to continue, schedule the parent directories for
1415 // deletion unconditionally. If they are not empty, the session manager
1416 // will not delete them on reboot.
1417 ScheduleParentAndGrandparentForDeletion(target_path
);
1418 } else if (DeleteChromeDirectoriesIfEmpty(target_path
) == DELETE_FAILED
) {
1419 *uninstall_status
= UNINSTALL_FAILED
;
1423 } // namespace installer