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/base_paths.h"
14 #include "base/files/file_enumerator.h"
15 #include "base/files/file_util.h"
16 #include "base/path_service.h"
17 #include "base/process/kill.h"
18 #include "base/strings/string16.h"
19 #include "base/strings/string_number_conversions.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "base/win/registry.h"
23 #include "base/win/scoped_handle.h"
24 #include "base/win/shortcut.h"
25 #include "base/win/windows_version.h"
26 #include "chrome/common/chrome_constants.h"
27 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/chrome_result_codes.h"
29 #include "chrome/installer/setup/app_launcher_installer.h"
30 #include "chrome/installer/setup/install.h"
31 #include "chrome/installer/setup/install_worker.h"
32 #include "chrome/installer/setup/setup_constants.h"
33 #include "chrome/installer/setup/setup_util.h"
34 #include "chrome/installer/util/auto_launch_util.h"
35 #include "chrome/installer/util/browser_distribution.h"
36 #include "chrome/installer/util/channel_info.h"
37 #include "chrome/installer/util/delete_after_reboot_helper.h"
38 #include "chrome/installer/util/firewall_manager_win.h"
39 #include "chrome/installer/util/google_update_constants.h"
40 #include "chrome/installer/util/google_update_settings.h"
41 #include "chrome/installer/util/helper.h"
42 #include "chrome/installer/util/install_util.h"
43 #include "chrome/installer/util/installation_state.h"
44 #include "chrome/installer/util/installer_state.h"
45 #include "chrome/installer/util/logging_installer.h"
46 #include "chrome/installer/util/self_cleaning_temp_dir.h"
47 #include "chrome/installer/util/shell_util.h"
48 #include "chrome/installer/util/util_constants.h"
49 #include "chrome/installer/util/work_item.h"
50 #include "chrome_elf/chrome_elf_constants.h"
51 #include "content/public/common/result_codes.h"
52 #include "rlz/lib/rlz_lib.h"
54 using base::win::RegKey
;
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 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 InstallationState
& original_state
,
83 const InstallerState
& installer_state
,
84 const 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 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
,
100 other_dist
->GetStateKey(),
102 google_update::kRegApField
,
103 channel_info
.value(),
107 product_state
!= NULL
&& product_state
->is_multi_install())
108 << "Channel value for "
109 << BrowserDistribution::GetSpecificDistribution(
110 dist_type
)->GetDisplayName()
111 << " is somehow already set to the desired new value of "
112 << channel_info
.value();
117 // Makes appropriate changes to the Google Update "ap" value in the registry.
118 // Specifically, removes the flags associated with this product ("-chrome" or
119 // "-chromeframe") from the "ap" values for all other installed products and for
120 // the multi-installer package.
121 void ProcessGoogleUpdateItems(const InstallationState
& original_state
,
122 const InstallerState
& installer_state
,
123 const Product
& product
) {
124 DCHECK(installer_state
.is_multi_install());
125 const bool system_level
= installer_state
.system_install();
126 BrowserDistribution
* distribution
= product
.distribution();
127 const ProductState
* product_state
=
128 original_state
.GetProductState(system_level
, distribution
->GetType());
129 DCHECK(product_state
!= NULL
);
130 ChannelInfo channel_info
;
132 // Remove product's flags from the channel value.
133 channel_info
.set_value(product_state
->channel().value());
134 const bool modified
= product
.SetChannelFlags(false, &channel_info
);
136 // Apply the new channel value to all other products and to the multi package.
138 scoped_ptr
<WorkItemList
>
139 update_list(WorkItem::CreateNoRollbackWorkItemList());
140 std::vector
<BrowserDistribution::Type
> dist_types
;
141 for (size_t i
= 0; i
< BrowserDistribution::NUM_TYPES
; ++i
) {
142 BrowserDistribution::Type other_dist_type
=
143 static_cast<BrowserDistribution::Type
>(i
);
144 if (distribution
->GetType() != other_dist_type
)
145 dist_types
.push_back(other_dist_type
);
147 AddChannelValueUpdateWorkItems(original_state
, installer_state
,
148 channel_info
, dist_types
,
150 bool success
= update_list
->Do();
151 LOG_IF(ERROR
, !success
) << "Failed updating channel values.";
155 void ProcessOnOsUpgradeWorkItems(const InstallerState
& installer_state
,
156 const Product
& product
) {
157 scoped_ptr
<WorkItemList
> work_item_list(
158 WorkItem::CreateNoRollbackWorkItemList());
159 AddOsUpgradeWorkItems(installer_state
, base::FilePath(), Version(), product
,
160 work_item_list
.get());
161 if (!work_item_list
->Do())
162 LOG(ERROR
) << "Failed to remove on-os-upgrade command.";
165 void ProcessIELowRightsPolicyWorkItems(const InstallerState
& installer_state
) {
166 scoped_ptr
<WorkItemList
> work_items(WorkItem::CreateNoRollbackWorkItemList());
167 AddDeleteOldIELowRightsPolicyWorkItems(installer_state
, work_items
.get());
169 RefreshElevationPolicy();
172 void ClearRlzProductState() {
173 const rlz_lib::AccessPoint points
[] = {rlz_lib::CHROME_OMNIBOX
,
174 rlz_lib::CHROME_HOME_PAGE
,
175 rlz_lib::CHROME_APP_LIST
,
176 rlz_lib::NO_ACCESS_POINT
};
178 rlz_lib::ClearProductState(rlz_lib::CHROME
, points
);
180 // If chrome has been reactivated, clear all events for this brand as well.
181 base::string16 reactivation_brand_wide
;
182 if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand_wide
)) {
183 std::string
reactivation_brand(base::UTF16ToASCII(reactivation_brand_wide
));
184 rlz_lib::SupplementaryBranding
branding(reactivation_brand
.c_str());
185 rlz_lib::ClearProductState(rlz_lib::CHROME
, points
);
189 // Returns whether setup.exe should be removed based on the original and
191 // * non-multi product being uninstalled: remove setup.exe
192 // * any multi product left: keep setup.exe
193 bool CheckShouldRemoveSetup(const InstallationState
& original_state
,
194 const InstallerState
& installer_state
) {
195 // If any multi-install product is left we must leave the installer and
197 if (!installer_state
.is_multi_install()) {
198 VLOG(1) << "Removing all installer files for a non-multi installation.";
200 // Loop through all known products...
201 for (size_t i
= 0; i
< BrowserDistribution::NUM_TYPES
; ++i
) {
202 BrowserDistribution::Type dist_type
=
203 static_cast<BrowserDistribution::Type
>(i
);
204 const ProductState
* product_state
= original_state
.GetProductState(
205 installer_state
.system_install(), dist_type
);
206 // If the product is installed, in multi mode, and is not part of the
207 // active uninstallation...
208 if (product_state
&& product_state
->is_multi_install() &&
209 !installer_state
.FindProduct(dist_type
)) {
210 // setup.exe will not be removed as there is a remaining multi-install
212 VLOG(1) << "Keeping all installer files due to a remaining "
213 << "multi-install product.";
217 VLOG(1) << "Removing all installer files.";
222 // Removes all files from the installer directory, leaving setup.exe iff
223 // |remove_setup| is false.
224 // Returns false in case of an error.
225 bool RemoveInstallerFiles(const base::FilePath
& installer_directory
,
227 base::FileEnumerator
file_enumerator(
230 base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
);
233 base::FilePath
setup_exe_base_name(installer::kSetupExe
);
235 for (base::FilePath to_delete
= file_enumerator
.Next(); !to_delete
.empty();
236 to_delete
= file_enumerator
.Next()) {
237 if (!remove_setup
&& to_delete
.BaseName() == setup_exe_base_name
)
240 VLOG(1) << "Deleting installer path " << to_delete
.value();
241 if (!base::DeleteFile(to_delete
, true)) {
242 LOG(ERROR
) << "Failed to delete path: " << to_delete
.value();
250 // Kills all Chrome processes, immediately.
251 void CloseAllChromeProcesses() {
252 base::CleanupProcesses(installer::kChromeExe
, base::TimeDelta(),
253 content::RESULT_CODE_HUNG
, NULL
);
254 base::CleanupProcesses(installer::kNaClExe
, base::TimeDelta(),
255 content::RESULT_CODE_HUNG
, NULL
);
258 // Attempts to close the Chrome Frame helper process by sending WM_CLOSE
259 // messages to its window, or just killing it if that doesn't work.
260 void CloseChromeFrameHelperProcess() {
261 HWND window
= FindWindow(installer::kChromeFrameHelperWndClass
, NULL
);
262 if (!::IsWindow(window
))
265 const DWORD kWaitMs
= 3000;
268 ::GetWindowThreadProcessId(window
, &pid
);
270 base::win::ScopedHandle
process(::OpenProcess(SYNCHRONIZE
, FALSE
, pid
));
271 PLOG_IF(INFO
, !process
.IsValid()) << "Failed to open process: " << pid
;
274 if (SendMessageTimeout(window
, WM_CLOSE
, 0, 0, SMTO_BLOCK
, kWaitMs
, NULL
) &&
276 VLOG(1) << "Waiting for " << installer::kChromeFrameHelperExe
;
277 DWORD wait
= ::WaitForSingleObject(process
.Get(), kWaitMs
);
278 if (wait
!= WAIT_OBJECT_0
) {
279 LOG(WARNING
) << "Wait for " << installer::kChromeFrameHelperExe
280 << " to exit failed or timed out.";
283 VLOG(1) << installer::kChromeFrameHelperExe
<< " exited normally.";
288 VLOG(1) << installer::kChromeFrameHelperExe
<< " hung. Killing.";
289 base::CleanupProcesses(installer::kChromeFrameHelperExe
, base::TimeDelta(),
290 content::RESULT_CODE_HUNG
, NULL
);
294 // Updates shortcuts to |old_target_exe| that have non-empty args, making them
295 // target |new_target_exe| instead. The non-empty args requirement is a
296 // heuristic to determine whether a shortcut is "user-generated". This routine
297 // can only be called for user-level installs.
298 void RetargetUserShortcutsWithArgs(const InstallerState
& installer_state
,
299 const Product
& product
,
300 const base::FilePath
& old_target_exe
,
301 const base::FilePath
& new_target_exe
) {
302 if (installer_state
.system_install()) {
306 BrowserDistribution
* dist
= product
.distribution();
307 ShellUtil::ShellChange install_level
= ShellUtil::CURRENT_USER
;
309 // Retarget all shortcuts that point to |old_target_exe| from all
310 // ShellUtil::ShortcutLocations.
311 VLOG(1) << "Retargeting shortcuts.";
312 for (int location
= ShellUtil::SHORTCUT_LOCATION_FIRST
;
313 location
< ShellUtil::NUM_SHORTCUT_LOCATIONS
; ++location
) {
314 if (!ShellUtil::RetargetShortcutsWithArgs(
315 static_cast<ShellUtil::ShortcutLocation
>(location
), dist
,
316 install_level
, old_target_exe
, new_target_exe
)) {
317 LOG(WARNING
) << "Failed to retarget shortcuts in ShortcutLocation: "
323 // Deletes shortcuts at |install_level| from Start menu, Desktop,
324 // Quick Launch, taskbar, and secondary tiles on the Start Screen (Win8+).
325 // Only shortcuts pointing to |target_exe| will be removed.
326 void DeleteShortcuts(const InstallerState
& installer_state
,
327 const Product
& product
,
328 const base::FilePath
& target_exe
) {
329 BrowserDistribution
* dist
= product
.distribution();
331 // The per-user shortcut for this user, if present on a system-level install,
332 // has already been deleted in chrome_browser_main_win.cc::DoUninstallTasks().
333 ShellUtil::ShellChange install_level
= installer_state
.system_install() ?
334 ShellUtil::SYSTEM_LEVEL
: ShellUtil::CURRENT_USER
;
336 // Delete and unpin all shortcuts that point to |target_exe| from all
337 // ShellUtil::ShortcutLocations.
338 VLOG(1) << "Deleting shortcuts.";
339 for (int location
= ShellUtil::SHORTCUT_LOCATION_FIRST
;
340 location
< ShellUtil::NUM_SHORTCUT_LOCATIONS
; ++location
) {
341 if (!ShellUtil::RemoveShortcuts(
342 static_cast<ShellUtil::ShortcutLocation
>(location
), dist
,
343 install_level
, target_exe
)) {
344 LOG(WARNING
) << "Failed to delete shortcuts in ShortcutLocation: "
350 bool ScheduleParentAndGrandparentForDeletion(const base::FilePath
& path
) {
351 base::FilePath parent_dir
= path
.DirName();
352 bool ret
= ScheduleFileSystemEntityForDeletion(parent_dir
);
354 LOG(ERROR
) << "Failed to schedule parent dir for deletion: "
355 << parent_dir
.value();
357 base::FilePath
grandparent_dir(parent_dir
.DirName());
358 ret
= ScheduleFileSystemEntityForDeletion(grandparent_dir
);
360 LOG(ERROR
) << "Failed to schedule grandparent dir for deletion: "
361 << grandparent_dir
.value();
367 // Deletes the given directory if it is empty. Returns DELETE_SUCCEEDED if the
368 // directory is deleted, DELETE_NOT_EMPTY if it is not empty, and DELETE_FAILED
370 DeleteResult
DeleteEmptyDir(const base::FilePath
& path
) {
371 if (!base::IsDirectoryEmpty(path
))
372 return DELETE_NOT_EMPTY
;
374 if (base::DeleteFile(path
, true))
375 return DELETE_SUCCEEDED
;
377 LOG(ERROR
) << "Failed to delete folder: " << path
.value();
378 return DELETE_FAILED
;
381 // Get the user data directory, which is *not* DIR_USER_DATA for Chrome Frame.
382 // TODO(grt): Remove Chrome Frame uninstall support when usage is low enough.
383 base::FilePath
GetUserDataDir(const Product
& product
) {
385 bool is_chrome_frame
= product
.is_chrome_frame();
386 int key
= is_chrome_frame
? base::DIR_LOCAL_APP_DATA
: chrome::DIR_USER_DATA
;
387 if (!PathService::Get(key
, &path
))
388 return base::FilePath();
389 if (is_chrome_frame
) {
390 path
= path
.Append(product
.distribution()->GetInstallSubDir());
391 path
= path
.Append(chrome::kUserDataDirname
);
396 // Creates a copy of the local state file and returns a path to the copy.
397 base::FilePath
BackupLocalStateFile(const base::FilePath
& user_data_dir
) {
398 base::FilePath backup
;
399 base::FilePath
state_file(user_data_dir
.Append(chrome::kLocalStateFilename
));
400 if (!base::CreateTemporaryFile(&backup
))
401 LOG(ERROR
) << "Failed to create temporary file for Local State.";
403 base::CopyFile(state_file
, backup
);
407 // Deletes a given user data directory as well as the containing product
408 // directories if they are empty (e.g., "Google\Chrome").
409 DeleteResult
DeleteUserDataDir(const base::FilePath
& user_data_dir
,
410 bool schedule_on_failure
) {
411 if (user_data_dir
.empty())
412 return DELETE_SUCCEEDED
;
414 DeleteResult result
= DELETE_SUCCEEDED
;
415 VLOG(1) << "Deleting user profile " << user_data_dir
.value();
416 if (!base::DeleteFile(user_data_dir
, true)) {
417 LOG(ERROR
) << "Failed to delete user profile dir: "
418 << user_data_dir
.value();
419 if (schedule_on_failure
) {
420 ScheduleDirectoryForDeletion(user_data_dir
);
421 result
= DELETE_REQUIRES_REBOOT
;
423 result
= DELETE_FAILED
;
427 if (result
== DELETE_REQUIRES_REBOOT
) {
428 ScheduleParentAndGrandparentForDeletion(user_data_dir
);
430 const base::FilePath
product_dir1(user_data_dir
.DirName());
431 if (!product_dir1
.empty() &&
432 DeleteEmptyDir(product_dir1
) == DELETE_SUCCEEDED
) {
433 const base::FilePath
product_dir2(product_dir1
.DirName());
434 if (!product_dir2
.empty())
435 DeleteEmptyDir(product_dir2
);
442 // Moves setup to a temporary file, outside of the install folder. Also attempts
443 // to change the current directory to the TMP directory. On Windows, each
444 // process has a handle to its CWD. If setup.exe's CWD happens to be within the
445 // install directory, deletion will fail as a result of the open handle.
446 bool MoveSetupOutOfInstallFolder(const InstallerState
& installer_state
,
447 const base::FilePath
& setup_exe
) {
448 // The list of files which setup.exe depends on at runtime. Typically this is
449 // solely setup.exe itself, but in component builds this also includes the
450 // DLLs installed by setup.exe.
451 std::vector
<base::FilePath
> setup_files
;
452 setup_files
.push_back(setup_exe
);
453 #if defined(COMPONENT_BUILD)
454 base::FileEnumerator
file_enumerator(
455 setup_exe
.DirName(), false, base::FileEnumerator::FILES
, L
"*.dll");
456 for (base::FilePath setup_dll
= file_enumerator
.Next(); !setup_dll
.empty();
457 setup_dll
= file_enumerator
.Next()) {
458 setup_files
.push_back(setup_dll
);
460 #endif // defined(COMPONENT_BUILD)
462 base::FilePath tmp_dir
;
463 base::FilePath temp_file
;
464 if (!PathService::Get(base::DIR_TEMP
, &tmp_dir
)) {
469 // Change the current directory to the TMP directory. See method comment above
471 VLOG(1) << "Changing current directory to: " << tmp_dir
.value();
472 if (!base::SetCurrentDirectory(tmp_dir
))
473 PLOG(ERROR
) << "Failed to change the current directory.";
475 for (std::vector
<base::FilePath
>::const_iterator it
= setup_files
.begin();
476 it
!= setup_files
.end(); ++it
) {
477 const base::FilePath
& setup_file
= *it
;
478 if (!base::CreateTemporaryFileInDir(tmp_dir
, &temp_file
)) {
479 LOG(ERROR
) << "Failed to create temporary file for "
480 << setup_file
.BaseName().value();
484 VLOG(1) << "Attempting to move " << setup_file
.BaseName().value() << " to: "
485 << temp_file
.value();
486 if (!base::Move(setup_file
, temp_file
)) {
487 PLOG(ERROR
) << "Failed to move " << setup_file
.BaseName().value()
488 << " to " << temp_file
.value();
492 // We cannot delete the file right away, but try to delete it some other
493 // way. Either with the help of a different process or the system.
494 if (!base::DeleteFileAfterReboot(temp_file
)) {
495 const uint32 kDeleteAfterMs
= 10 * 1000;
496 installer::DeleteFileFromTempProcess(temp_file
, kDeleteAfterMs
);
502 DeleteResult
DeleteChromeFilesAndFolders(const InstallerState
& installer_state
,
503 const base::FilePath
& setup_exe
) {
504 const base::FilePath
& target_path
= installer_state
.target_path();
505 if (target_path
.empty()) {
506 LOG(ERROR
) << "DeleteChromeFilesAndFolders: no installation destination "
508 return DELETE_FAILED
; // Nothing else we can do to uninstall, so we return.
511 DeleteInstallTempDir(target_path
);
513 DeleteResult result
= DELETE_SUCCEEDED
;
515 base::FilePath installer_directory
;
516 if (target_path
.IsParent(setup_exe
))
517 installer_directory
= setup_exe
.DirName();
519 // Enumerate all the files in target_path recursively (breadth-first).
520 // We delete a file or folder unless it is a parent/child of the installer
521 // directory. For parents of the installer directory, we will later recurse
522 // and delete all the children (that are not also parents/children of the
523 // installer directory).
524 base::FileEnumerator
file_enumerator(target_path
, true,
525 base::FileEnumerator::FILES
| base::FileEnumerator::DIRECTORIES
);
526 for (base::FilePath to_delete
= file_enumerator
.Next(); !to_delete
.empty();
527 to_delete
= file_enumerator
.Next()) {
528 if (!installer_directory
.empty() &&
529 (to_delete
== installer_directory
||
530 installer_directory
.IsParent(to_delete
) ||
531 to_delete
.IsParent(installer_directory
))) {
535 VLOG(1) << "Deleting install path " << to_delete
.value();
536 if (!base::DeleteFile(to_delete
, true)) {
537 LOG(ERROR
) << "Failed to delete path (1st try): " << to_delete
.value();
538 if (installer_state
.FindProduct(BrowserDistribution::CHROME_FRAME
)) {
539 // We don't try killing Chrome processes for Chrome Frame builds since
540 // that is unlikely to help. Instead, schedule files for deletion and
541 // return a value that will trigger a reboot prompt.
542 base::FileEnumerator::FileInfo find_info
= file_enumerator
.GetInfo();
543 if (find_info
.IsDirectory())
544 ScheduleDirectoryForDeletion(to_delete
);
546 ScheduleFileSystemEntityForDeletion(to_delete
);
547 result
= DELETE_REQUIRES_REBOOT
;
549 // Try closing any running Chrome processes and deleting files once
551 CloseAllChromeProcesses();
552 if (!base::DeleteFile(to_delete
, true)) {
553 LOG(ERROR
) << "Failed to delete path (2nd try): "
554 << to_delete
.value();
555 result
= DELETE_FAILED
;
565 // This method checks if Chrome is currently running or if the user has
566 // cancelled the uninstall operation by clicking Cancel on the confirmation
567 // box that Chrome pops up.
568 InstallStatus
IsChromeActiveOrUserCancelled(
569 const InstallerState
& installer_state
,
570 const Product
& product
) {
571 int32 exit_code
= content::RESULT_CODE_NORMAL_EXIT
;
572 base::CommandLine
options(base::CommandLine::NO_PROGRAM
);
573 options
.AppendSwitch(installer::switches::kUninstall
);
575 // Here we want to save user from frustration (in case of Chrome crashes)
576 // and continue with the uninstallation as long as chrome.exe process exit
577 // code is NOT one of the following:
578 // - UNINSTALL_CHROME_ALIVE - chrome.exe is currently running
579 // - UNINSTALL_USER_CANCEL - User cancelled uninstallation
580 // - HUNG - chrome.exe was killed by HuntForZombieProcesses() (until we can
581 // give this method some brains and not kill chrome.exe launched
582 // by us, we will not uninstall if we get this return code).
583 VLOG(1) << "Launching Chrome to do uninstall tasks.";
584 if (product
.LaunchChromeAndWait(installer_state
.target_path(), options
,
586 VLOG(1) << "chrome.exe launched for uninstall confirmation returned: "
588 if ((exit_code
== chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE
) ||
589 (exit_code
== chrome::RESULT_CODE_UNINSTALL_USER_CANCEL
) ||
590 (exit_code
== content::RESULT_CODE_HUNG
))
591 return installer::UNINSTALL_CANCELLED
;
593 if (exit_code
== chrome::RESULT_CODE_UNINSTALL_DELETE_PROFILE
)
594 return installer::UNINSTALL_DELETE_PROFILE
;
596 PLOG(ERROR
) << "Failed to launch chrome.exe for uninstall confirmation.";
599 return installer::UNINSTALL_CONFIRMED
;
602 bool ShouldDeleteProfile(const InstallerState
& installer_state
,
603 const base::CommandLine
& cmd_line
,
604 InstallStatus status
,
605 const Product
& product
) {
606 bool should_delete
= false;
608 // Chrome Frame uninstallations always want to delete the profile (we have no
609 // UI to prompt otherwise and the profile stores no useful data anyway)
610 // unless they are managed by MSI. MSI uninstalls will explicitly include
611 // the --delete-profile flag to distinguish them from MSI upgrades.
612 if (product
.is_chrome_frame() && !installer_state
.is_msi()) {
613 should_delete
= true;
616 status
== installer::UNINSTALL_DELETE_PROFILE
||
617 cmd_line
.HasSwitch(installer::switches::kDeleteProfile
);
620 return should_delete
;
623 // Removes XP-era filetype registration making Chrome the default browser.
624 // MSDN (see http://msdn.microsoft.com/library/windows/desktop/cc144148.aspx)
625 // tells us not to do this, but certain applications break following
626 // uninstallation if we don't.
627 void RemoveFiletypeRegistration(const InstallerState
& installer_state
,
629 const base::string16
& browser_entry_suffix
) {
630 base::string16
classes_path(ShellUtil::kRegClasses
);
631 classes_path
.push_back(base::FilePath::kSeparators
[0]);
633 BrowserDistribution
* distribution
= BrowserDistribution::GetDistribution();
634 const base::string16
prog_id(
635 distribution
->GetBrowserProgIdPrefix() + browser_entry_suffix
);
637 // Delete each filetype association if it references this Chrome. Take care
638 // not to delete the association if it references a system-level install of
639 // Chrome (only a risk if the suffix is empty). Don't delete the whole key
640 // since other apps may have stored data there.
641 std::vector
<const wchar_t*> cleared_assocs
;
642 if (installer_state
.system_install() ||
643 !browser_entry_suffix
.empty() ||
644 !base::win::RegKey(HKEY_LOCAL_MACHINE
, (classes_path
+ prog_id
).c_str(),
645 KEY_QUERY_VALUE
).Valid()) {
646 InstallUtil::ValueEquals
prog_id_pred(prog_id
);
647 for (const wchar_t* const* filetype
=
648 &ShellUtil::kPotentialFileAssociations
[0]; *filetype
!= NULL
;
650 if (InstallUtil::DeleteRegistryValueIf(
651 root
, (classes_path
+ *filetype
).c_str(), WorkItem::kWow64Default
,
652 NULL
, prog_id_pred
) == InstallUtil::DELETED
) {
653 cleared_assocs
.push_back(*filetype
);
658 // For all filetype associations in HKLM that have just been removed, attempt
659 // to restore some reasonable value. We have no definitive way of knowing
660 // what handlers are the most appropriate, so we use a fixed mapping based on
661 // the default values for a fresh install of Windows.
662 if (root
== HKEY_LOCAL_MACHINE
) {
663 base::string16 assoc
;
664 base::win::RegKey key
;
666 for (size_t i
= 0; i
< cleared_assocs
.size(); ++i
) {
667 const wchar_t* replacement_prog_id
= NULL
;
668 assoc
.assign(cleared_assocs
[i
]);
670 // Inelegant, but simpler than a pure data-driven approach.
671 if (assoc
== L
".htm" || assoc
== L
".html")
672 replacement_prog_id
= L
"htmlfile";
673 else if (assoc
== L
".xht" || assoc
== L
".xhtml")
674 replacement_prog_id
= L
"xhtmlfile";
676 if (!replacement_prog_id
) {
677 LOG(WARNING
) << "No known replacement ProgID for " << assoc
679 } else if (key
.Open(HKEY_LOCAL_MACHINE
,
680 (classes_path
+ replacement_prog_id
).c_str(),
681 KEY_QUERY_VALUE
) == ERROR_SUCCESS
&&
682 (key
.Open(HKEY_LOCAL_MACHINE
, (classes_path
+ assoc
).c_str(),
683 KEY_SET_VALUE
) != ERROR_SUCCESS
||
684 key
.WriteValue(NULL
, replacement_prog_id
) != ERROR_SUCCESS
)) {
685 // The replacement ProgID is registered on the computer but the attempt
686 // to set it for the filetype failed.
687 LOG(ERROR
) << "Failed to restore system-level filetype association "
688 << assoc
<< " = " << replacement_prog_id
;
694 // Builds and executes a work item list to remove DelegateExecute verb handler
695 // work items for |product|. This will be a noop for products whose
696 // corresponding BrowserDistribution implementations do not publish a CLSID via
697 // GetCommandExecuteImplClsid.
698 bool ProcessDelegateExecuteWorkItems(const InstallerState
& installer_state
,
699 const Product
& product
) {
700 scoped_ptr
<WorkItemList
> item_list(WorkItem::CreateNoRollbackWorkItemList());
701 AddDelegateExecuteWorkItems(installer_state
, base::FilePath(), Version(),
702 product
, item_list
.get());
703 return item_list
->Do();
706 // Removes Active Setup entries from the registry. This cannot be done through
707 // a work items list as usual because of different paths based on conditionals,
708 // but otherwise respects the no rollback/best effort uninstall mentality.
709 // This will only apply for system-level installs of Chrome/Chromium and will be
710 // a no-op for all other types of installs.
711 void UninstallActiveSetupEntries(const InstallerState
& installer_state
,
712 const Product
& product
) {
713 VLOG(1) << "Uninstalling registry entries for Active Setup.";
714 BrowserDistribution
* distribution
= product
.distribution();
716 if (!product
.is_chrome() || !installer_state
.system_install()) {
717 const char* install_level
=
718 installer_state
.system_install() ? "system" : "user";
719 VLOG(1) << "No Active Setup processing to do for " << install_level
720 << "-level " << distribution
->GetDisplayName();
724 const base::string16
active_setup_path(
725 InstallUtil::GetActiveSetupPath(distribution
));
726 InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE
, active_setup_path
,
727 WorkItem::kWow64Default
);
729 // Windows leaves keys behind in HKCU\\Software\\(Wow6432Node\\)?Microsoft\\
730 // Active Setup\\Installed Components\\{guid}
731 // for every user that logged in since system-level Chrome was installed. This
732 // is a problem because Windows compares the value of the Version subkey in
733 // there with the value of the Version subkey in the matching HKLM entries
734 // before running Chrome's Active Setup so if Chrome was to be
735 // uninstalled/reinstalled by an admin, some users may not go through Active
736 // Setup again as desired.
738 // It is however very hard to delete those values as the registry hives for
739 // other users are not loaded by default under HKEY_USERS (unless a user is
740 // logged on or has a process impersonating him).
742 // Following our best effort uninstall practices, try to delete the value in
743 // all users hives. If a given user's hive is not loaded, try to load it to
744 // proceed with the deletion (failure to do so is ignored).
746 VLOG(1) << "Uninstall per-user Active Setup keys.";
748 static const wchar_t kProfileList
[] =
749 L
"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\";
751 // Windows automatically adds Wow6432Node when creating/deleting the HKLM key,
752 // but doesn't seem to do so when manually deleting the user-level keys it
754 base::string16
alternate_active_setup_path(active_setup_path
);
755 alternate_active_setup_path
.insert(arraysize("Software\\") - 1,
758 // These two privileges are required by RegLoadKey() and RegUnloadKey() below.
759 ScopedTokenPrivilege
se_restore_name_privilege(SE_RESTORE_NAME
);
760 ScopedTokenPrivilege
se_backup_name_privilege(SE_BACKUP_NAME
);
761 if (!se_restore_name_privilege
.is_enabled() ||
762 !se_backup_name_privilege
.is_enabled()) {
763 // This is not a critical failure as those privileges aren't required to
764 // clean hives that are already loaded, but attempts to LoadRegKey() below
766 LOG(WARNING
) << "Failed to enable privileges required to load registry "
770 for (base::win::RegistryKeyIterator
it(HKEY_LOCAL_MACHINE
, kProfileList
);
772 const wchar_t* profile_sid
= it
.Name();
774 VLOG(1) << "Uninstalling Active Setup key for " << profile_sid
;
776 // First check if this user's registry hive needs to be loaded in
778 base::win::RegKey
user_reg_root_probe(
779 HKEY_USERS
, profile_sid
, KEY_READ
);
780 bool loaded_hive
= false;
781 if (user_reg_root_probe
.Valid()) {
782 VLOG(1) << "Registry hive already loaded for " << profile_sid
;
784 VLOG(1) << "Attempting to load registry hive for " << profile_sid
;
786 base::string16
reg_profile_info_path(kProfileList
);
787 reg_profile_info_path
.append(profile_sid
);
788 base::win::RegKey
reg_profile_info_key(
789 HKEY_LOCAL_MACHINE
, reg_profile_info_path
.c_str(), KEY_READ
);
791 base::string16 profile_path
;
792 LONG result
= reg_profile_info_key
.ReadValue(L
"ProfileImagePath",
794 if (result
!= ERROR_SUCCESS
) {
795 LOG(ERROR
) << "Error reading ProfileImagePath: " << result
;
798 base::FilePath
registry_hive_file(profile_path
);
799 registry_hive_file
= registry_hive_file
.AppendASCII("NTUSER.DAT");
801 result
= RegLoadKey(HKEY_USERS
, profile_sid
,
802 registry_hive_file
.value().c_str());
803 if (result
!= ERROR_SUCCESS
) {
804 LOG(ERROR
) << "Error loading registry hive: " << result
;
808 VLOG(1) << "Loaded registry hive for " << profile_sid
;
812 base::win::RegKey
user_reg_root(
813 HKEY_USERS
, profile_sid
, KEY_ALL_ACCESS
);
815 LONG result
= user_reg_root
.DeleteKey(active_setup_path
.c_str());
816 if (result
!= ERROR_SUCCESS
) {
817 result
= user_reg_root
.DeleteKey(alternate_active_setup_path
.c_str());
818 if (result
!= ERROR_SUCCESS
&& result
!= ERROR_FILE_NOT_FOUND
) {
819 LOG(ERROR
) << "Failed to delete key at " << active_setup_path
820 << " and at " << alternate_active_setup_path
821 << ", result: " << result
;
824 VLOG_IF(1, result
== ERROR_SUCCESS
)
825 << "Deleted Active Setup entry for " << profile_sid
;
826 VLOG_IF(1, result
== ERROR_FILE_NOT_FOUND
)
827 << "No Active Setup entry to delete for " << profile_sid
;
830 user_reg_root
.Close();
831 if (RegUnLoadKey(HKEY_USERS
, profile_sid
) == ERROR_SUCCESS
)
832 VLOG(1) << "Unloaded registry hive for " << profile_sid
;
834 LOG(ERROR
) << "Error unloading registry hive for " << profile_sid
;
839 // Removes the persistent blacklist state for the current user. Note: this will
840 // not remove the state for users other than the one uninstalling Chrome on a
841 // system-level install (http://crbug.com/388725). Doing so would require
842 // extracting the per-user registry hive iteration from
843 // UninstallActiveSetupEntries so that it could service multiple tasks.
844 void RemoveBlacklistState() {
845 InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER
,
846 blacklist::kRegistryBeaconPath
,
848 InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER
,
849 blacklist::kRegistryFinchListPath
,
853 // Removes the persistent state for |distribution| for the current user. Note:
854 // this will not remove the state for users other than the one uninstalling
855 // Chrome on a system-level install; see RemoveBlacklistState for details.
856 void RemoveDistributionRegistryState(BrowserDistribution
* distribution
) {
857 static const base::char16
* const kKeysToPreserve
[] = {
859 L
"NativeMessagingHosts",
861 base::string16
key_name(L
"Software\\");
862 key_name
+= distribution
->GetInstallSubDir();
863 // Delete the contents of the distribution key except for those parts used by
864 // outsiders to configure Chrome.
865 DeleteRegistryKeyPartial(
866 HKEY_CURRENT_USER
, key_name
,
867 std::vector
<base::string16
>(
869 &kKeysToPreserve
[arraysize(kKeysToPreserve
) - 1]));
874 DeleteResult
DeleteChromeDirectoriesIfEmpty(
875 const base::FilePath
& application_directory
) {
876 DeleteResult
result(DeleteEmptyDir(application_directory
));
877 if (result
== DELETE_SUCCEEDED
) {
878 // Now check and delete if the parent directories are empty
879 // For example Google\Chrome or Chromium
880 const base::FilePath
product_directory(application_directory
.DirName());
881 if (!product_directory
.empty()) {
882 result
= DeleteEmptyDir(product_directory
);
883 if (result
== DELETE_SUCCEEDED
) {
884 const base::FilePath
vendor_directory(product_directory
.DirName());
885 if (!vendor_directory
.empty())
886 result
= DeleteEmptyDir(vendor_directory
);
890 if (result
== DELETE_NOT_EMPTY
)
891 result
= DELETE_SUCCEEDED
;
895 bool DeleteChromeRegistrationKeys(const InstallerState
& installer_state
,
896 BrowserDistribution
* dist
,
898 const base::string16
& browser_entry_suffix
,
899 InstallStatus
* exit_code
) {
901 if (dist
->GetDefaultBrowserControlPolicy() ==
902 BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED
) {
903 // We should have never set those keys.
907 base::FilePath
chrome_exe(installer_state
.target_path().Append(kChromeExe
));
909 // Delete Software\Classes\ChromeHTML.
910 const base::string16
prog_id(
911 dist
->GetBrowserProgIdPrefix() + browser_entry_suffix
);
912 base::string16
reg_prog_id(ShellUtil::kRegClasses
);
913 reg_prog_id
.push_back(base::FilePath::kSeparators
[0]);
914 reg_prog_id
.append(prog_id
);
915 InstallUtil::DeleteRegistryKey(root
, reg_prog_id
, WorkItem::kWow64Default
);
917 // Delete Software\Classes\Chrome.
918 base::string16
reg_app_id(ShellUtil::kRegClasses
);
919 reg_app_id
.push_back(base::FilePath::kSeparators
[0]);
920 // Append the requested suffix manually here (as ShellUtil::GetBrowserModelId
921 // would otherwise try to figure out the currently installed suffix).
922 reg_app_id
.append(dist
->GetBaseAppId() + browser_entry_suffix
);
923 InstallUtil::DeleteRegistryKey(root
, reg_app_id
, WorkItem::kWow64Default
);
925 // Delete all Start Menu Internet registrations that refer to this Chrome.
927 using base::win::RegistryKeyIterator
;
928 InstallUtil::ProgramCompare
open_command_pred(chrome_exe
);
929 base::string16 client_name
;
930 base::string16 client_key
;
931 base::string16 open_key
;
932 for (RegistryKeyIterator
iter(root
, ShellUtil::kRegStartMenuInternet
);
933 iter
.Valid(); ++iter
) {
934 client_name
.assign(iter
.Name());
935 client_key
.assign(ShellUtil::kRegStartMenuInternet
)
937 .append(client_name
);
938 open_key
.assign(client_key
).append(ShellUtil::kRegShellOpen
);
939 if (InstallUtil::DeleteRegistryKeyIf(root
, client_key
, open_key
,
940 WorkItem::kWow64Default
, NULL
, open_command_pred
)
941 != InstallUtil::NOT_FOUND
) {
942 // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
943 // references this Chrome (i.e., if it was made the default browser).
944 InstallUtil::DeleteRegistryValueIf(
945 root
, ShellUtil::kRegStartMenuInternet
, WorkItem::kWow64Default
,
946 NULL
, InstallUtil::ValueEquals(client_name
));
947 // Also delete the value for the default user if we're operating in
949 if (root
== HKEY_LOCAL_MACHINE
) {
950 InstallUtil::DeleteRegistryValueIf(
952 base::string16(L
".DEFAULT\\").append(
953 ShellUtil::kRegStartMenuInternet
).c_str(),
954 WorkItem::kWow64Default
, NULL
,
955 InstallUtil::ValueEquals(client_name
));
961 // Delete Software\RegisteredApplications\Chromium
962 InstallUtil::DeleteRegistryValue(
963 root
, ShellUtil::kRegRegisteredApplications
,
964 WorkItem::kWow64Default
,
965 dist
->GetBaseAppName() + browser_entry_suffix
);
967 // Delete the App Paths and Applications keys that let Explorer find Chrome:
968 // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
969 base::string16
app_key(ShellUtil::kRegClasses
);
970 app_key
.push_back(base::FilePath::kSeparators
[0]);
971 app_key
.append(L
"Applications");
972 app_key
.push_back(base::FilePath::kSeparators
[0]);
973 app_key
.append(installer::kChromeExe
);
974 InstallUtil::DeleteRegistryKey(root
, app_key
, WorkItem::kWow64Default
);
976 base::string16
app_path_key(ShellUtil::kAppPathsRegistryKey
);
977 app_path_key
.push_back(base::FilePath::kSeparators
[0]);
978 app_path_key
.append(installer::kChromeExe
);
979 InstallUtil::DeleteRegistryKey(root
, app_path_key
, WorkItem::kWow64Default
);
981 // Cleanup OpenWithList and OpenWithProgids:
982 // http://msdn.microsoft.com/en-us/library/bb166549
983 base::string16 file_assoc_key
;
984 base::string16 open_with_list_key
;
985 base::string16 open_with_progids_key
;
986 for (int i
= 0; ShellUtil::kPotentialFileAssociations
[i
] != NULL
; ++i
) {
987 file_assoc_key
.assign(ShellUtil::kRegClasses
);
988 file_assoc_key
.push_back(base::FilePath::kSeparators
[0]);
989 file_assoc_key
.append(ShellUtil::kPotentialFileAssociations
[i
]);
990 file_assoc_key
.push_back(base::FilePath::kSeparators
[0]);
992 open_with_list_key
.assign(file_assoc_key
);
993 open_with_list_key
.append(L
"OpenWithList");
994 open_with_list_key
.push_back(base::FilePath::kSeparators
[0]);
995 open_with_list_key
.append(installer::kChromeExe
);
996 InstallUtil::DeleteRegistryKey(
997 root
, open_with_list_key
, WorkItem::kWow64Default
);
999 open_with_progids_key
.assign(file_assoc_key
);
1000 open_with_progids_key
.append(ShellUtil::kRegOpenWithProgids
);
1001 InstallUtil::DeleteRegistryValue(root
, open_with_progids_key
,
1002 WorkItem::kWow64Default
, prog_id
);
1005 // Cleanup in case Chrome had been made the default browser.
1007 // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
1008 // references this Chrome. Do this explicitly here for the case where HKCU is
1009 // being processed; the iteration above will have no hits since registration
1011 InstallUtil::DeleteRegistryValueIf(
1012 root
, ShellUtil::kRegStartMenuInternet
, WorkItem::kWow64Default
, NULL
,
1013 InstallUtil::ValueEquals(dist
->GetBaseAppName() + browser_entry_suffix
));
1015 // Delete each protocol association if it references this Chrome.
1016 InstallUtil::ProgramCompare
open_command_pred(chrome_exe
);
1017 base::string16
parent_key(ShellUtil::kRegClasses
);
1018 parent_key
.push_back(base::FilePath::kSeparators
[0]);
1019 const base::string16::size_type base_length
= parent_key
.size();
1020 base::string16 child_key
;
1021 for (const wchar_t* const* proto
=
1022 &ShellUtil::kPotentialProtocolAssociations
[0];
1025 parent_key
.resize(base_length
);
1026 parent_key
.append(*proto
);
1027 child_key
.assign(parent_key
).append(ShellUtil::kRegShellOpen
);
1028 InstallUtil::DeleteRegistryKeyIf(root
, parent_key
, child_key
,
1029 WorkItem::kWow64Default
, NULL
,
1033 RemoveFiletypeRegistration(installer_state
, root
, browser_entry_suffix
);
1035 *exit_code
= installer::UNINSTALL_SUCCESSFUL
;
1039 void RemoveChromeLegacyRegistryKeys(BrowserDistribution
* dist
,
1040 const base::FilePath
& chrome_exe
) {
1041 // We used to register Chrome to handle crx files, but this turned out
1042 // to be not worth the hassle. Remove these old registry entries if
1043 // they exist. See: http://codereview.chromium.org/210007
1045 #if defined(GOOGLE_CHROME_BUILD)
1046 const wchar_t kChromeExtProgId
[] = L
"ChromeExt";
1048 const wchar_t kChromeExtProgId
[] = L
"ChromiumExt";
1051 HKEY roots
[] = { HKEY_LOCAL_MACHINE
, HKEY_CURRENT_USER
};
1052 for (size_t i
= 0; i
< arraysize(roots
); ++i
) {
1053 base::string16 suffix
;
1054 if (roots
[i
] == HKEY_LOCAL_MACHINE
)
1055 suffix
= ShellUtil::GetCurrentInstallationSuffix(dist
, chrome_exe
);
1057 // Delete Software\Classes\ChromeExt,
1058 base::string16
ext_prog_id(ShellUtil::kRegClasses
);
1059 ext_prog_id
.push_back(base::FilePath::kSeparators
[0]);
1060 ext_prog_id
.append(kChromeExtProgId
);
1061 ext_prog_id
.append(suffix
);
1062 InstallUtil::DeleteRegistryKey(roots
[i
], ext_prog_id
,
1063 WorkItem::kWow64Default
);
1065 // Delete Software\Classes\.crx,
1066 base::string16
ext_association(ShellUtil::kRegClasses
);
1067 ext_association
.append(L
"\\");
1068 ext_association
.append(L
".crx");
1069 InstallUtil::DeleteRegistryKey(roots
[i
], ext_association
,
1070 WorkItem::kWow64Default
);
1074 void UninstallFirewallRules(BrowserDistribution
* dist
,
1075 const base::FilePath
& chrome_exe
) {
1076 scoped_ptr
<FirewallManager
> manager
=
1077 FirewallManager::Create(dist
, chrome_exe
);
1079 manager
->RemoveFirewallRules();
1082 InstallStatus
UninstallProduct(const InstallationState
& original_state
,
1083 const InstallerState
& installer_state
,
1084 const base::FilePath
& setup_exe
,
1085 const Product
& product
,
1087 bool force_uninstall
,
1088 const base::CommandLine
& cmd_line
) {
1089 InstallStatus status
= installer::UNINSTALL_CONFIRMED
;
1090 BrowserDistribution
* browser_dist
= product
.distribution();
1091 const base::FilePath
chrome_exe(
1092 installer_state
.target_path().Append(installer::kChromeExe
));
1094 bool is_chrome
= product
.is_chrome();
1096 VLOG(1) << "UninstallProduct: " << browser_dist
->GetDisplayName();
1098 if (force_uninstall
) {
1099 // Since --force-uninstall command line option is used, we are going to
1100 // do silent uninstall. Try to close all running Chrome instances.
1101 // NOTE: We don't do this for Chrome Frame.
1103 CloseAllChromeProcesses();
1104 } else if (is_chrome
) {
1105 // no --force-uninstall so lets show some UI dialog boxes.
1106 status
= IsChromeActiveOrUserCancelled(installer_state
, product
);
1107 if (status
!= installer::UNINSTALL_CONFIRMED
&&
1108 status
!= installer::UNINSTALL_DELETE_PROFILE
)
1111 const base::string16
suffix(
1112 ShellUtil::GetCurrentInstallationSuffix(browser_dist
, chrome_exe
));
1114 // Check if we need admin rights to cleanup HKLM (the conditions for
1115 // requiring a cleanup are the same as the conditions to do the actual
1116 // cleanup where DeleteChromeRegistrationKeys() is invoked for
1117 // HKEY_LOCAL_MACHINE below). If we do, try to launch another uninstaller
1118 // (silent) in elevated mode to do HKLM cleanup.
1119 // And continue uninstalling in the current process also to do HKCU cleanup.
1121 ShellUtil::QuickIsChromeRegisteredInHKLM(
1122 browser_dist
, chrome_exe
, suffix
) &&
1123 !::IsUserAnAdmin() &&
1124 base::win::GetVersion() >= base::win::VERSION_VISTA
&&
1125 !cmd_line
.HasSwitch(installer::switches::kRunAsAdmin
)) {
1126 base::CommandLine
new_cmd(base::CommandLine::NO_PROGRAM
);
1127 new_cmd
.AppendArguments(cmd_line
, true);
1128 // Append --run-as-admin flag to let the new instance of setup.exe know
1129 // that we already tried to launch ourselves as admin.
1130 new_cmd
.AppendSwitch(installer::switches::kRunAsAdmin
);
1131 // Append --remove-chrome-registration to remove registry keys only.
1132 new_cmd
.AppendSwitch(installer::switches::kRemoveChromeRegistration
);
1133 if (!suffix
.empty()) {
1134 new_cmd
.AppendSwitchNative(
1135 installer::switches::kRegisterChromeBrowserSuffix
, suffix
);
1137 DWORD exit_code
= installer::UNKNOWN_STATUS
;
1138 InstallUtil::ExecuteExeAsAdmin(new_cmd
, &exit_code
);
1143 // Chrome is not in use so lets uninstall Chrome by deleting various files
1144 // and registry entries. Here we will just make best effort and keep going
1145 // in case of errors.
1146 ClearRlzProductState();
1147 // Delete the key that delegate_execute might make.
1148 if (base::win::GetVersion() >= base::win::VERSION_WIN8
) {
1149 InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER
,
1150 chrome::kMetroRegistryPath
,
1151 WorkItem::kWow64Default
);
1154 auto_launch_util::DisableAllAutoStartFeatures(
1155 base::ASCIIToUTF16(chrome::kInitialProfile
));
1157 // If user-level chrome is self-destructing as a result of encountering a
1158 // system-level chrome, retarget owned non-default shortcuts (app shortcuts,
1159 // profile shortcuts, etc.) to the system-level chrome.
1160 if (cmd_line
.HasSwitch(installer::switches::kSelfDestruct
) &&
1161 !installer_state
.system_install()) {
1162 const base::FilePath
system_chrome_path(
1163 GetChromeInstallPath(true, browser_dist
).
1164 Append(installer::kChromeExe
));
1165 VLOG(1) << "Retargeting user-generated Chrome shortcuts.";
1166 if (base::PathExists(system_chrome_path
)) {
1167 RetargetUserShortcutsWithArgs(installer_state
, product
, chrome_exe
,
1168 system_chrome_path
);
1170 LOG(ERROR
) << "Retarget failed: system-level Chrome not found.";
1174 DeleteShortcuts(installer_state
, product
, chrome_exe
);
1177 // Delete the registry keys (Uninstall key and Version key).
1178 HKEY reg_root
= installer_state
.root_key();
1180 // Note that we must retrieve the distribution-specific data before deleting
1181 // product.GetVersionKey().
1182 base::string16
distribution_data(browser_dist
->GetDistributionData(reg_root
));
1184 // Remove Control Panel uninstall link.
1185 if (product
.ShouldCreateUninstallEntry()) {
1186 InstallUtil::DeleteRegistryKey(
1187 reg_root
, browser_dist
->GetUninstallRegPath(), KEY_WOW64_32KEY
);
1190 // Remove Omaha product key.
1191 InstallUtil::DeleteRegistryKey(
1192 reg_root
, browser_dist
->GetVersionKey(), KEY_WOW64_32KEY
);
1194 // Also try to delete the MSI value in the ClientState key (it might not be
1195 // there). This is due to a Google Update behaviour where an uninstall and a
1196 // rapid reinstall might result in stale values from the old ClientState key
1197 // being picked up on reinstall.
1198 product
.SetMsiMarker(installer_state
.system_install(), false);
1200 InstallStatus ret
= installer::UNKNOWN_STATUS
;
1203 const base::string16
suffix(
1204 ShellUtil::GetCurrentInstallationSuffix(browser_dist
, chrome_exe
));
1206 // Remove all Chrome registration keys.
1207 // Registration data is put in HKCU for both system level and user level
1209 DeleteChromeRegistrationKeys(installer_state
, browser_dist
,
1210 HKEY_CURRENT_USER
, suffix
, &ret
);
1212 #if defined(GOOGLE_CHROME_BUILD)
1213 if (!InstallUtil::IsChromeSxSProcess())
1214 RemoveAppLauncherVersionKey(reg_root
);
1215 #endif // GOOGLE_CHROME_BUILD
1217 // If the user's Chrome is registered with a suffix: it is possible that old
1218 // unsuffixed registrations were left in HKCU (e.g. if this install was
1219 // previously installed with no suffix in HKCU (old suffix rules if the user
1220 // is not an admin (or declined UAC at first run)) and later had to be
1221 // suffixed when fully registered in HKLM (e.g. when later making Chrome
1222 // default through the UI)).
1223 // Remove remaining HKCU entries with no suffix if any.
1224 if (!suffix
.empty()) {
1225 DeleteChromeRegistrationKeys(installer_state
, browser_dist
,
1226 HKEY_CURRENT_USER
, base::string16(), &ret
);
1228 // For similar reasons it is possible in very few installs (from
1229 // 21.0.1180.0 and fixed shortly after) to be installed with the new-style
1230 // suffix, but have some old-style suffix registrations left behind.
1231 base::string16 old_style_suffix
;
1232 if (ShellUtil::GetOldUserSpecificRegistrySuffix(&old_style_suffix
) &&
1233 suffix
!= old_style_suffix
) {
1234 DeleteChromeRegistrationKeys(installer_state
, browser_dist
,
1235 HKEY_CURRENT_USER
, old_style_suffix
, &ret
);
1239 // Chrome is registered in HKLM for all system-level installs and for
1240 // user-level installs for which Chrome has been made the default browser.
1241 // Always remove the HKLM registration for system-level installs. For
1242 // user-level installs, only remove it if both: 1) this uninstall isn't a
1243 // self destruct following the installation of a system-level Chrome
1244 // (because the system-level Chrome owns the HKLM registration now), and 2)
1245 // this user has made Chrome their default browser (i.e. has shell
1246 // integration entries registered with |suffix| (note: |suffix| will be the
1247 // empty string if required as it is obtained by
1248 // GetCurrentInstallationSuffix() above)).
1249 // TODO(gab): This can still leave parts of a suffixed install behind. To be
1250 // able to remove them we would need to be able to remove only suffixed
1251 // entries (as it is now some of the registry entries (e.g. App Paths) are
1252 // unsuffixed; thus removing suffixed installs is prohibited in HKLM if
1253 // !|remove_all| for now).
1254 if (installer_state
.system_install() ||
1256 ShellUtil::QuickIsChromeRegisteredInHKLM(
1257 browser_dist
, chrome_exe
, suffix
))) {
1258 DeleteChromeRegistrationKeys(installer_state
, browser_dist
,
1259 HKEY_LOCAL_MACHINE
, suffix
, &ret
);
1262 ProcessDelegateExecuteWorkItems(installer_state
, product
);
1264 ProcessOnOsUpgradeWorkItems(installer_state
, product
);
1266 UninstallActiveSetupEntries(installer_state
, product
);
1268 UninstallFirewallRules(browser_dist
, chrome_exe
);
1270 RemoveBlacklistState();
1272 // Notify the shell that associations have changed since Chrome was likely
1274 SHChangeNotify(SHCNE_ASSOCCHANGED
, SHCNF_IDLIST
, NULL
, NULL
);
1277 if (installer_state
.is_multi_install())
1278 ProcessGoogleUpdateItems(original_state
, installer_state
, product
);
1280 // Get the state of the installed product (if any)
1281 const ProductState
* product_state
=
1282 original_state
.GetProductState(installer_state
.system_install(),
1283 browser_dist
->GetType());
1285 // Delete shared registry keys as well (these require admin rights) if
1286 // remove_all option is specified.
1288 if (!InstallUtil::IsChromeSxSProcess() && is_chrome
) {
1289 // Delete media player registry key that exists only in HKLM.
1290 // We don't delete this key in SxS uninstall or Chrome Frame uninstall
1291 // as we never set the key for those products.
1292 base::string16
reg_path(installer::kMediaPlayerRegPath
);
1293 reg_path
.push_back(base::FilePath::kSeparators
[0]);
1294 reg_path
.append(installer::kChromeExe
);
1295 InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE
, reg_path
,
1296 WorkItem::kWow64Default
);
1299 // Unregister any dll servers that we may have registered for this
1301 if (product_state
!= NULL
) {
1302 std::vector
<base::FilePath
> com_dll_list
;
1303 product
.AddComDllList(&com_dll_list
);
1304 base::FilePath dll_folder
= installer_state
.target_path().AppendASCII(
1305 product_state
->version().GetString());
1307 scoped_ptr
<WorkItemList
> unreg_work_item_list(
1308 WorkItem::CreateWorkItemList());
1310 AddRegisterComDllWorkItems(dll_folder
,
1312 installer_state
.system_install(),
1313 false, // Unregister
1315 unreg_work_item_list
.get());
1316 unreg_work_item_list
->Do();
1319 if (product
.is_chrome_frame())
1320 ProcessIELowRightsPolicyWorkItems(installer_state
);
1323 // Close any Chrome Frame helper processes that may be running.
1324 if (product
.is_chrome_frame()) {
1325 VLOG(1) << "Closing the Chrome Frame helper process";
1326 CloseChromeFrameHelperProcess();
1329 if (product_state
== NULL
)
1330 return installer::UNINSTALL_SUCCESSFUL
;
1332 // Finally delete all the files from Chrome folder after moving setup.exe
1333 // and the user's Local State to a temp location.
1334 bool delete_profile
= ShouldDeleteProfile(installer_state
, cmd_line
, status
,
1336 ret
= installer::UNINSTALL_SUCCESSFUL
;
1338 // When deleting files, we must make sure that we're either a "single"
1339 // (aka non-multi) installation or we are the Chrome Binaries.
1341 base::FilePath
user_data_dir(GetUserDataDir(product
));
1342 base::FilePath backup_state_file
;
1343 if (!user_data_dir
.empty()) {
1344 backup_state_file
= BackupLocalStateFile(user_data_dir
);
1346 LOG(ERROR
) << "Could not retrieve the user's profile directory.";
1347 ret
= installer::UNINSTALL_FAILED
;
1348 delete_profile
= false;
1351 if (!installer_state
.is_multi_install() || product
.is_chrome_binaries()) {
1352 DeleteResult delete_result
= DeleteChromeFilesAndFolders(
1353 installer_state
, base::MakeAbsoluteFilePath(setup_exe
));
1354 if (delete_result
== DELETE_FAILED
) {
1355 ret
= installer::UNINSTALL_FAILED
;
1356 } else if (delete_result
== DELETE_REQUIRES_REBOOT
) {
1357 ret
= installer::UNINSTALL_REQUIRES_REBOOT
;
1361 if (delete_profile
) {
1362 DeleteUserDataDir(user_data_dir
, product
.is_chrome_frame());
1363 RemoveDistributionRegistryState(browser_dist
);
1366 if (!force_uninstall
) {
1367 VLOG(1) << "Uninstallation complete. Launching post-uninstall operations.";
1368 browser_dist
->DoPostUninstallOperations(product_state
->version(),
1369 backup_state_file
, distribution_data
);
1372 // Try and delete the preserved local state once the post-install
1373 // operations are complete.
1374 if (!backup_state_file
.empty())
1375 base::DeleteFile(backup_state_file
, false);
1380 void CleanUpInstallationDirectoryAfterUninstall(
1381 const InstallationState
& original_state
,
1382 const InstallerState
& installer_state
,
1383 const base::FilePath
& setup_exe
,
1384 InstallStatus
* uninstall_status
) {
1385 if (*uninstall_status
!= UNINSTALL_SUCCESSFUL
&&
1386 *uninstall_status
!= UNINSTALL_REQUIRES_REBOOT
) {
1389 const base::FilePath
target_path(installer_state
.target_path());
1390 if (target_path
.empty()) {
1391 LOG(ERROR
) << "No installation destination path.";
1392 *uninstall_status
= UNINSTALL_FAILED
;
1395 if (!target_path
.IsParent(base::MakeAbsoluteFilePath(setup_exe
))) {
1396 VLOG(1) << "setup.exe is not in target path. Skipping installer cleanup.";
1399 base::FilePath
install_directory(setup_exe
.DirName());
1401 bool remove_setup
= CheckShouldRemoveSetup(original_state
, installer_state
);
1404 // In order to be able to remove the folder in which we're running, we
1405 // need to move setup.exe out of the install folder.
1406 // TODO(tommi): What if the temp folder is on a different volume?
1407 MoveSetupOutOfInstallFolder(installer_state
, setup_exe
);
1410 // Remove files from "...\<product>\Application\<version>\Installer"
1411 if (!RemoveInstallerFiles(install_directory
, remove_setup
)) {
1412 *uninstall_status
= UNINSTALL_FAILED
;
1419 // Try to remove the empty directory hierarchy.
1421 // Delete "...\<product>\Application\<version>\Installer"
1422 if (DeleteEmptyDir(install_directory
) != DELETE_SUCCEEDED
) {
1423 *uninstall_status
= UNINSTALL_FAILED
;
1427 // Delete "...\<product>\Application\<version>"
1428 DeleteResult delete_result
= DeleteEmptyDir(install_directory
.DirName());
1429 if (delete_result
== DELETE_FAILED
||
1430 (delete_result
== DELETE_NOT_EMPTY
&&
1431 *uninstall_status
!= UNINSTALL_REQUIRES_REBOOT
)) {
1432 *uninstall_status
= UNINSTALL_FAILED
;
1436 if (*uninstall_status
== UNINSTALL_REQUIRES_REBOOT
) {
1437 // Delete the Application directory at reboot if empty.
1438 ScheduleFileSystemEntityForDeletion(target_path
);
1440 // If we need a reboot to continue, schedule the parent directories for
1441 // deletion unconditionally. If they are not empty, the session manager
1442 // will not delete them on reboot.
1443 ScheduleParentAndGrandparentForDeletion(target_path
);
1444 } else if (DeleteChromeDirectoriesIfEmpty(target_path
) == DELETE_FAILED
) {
1445 *uninstall_status
= UNINSTALL_FAILED
;
1449 } // namespace installer