Upstream version 7.36.149.0
[platform/framework/web/crosswalk.git] / src / chrome / installer / setup / uninstall.cc
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.
4 //
5 // This file defines the methods useful for uninstalling Chrome.
6
7 #include "chrome/installer/setup/uninstall.h"
8
9 #include <windows.h>
10
11 #include <vector>
12
13 #include "base/base_paths.h"
14 #include "base/file_util.h"
15 #include "base/files/file_enumerator.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/launcher_support/chrome_launcher_support.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 "content/public/common/result_codes.h"
50 #include "rlz/lib/rlz_lib.h"
51
52 using base::win::RegKey;
53
54 namespace installer {
55
56 namespace {
57
58 // Avoid leaving behind a Temp dir.  If one exists, ask SelfCleaningTempDir to
59 // clean it up for us.  This may involve scheduling it for deletion after
60 // reboot.  Don't report that a reboot is required in this case, however.
61 // TODO(erikwright): Shouldn't this still lead to
62 // ScheduleParentAndGrandparentForDeletion?
63 void DeleteInstallTempDir(const base::FilePath& target_path) {
64   base::FilePath temp_path(target_path.DirName().Append(
65       installer::kInstallTempDir));
66   if (base::DirectoryExists(temp_path)) {
67     SelfCleaningTempDir temp_dir;
68     if (!temp_dir.Initialize(target_path.DirName(),
69                              installer::kInstallTempDir) ||
70         !temp_dir.Delete()) {
71       LOG(ERROR) << "Failed to delete temp dir " << temp_path.value();
72     }
73   }
74 }
75
76 // Iterates over the list of distribution types in |dist_types|, and
77 // adds to |update_list| the work item to update the corresponding "ap"
78 // registry value specified in |channel_info|.
79 void AddChannelValueUpdateWorkItems(
80     const InstallationState& original_state,
81     const InstallerState& installer_state,
82     const ChannelInfo& channel_info,
83     const std::vector<BrowserDistribution::Type>& dist_types,
84     WorkItemList* update_list) {
85   const bool system_level = installer_state.system_install();
86   const HKEY reg_root = installer_state.root_key();
87   for (size_t i = 0; i < dist_types.size(); ++i) {
88     BrowserDistribution::Type dist_type = dist_types[i];
89     const ProductState* product_state =
90         original_state.GetProductState(system_level, dist_type);
91     // Only modify other products if they're installed and multi.
92     if (product_state != NULL &&
93         product_state->is_multi_install() &&
94         !product_state->channel().Equals(channel_info)) {
95       BrowserDistribution* other_dist =
96           BrowserDistribution::GetSpecificDistribution(dist_type);
97       update_list->AddSetRegValueWorkItem(reg_root, other_dist->GetStateKey(),
98           google_update::kRegApField, channel_info.value(), true);
99     } else {
100       LOG_IF(ERROR,
101              product_state != NULL && product_state->is_multi_install())
102           << "Channel value for "
103           << BrowserDistribution::GetSpecificDistribution(
104                  dist_type)->GetDisplayName()
105           << " is somehow already set to the desired new value of "
106           << channel_info.value();
107     }
108   }
109 }
110
111 // Makes appropriate changes to the Google Update "ap" value in the registry.
112 // Specifically, removes the flags associated with this product ("-chrome" or
113 // "-chromeframe") from the "ap" values for all other installed products and for
114 // the multi-installer package.
115 void ProcessGoogleUpdateItems(const InstallationState& original_state,
116                               const InstallerState& installer_state,
117                               const Product& product) {
118   DCHECK(installer_state.is_multi_install());
119   const bool system_level = installer_state.system_install();
120   BrowserDistribution* distribution = product.distribution();
121   const ProductState* product_state =
122       original_state.GetProductState(system_level, distribution->GetType());
123   DCHECK(product_state != NULL);
124   ChannelInfo channel_info;
125
126   // Remove product's flags from the channel value.
127   channel_info.set_value(product_state->channel().value());
128   const bool modified = product.SetChannelFlags(false, &channel_info);
129
130   // Apply the new channel value to all other products and to the multi package.
131   if (modified) {
132     scoped_ptr<WorkItemList>
133         update_list(WorkItem::CreateNoRollbackWorkItemList());
134     std::vector<BrowserDistribution::Type> dist_types;
135     for (size_t i = 0; i < BrowserDistribution::NUM_TYPES; ++i) {
136       BrowserDistribution::Type other_dist_type =
137           static_cast<BrowserDistribution::Type>(i);
138       if (distribution->GetType() != other_dist_type)
139         dist_types.push_back(other_dist_type);
140     }
141     AddChannelValueUpdateWorkItems(original_state, installer_state,
142                                    channel_info, dist_types,
143                                    update_list.get());
144     bool success = update_list->Do();
145     LOG_IF(ERROR, !success) << "Failed updating channel values.";
146   }
147 }
148
149 void ProcessOnOsUpgradeWorkItems(const InstallerState& installer_state,
150                                  const Product& product) {
151   scoped_ptr<WorkItemList> work_item_list(
152       WorkItem::CreateNoRollbackWorkItemList());
153   AddOsUpgradeWorkItems(installer_state, base::FilePath(), Version(), product,
154                         work_item_list.get());
155   if (!work_item_list->Do())
156     LOG(ERROR) << "Failed to remove on-os-upgrade command.";
157 }
158
159 void ProcessIELowRightsPolicyWorkItems(const InstallerState& installer_state) {
160   scoped_ptr<WorkItemList> work_items(WorkItem::CreateNoRollbackWorkItemList());
161   AddDeleteOldIELowRightsPolicyWorkItems(installer_state, work_items.get());
162   work_items->Do();
163   RefreshElevationPolicy();
164 }
165
166 void ClearRlzProductState() {
167   const rlz_lib::AccessPoint points[] = {rlz_lib::CHROME_OMNIBOX,
168                                          rlz_lib::CHROME_HOME_PAGE,
169                                          rlz_lib::CHROME_APP_LIST,
170                                          rlz_lib::NO_ACCESS_POINT};
171
172   rlz_lib::ClearProductState(rlz_lib::CHROME, points);
173
174   // If chrome has been reactivated, clear all events for this brand as well.
175   base::string16 reactivation_brand_wide;
176   if (GoogleUpdateSettings::GetReactivationBrand(&reactivation_brand_wide)) {
177     std::string reactivation_brand(base::UTF16ToASCII(reactivation_brand_wide));
178     rlz_lib::SupplementaryBranding branding(reactivation_brand.c_str());
179     rlz_lib::ClearProductState(rlz_lib::CHROME, points);
180   }
181 }
182
183 // Decides whether setup.exe and the installer archive should be removed based
184 // on the original and installer states:
185 // * non-multi product being uninstalled: remove both
186 // * any multi product left besides App Host: keep both
187 // * only App Host left: keep setup.exe
188 void CheckShouldRemoveSetupAndArchive(const InstallationState& original_state,
189                                       const InstallerState& installer_state,
190                                       bool* remove_setup,
191                                       bool* remove_archive) {
192   *remove_setup = true;
193   *remove_archive = true;
194
195   // If any multi-install product is left (other than App Host) we must leave
196   // the installer and archive. For the App Host, we only leave the installer.
197   if (!installer_state.is_multi_install()) {
198     VLOG(1) << "Removing all installer files for a non-multi installation.";
199   } else {
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
211         // product.
212         *remove_setup = false;
213         // As a special case, we can still remove the actual archive if the
214         // only remaining product is the App Host.
215         if (dist_type != BrowserDistribution::CHROME_APP_HOST) {
216           VLOG(1) << "Keeping all installer files due to a remaining "
217                   << "multi-install product.";
218           *remove_archive = false;
219           return;
220         }
221         VLOG(1) << "Keeping setup.exe due to a remaining "
222                 << "app-host installation.";
223       }
224     }
225     VLOG(1) << "Removing the installer archive.";
226     if (remove_setup)
227       VLOG(1) << "Removing setup.exe.";
228   }
229 }
230
231 // Removes all files from the installer directory, leaving setup.exe iff
232 // |remove_setup| is false.
233 // Returns false in case of an error.
234 bool RemoveInstallerFiles(const base::FilePath& installer_directory,
235                           bool remove_setup) {
236   base::FileEnumerator file_enumerator(
237       installer_directory,
238       false,
239       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
240   bool success = true;
241
242   base::FilePath setup_exe_base_name(installer::kSetupExe);
243
244   for (base::FilePath to_delete = file_enumerator.Next(); !to_delete.empty();
245        to_delete = file_enumerator.Next()) {
246     if (!remove_setup && to_delete.BaseName() == setup_exe_base_name)
247       continue;
248
249     VLOG(1) << "Deleting installer path " << to_delete.value();
250     if (!base::DeleteFile(to_delete, true)) {
251       LOG(ERROR) << "Failed to delete path: " << to_delete.value();
252       success = false;
253     }
254   }
255
256   return success;
257 }
258
259 // Kills all Chrome processes, immediately.
260 void CloseAllChromeProcesses() {
261   base::CleanupProcesses(installer::kChromeExe, base::TimeDelta(),
262                          content::RESULT_CODE_HUNG, NULL);
263   base::CleanupProcesses(installer::kNaClExe, base::TimeDelta(),
264                          content::RESULT_CODE_HUNG, NULL);
265 }
266
267 // Attempts to close the Chrome Frame helper process by sending WM_CLOSE
268 // messages to its window, or just killing it if that doesn't work.
269 void CloseChromeFrameHelperProcess() {
270   HWND window = FindWindow(installer::kChromeFrameHelperWndClass, NULL);
271   if (!::IsWindow(window))
272     return;
273
274   const DWORD kWaitMs = 3000;
275
276   DWORD pid = 0;
277   ::GetWindowThreadProcessId(window, &pid);
278   DCHECK_NE(pid, 0U);
279   base::win::ScopedHandle process(::OpenProcess(SYNCHRONIZE, FALSE, pid));
280   PLOG_IF(INFO, !process) << "Failed to open process: " << pid;
281
282   bool kill = true;
283   if (SendMessageTimeout(window, WM_CLOSE, 0, 0, SMTO_BLOCK, kWaitMs, NULL) &&
284       process) {
285     VLOG(1) << "Waiting for " << installer::kChromeFrameHelperExe;
286     DWORD wait = ::WaitForSingleObject(process, kWaitMs);
287     if (wait != WAIT_OBJECT_0) {
288       LOG(WARNING) << "Wait for " << installer::kChromeFrameHelperExe
289                    << " to exit failed or timed out.";
290     } else {
291       kill = false;
292       VLOG(1) << installer::kChromeFrameHelperExe << " exited normally.";
293     }
294   }
295
296   if (kill) {
297     VLOG(1) << installer::kChromeFrameHelperExe << " hung.  Killing.";
298     base::CleanupProcesses(installer::kChromeFrameHelperExe, base::TimeDelta(),
299                            content::RESULT_CODE_HUNG, NULL);
300   }
301 }
302
303 // Updates shortcuts to |old_target_exe| that have non-empty args, making them
304 // target |new_target_exe| instead. The non-empty args requirement is a
305 // heuristic to determine whether a shortcut is "user-generated". This routine
306 // can only be called for user-level installs.
307 void RetargetUserShortcutsWithArgs(const InstallerState& installer_state,
308                                    const Product& product,
309                                    const base::FilePath& old_target_exe,
310                                    const base::FilePath& new_target_exe) {
311   if (installer_state.system_install()) {
312     NOTREACHED();
313     return;
314   }
315   BrowserDistribution* dist = product.distribution();
316   ShellUtil::ShellChange install_level = ShellUtil::CURRENT_USER;
317
318   // Retarget all shortcuts that point to |old_target_exe| from all
319   // ShellUtil::ShortcutLocations.
320   VLOG(1) << "Retargeting shortcuts.";
321   for (int location = ShellUtil::SHORTCUT_LOCATION_FIRST;
322       location < ShellUtil::NUM_SHORTCUT_LOCATIONS; ++location) {
323     if (!ShellUtil::RetargetShortcutsWithArgs(
324             static_cast<ShellUtil::ShortcutLocation>(location), dist,
325             install_level, old_target_exe, new_target_exe)) {
326       LOG(WARNING) << "Failed to retarget shortcuts in ShortcutLocation: "
327                    << location;
328     }
329   }
330 }
331
332 // Deletes shortcuts at |install_level| from Start menu, Desktop,
333 // Quick Launch, taskbar, and secondary tiles on the Start Screen (Win8+).
334 // Only shortcuts pointing to |target_exe| will be removed.
335 void DeleteShortcuts(const InstallerState& installer_state,
336                      const Product& product,
337                      const base::FilePath& target_exe) {
338   BrowserDistribution* dist = product.distribution();
339
340   // The per-user shortcut for this user, if present on a system-level install,
341   // has already been deleted in chrome_browser_main_win.cc::DoUninstallTasks().
342   ShellUtil::ShellChange install_level = installer_state.system_install() ?
343       ShellUtil::SYSTEM_LEVEL : ShellUtil::CURRENT_USER;
344
345   // Delete and unpin all shortcuts that point to |target_exe| from all
346   // ShellUtil::ShortcutLocations.
347   VLOG(1) << "Deleting shortcuts.";
348   for (int location = ShellUtil::SHORTCUT_LOCATION_FIRST;
349       location < ShellUtil::NUM_SHORTCUT_LOCATIONS; ++location) {
350     if (!ShellUtil::RemoveShortcuts(
351             static_cast<ShellUtil::ShortcutLocation>(location), dist,
352             install_level, target_exe)) {
353       LOG(WARNING) << "Failed to delete shortcuts in ShortcutLocation:"
354                    << location;
355     }
356   }
357 }
358
359 bool ScheduleParentAndGrandparentForDeletion(const base::FilePath& path) {
360   base::FilePath parent_dir = path.DirName();
361   bool ret = ScheduleFileSystemEntityForDeletion(parent_dir);
362   if (!ret) {
363     LOG(ERROR) << "Failed to schedule parent dir for deletion: "
364                << parent_dir.value();
365   } else {
366     base::FilePath grandparent_dir(parent_dir.DirName());
367     ret = ScheduleFileSystemEntityForDeletion(grandparent_dir);
368     if (!ret) {
369       LOG(ERROR) << "Failed to schedule grandparent dir for deletion: "
370                  << grandparent_dir.value();
371     }
372   }
373   return ret;
374 }
375
376 // Deletes the given directory if it is empty. Returns DELETE_SUCCEEDED if the
377 // directory is deleted, DELETE_NOT_EMPTY if it is not empty, and DELETE_FAILED
378 // otherwise.
379 DeleteResult DeleteEmptyDir(const base::FilePath& path) {
380   if (!base::IsDirectoryEmpty(path))
381     return DELETE_NOT_EMPTY;
382
383   if (base::DeleteFile(path, true))
384     return DELETE_SUCCEEDED;
385
386   LOG(ERROR) << "Failed to delete folder: " << path.value();
387   return DELETE_FAILED;
388 }
389
390 // Get the user data directory, which is *not* DIR_USER_DATA for Chrome Frame.
391 // TODO(grt): Remove Chrome Frame uninstall support when usage is low enough.
392 base::FilePath GetUserDataDir(const Product& product) {
393   base::FilePath path;
394   bool is_chrome_frame = product.is_chrome_frame();
395   int key = is_chrome_frame ? base::DIR_LOCAL_APP_DATA : chrome::DIR_USER_DATA;
396   if (!PathService::Get(key, &path))
397     return base::FilePath();
398   if (is_chrome_frame) {
399     path = path.Append(product.distribution()->GetInstallSubDir());
400     path = path.Append(chrome::kUserDataDirname);
401   }
402   return path;
403 }
404
405 // Creates a copy of the local state file and returns a path to the copy.
406 base::FilePath BackupLocalStateFile(const base::FilePath& user_data_dir) {
407   base::FilePath backup;
408   base::FilePath state_file(user_data_dir.Append(chrome::kLocalStateFilename));
409   if (!base::CreateTemporaryFile(&backup))
410     LOG(ERROR) << "Failed to create temporary file for Local State.";
411   else
412     base::CopyFile(state_file, backup);
413   return backup;
414 }
415
416 // Deletes a given user data directory as well as the containing product
417 // directories if they are empty (e.g., "Google\Chrome").
418 DeleteResult DeleteUserDataDir(const base::FilePath& user_data_dir,
419                                bool schedule_on_failure) {
420   if (user_data_dir.empty())
421     return DELETE_SUCCEEDED;
422
423   DeleteResult result = DELETE_SUCCEEDED;
424   VLOG(1) << "Deleting user profile " << user_data_dir.value();
425   if (!base::DeleteFile(user_data_dir, true)) {
426     LOG(ERROR) << "Failed to delete user profile dir: "
427                << user_data_dir.value();
428     if (schedule_on_failure) {
429       ScheduleDirectoryForDeletion(user_data_dir);
430       result = DELETE_REQUIRES_REBOOT;
431     } else {
432       result = DELETE_FAILED;
433     }
434   }
435
436   if (result == DELETE_REQUIRES_REBOOT) {
437     ScheduleParentAndGrandparentForDeletion(user_data_dir);
438   } else {
439     const base::FilePath user_data_dir(user_data_dir.DirName());
440     if (!user_data_dir.empty() &&
441         DeleteEmptyDir(user_data_dir) == DELETE_SUCCEEDED) {
442       const base::FilePath product_dir(user_data_dir.DirName());
443       if (!product_dir.empty())
444         DeleteEmptyDir(product_dir);
445     }
446   }
447
448   return result;
449 }
450
451 // Moves setup to a temporary file, outside of the install folder. Also attempts
452 // to change the current directory to the TMP directory. On Windows, each
453 // process has a handle to its CWD. If setup.exe's CWD happens to be within the
454 // install directory, deletion will fail as a result of the open handle.
455 bool MoveSetupOutOfInstallFolder(const InstallerState& installer_state,
456                                  const base::FilePath& setup_exe) {
457   // The list of files which setup.exe depends on at runtime. Typically this is
458   // solely setup.exe itself, but in component builds this also includes the
459   // DLLs installed by setup.exe.
460   std::vector<base::FilePath> setup_files;
461   setup_files.push_back(setup_exe);
462 #if defined(COMPONENT_BUILD)
463   base::FileEnumerator file_enumerator(
464       setup_exe.DirName(), false, base::FileEnumerator::FILES, L"*.dll");
465   for (base::FilePath setup_dll = file_enumerator.Next(); !setup_dll.empty();
466        setup_dll = file_enumerator.Next()) {
467     setup_files.push_back(setup_dll);
468   }
469 #endif  // defined(COMPONENT_BUILD)
470
471   base::FilePath tmp_dir;
472   base::FilePath temp_file;
473   if (!PathService::Get(base::DIR_TEMP, &tmp_dir)) {
474     NOTREACHED();
475     return false;
476   }
477
478   // Change the current directory to the TMP directory. See method comment above
479   // for details.
480   VLOG(1) << "Changing current directory to: " << tmp_dir.value();
481   if (!base::SetCurrentDirectory(tmp_dir))
482     PLOG(ERROR) << "Failed to change the current directory.";
483
484   for (std::vector<base::FilePath>::const_iterator it = setup_files.begin();
485        it != setup_files.end(); ++it) {
486     const base::FilePath& setup_file = *it;
487     if (!base::CreateTemporaryFileInDir(tmp_dir, &temp_file)) {
488       LOG(ERROR) << "Failed to create temporary file for "
489                  << setup_file.BaseName().value();
490       return false;
491     }
492
493     VLOG(1) << "Attempting to move " << setup_file.BaseName().value() << " to: "
494             << temp_file.value();
495     if (!base::Move(setup_file, temp_file)) {
496       PLOG(ERROR) << "Failed to move " << setup_file.BaseName().value()
497                   << " to " << temp_file.value();
498       return false;
499     }
500
501     // We cannot delete the file right away, but try to delete it some other
502     // way. Either with the help of a different process or the system.
503     if (!base::DeleteFileAfterReboot(temp_file)) {
504       const uint32 kDeleteAfterMs = 10 * 1000;
505       installer::DeleteFileFromTempProcess(temp_file, kDeleteAfterMs);
506     }
507   }
508   return true;
509 }
510
511 DeleteResult DeleteAppHostFilesAndFolders(const InstallerState& installer_state,
512                                           const Version& installed_version) {
513   const base::FilePath& target_path = installer_state.target_path();
514   if (target_path.empty()) {
515     LOG(ERROR) << "DeleteAppHostFilesAndFolders: no installation destination "
516                << "path.";
517     return DELETE_FAILED;  // Nothing else we can do to uninstall, so we return.
518   }
519
520   DeleteInstallTempDir(target_path);
521
522   DeleteResult result = DELETE_SUCCEEDED;
523
524   base::FilePath app_host_exe(target_path.Append(installer::kChromeAppHostExe));
525   if (!base::DeleteFile(app_host_exe, false)) {
526     result = DELETE_FAILED;
527     LOG(ERROR) << "Failed to delete path: " << app_host_exe.value();
528   }
529
530   return result;
531 }
532
533 DeleteResult DeleteChromeFilesAndFolders(const InstallerState& installer_state,
534                                          const base::FilePath& setup_exe) {
535   const base::FilePath& target_path = installer_state.target_path();
536   if (target_path.empty()) {
537     LOG(ERROR) << "DeleteChromeFilesAndFolders: no installation destination "
538                << "path.";
539     return DELETE_FAILED;  // Nothing else we can do to uninstall, so we return.
540   }
541
542   DeleteInstallTempDir(target_path);
543
544   DeleteResult result = DELETE_SUCCEEDED;
545
546   base::FilePath installer_directory;
547   if (target_path.IsParent(setup_exe))
548     installer_directory = setup_exe.DirName();
549
550   // Enumerate all the files in target_path recursively (breadth-first).
551   // We delete a file or folder unless it is a parent/child of the installer
552   // directory. For parents of the installer directory, we will later recurse
553   // and delete all the children (that are not also parents/children of the
554   // installer directory).
555   base::FileEnumerator file_enumerator(target_path, true,
556       base::FileEnumerator::FILES | base::FileEnumerator::DIRECTORIES);
557   for (base::FilePath to_delete = file_enumerator.Next(); !to_delete.empty();
558        to_delete = file_enumerator.Next()) {
559     if (to_delete.BaseName().value() == installer::kChromeAppHostExe)
560       continue;
561     if (!installer_directory.empty() &&
562         (to_delete == installer_directory ||
563          installer_directory.IsParent(to_delete) ||
564          to_delete.IsParent(installer_directory))) {
565       continue;
566     }
567
568     VLOG(1) << "Deleting install path " << to_delete.value();
569     if (!base::DeleteFile(to_delete, true)) {
570       LOG(ERROR) << "Failed to delete path (1st try): " << to_delete.value();
571       if (installer_state.FindProduct(BrowserDistribution::CHROME_FRAME)) {
572         // We don't try killing Chrome processes for Chrome Frame builds since
573         // that is unlikely to help. Instead, schedule files for deletion and
574         // return a value that will trigger a reboot prompt.
575         base::FileEnumerator::FileInfo find_info = file_enumerator.GetInfo();
576         if (find_info.IsDirectory())
577           ScheduleDirectoryForDeletion(to_delete);
578         else
579           ScheduleFileSystemEntityForDeletion(to_delete);
580         result = DELETE_REQUIRES_REBOOT;
581       } else {
582         // Try closing any running Chrome processes and deleting files once
583         // again.
584         CloseAllChromeProcesses();
585         if (!base::DeleteFile(to_delete, true)) {
586           LOG(ERROR) << "Failed to delete path (2nd try): "
587                      << to_delete.value();
588           result = DELETE_FAILED;
589           break;
590         }
591       }
592     }
593   }
594
595   return result;
596 }
597
598 // This method checks if Chrome is currently running or if the user has
599 // cancelled the uninstall operation by clicking Cancel on the confirmation
600 // box that Chrome pops up.
601 InstallStatus IsChromeActiveOrUserCancelled(
602     const InstallerState& installer_state,
603     const Product& product) {
604   int32 exit_code = content::RESULT_CODE_NORMAL_EXIT;
605   CommandLine options(CommandLine::NO_PROGRAM);
606   options.AppendSwitch(installer::switches::kUninstall);
607
608   // Here we want to save user from frustration (in case of Chrome crashes)
609   // and continue with the uninstallation as long as chrome.exe process exit
610   // code is NOT one of the following:
611   // - UNINSTALL_CHROME_ALIVE - chrome.exe is currently running
612   // - UNINSTALL_USER_CANCEL - User cancelled uninstallation
613   // - HUNG - chrome.exe was killed by HuntForZombieProcesses() (until we can
614   //          give this method some brains and not kill chrome.exe launched
615   //          by us, we will not uninstall if we get this return code).
616   VLOG(1) << "Launching Chrome to do uninstall tasks.";
617   if (product.LaunchChromeAndWait(installer_state.target_path(), options,
618                                   &exit_code)) {
619     VLOG(1) << "chrome.exe launched for uninstall confirmation returned: "
620             << exit_code;
621     if ((exit_code == chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE) ||
622         (exit_code == chrome::RESULT_CODE_UNINSTALL_USER_CANCEL) ||
623         (exit_code == content::RESULT_CODE_HUNG))
624       return installer::UNINSTALL_CANCELLED;
625
626     if (exit_code == chrome::RESULT_CODE_UNINSTALL_DELETE_PROFILE)
627       return installer::UNINSTALL_DELETE_PROFILE;
628   } else {
629     PLOG(ERROR) << "Failed to launch chrome.exe for uninstall confirmation.";
630   }
631
632   return installer::UNINSTALL_CONFIRMED;
633 }
634
635 bool ShouldDeleteProfile(const InstallerState& installer_state,
636                          const CommandLine& cmd_line, InstallStatus status,
637                          const Product& product) {
638   bool should_delete = false;
639
640   // Chrome Frame uninstallations always want to delete the profile (we have no
641   // UI to prompt otherwise and the profile stores no useful data anyway)
642   // unless they are managed by MSI. MSI uninstalls will explicitly include
643   // the --delete-profile flag to distinguish them from MSI upgrades.
644   if (product.is_chrome_frame() && !installer_state.is_msi()) {
645     should_delete = true;
646   } else {
647     should_delete =
648         status == installer::UNINSTALL_DELETE_PROFILE ||
649         cmd_line.HasSwitch(installer::switches::kDeleteProfile);
650   }
651
652   return should_delete;
653 }
654
655 // Removes XP-era filetype registration making Chrome the default browser.
656 // MSDN (see http://msdn.microsoft.com/library/windows/desktop/cc144148.aspx)
657 // tells us not to do this, but certain applications break following
658 // uninstallation if we don't.
659 void RemoveFiletypeRegistration(const InstallerState& installer_state,
660                                 HKEY root,
661                                 const base::string16& browser_entry_suffix) {
662   base::string16 classes_path(ShellUtil::kRegClasses);
663   classes_path.push_back(base::FilePath::kSeparators[0]);
664
665   BrowserDistribution* distribution = BrowserDistribution::GetDistribution();
666   const base::string16 prog_id(
667       distribution->GetBrowserProgIdPrefix() + browser_entry_suffix);
668
669   // Delete each filetype association if it references this Chrome.  Take care
670   // not to delete the association if it references a system-level install of
671   // Chrome (only a risk if the suffix is empty).  Don't delete the whole key
672   // since other apps may have stored data there.
673   std::vector<const wchar_t*> cleared_assocs;
674   if (installer_state.system_install() ||
675       !browser_entry_suffix.empty() ||
676       !base::win::RegKey(HKEY_LOCAL_MACHINE, (classes_path + prog_id).c_str(),
677                          KEY_QUERY_VALUE).Valid()) {
678     InstallUtil::ValueEquals prog_id_pred(prog_id);
679     for (const wchar_t* const* filetype =
680          &ShellUtil::kPotentialFileAssociations[0]; *filetype != NULL;
681          ++filetype) {
682       if (InstallUtil::DeleteRegistryValueIf(
683               root, (classes_path + *filetype).c_str(), NULL,
684               prog_id_pred) == InstallUtil::DELETED) {
685         cleared_assocs.push_back(*filetype);
686       }
687     }
688   }
689
690   // For all filetype associations in HKLM that have just been removed, attempt
691   // to restore some reasonable value.  We have no definitive way of knowing
692   // what handlers are the most appropriate, so we use a fixed mapping based on
693   // the default values for a fresh install of Windows.
694   if (root == HKEY_LOCAL_MACHINE) {
695     base::string16 assoc;
696     base::win::RegKey key;
697
698     for (size_t i = 0; i < cleared_assocs.size(); ++i) {
699       const wchar_t* replacement_prog_id = NULL;
700       assoc.assign(cleared_assocs[i]);
701
702       // Inelegant, but simpler than a pure data-driven approach.
703       if (assoc == L".htm" || assoc == L".html")
704         replacement_prog_id = L"htmlfile";
705       else if (assoc == L".xht" || assoc == L".xhtml")
706         replacement_prog_id = L"xhtmlfile";
707
708       if (!replacement_prog_id) {
709         LOG(WARNING) << "No known replacement ProgID for " << assoc
710                      << " files.";
711       } else if (key.Open(HKEY_LOCAL_MACHINE,
712                           (classes_path + replacement_prog_id).c_str(),
713                           KEY_QUERY_VALUE) == ERROR_SUCCESS &&
714                  (key.Open(HKEY_LOCAL_MACHINE, (classes_path + assoc).c_str(),
715                            KEY_SET_VALUE) != ERROR_SUCCESS ||
716                   key.WriteValue(NULL, replacement_prog_id) != ERROR_SUCCESS)) {
717         // The replacement ProgID is registered on the computer but the attempt
718         // to set it for the filetype failed.
719         LOG(ERROR) << "Failed to restore system-level filetype association "
720                    << assoc << " = " << replacement_prog_id;
721       }
722     }
723   }
724 }
725
726 // Builds and executes a work item list to remove DelegateExecute verb handler
727 // work items for |product|.  This will be a noop for products whose
728 // corresponding BrowserDistribution implementations do not publish a CLSID via
729 // GetCommandExecuteImplClsid.
730 bool ProcessDelegateExecuteWorkItems(const InstallerState& installer_state,
731                                      const Product& product) {
732   scoped_ptr<WorkItemList> item_list(WorkItem::CreateNoRollbackWorkItemList());
733   AddDelegateExecuteWorkItems(installer_state, base::FilePath(), Version(),
734                               product, item_list.get());
735   return item_list->Do();
736 }
737
738 // Removes Active Setup entries from the registry. This cannot be done through
739 // a work items list as usual because of different paths based on conditionals,
740 // but otherwise respects the no rollback/best effort uninstall mentality.
741 // This will only apply for system-level installs of Chrome/Chromium and will be
742 // a no-op for all other types of installs.
743 void UninstallActiveSetupEntries(const InstallerState& installer_state,
744                                  const Product& product) {
745   VLOG(1) << "Uninstalling registry entries for ActiveSetup.";
746   BrowserDistribution* distribution = product.distribution();
747
748   if (!product.is_chrome() || !installer_state.system_install()) {
749     const char* install_level =
750         installer_state.system_install() ? "system" : "user";
751     VLOG(1) << "No Active Setup processing to do for " << install_level
752             << "-level " << distribution->GetDisplayName();
753     return;
754   }
755
756   const base::string16 active_setup_path(
757       InstallUtil::GetActiveSetupPath(distribution));
758   InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, active_setup_path);
759
760   // Windows leaves keys behind in HKCU\\Software\\(Wow6432Node\\)?Microsoft\\
761   //     Active Setup\\Installed Components\\{guid}
762   // for every user that logged in since system-level Chrome was installed.
763   // This is a problem because Windows compares the value of the Version subkey
764   // in there with the value of the Version subkey in the matching HKLM entries
765   // before running Chrome's Active Setup so if Chrome was to be reinstalled
766   // with a lesser version (e.g. switching back to a more stable channel), the
767   // affected users would not have Chrome's Active Setup called until Chrome
768   // eventually updated passed that user's registered Version.
769   //
770   // It is however very hard to delete those values as the registry hives for
771   // other users are not loaded by default under HKEY_USERS (unless a user is
772   // logged on or has a process impersonating him).
773   //
774   // Following our best effort uninstall practices, try to delete the value in
775   // all users hives. If a given user's hive is not loaded, try to load it to
776   // proceed with the deletion (failure to do so is ignored).
777
778   static const wchar_t kProfileList[] =
779       L"Software\\Microsoft\\Windows NT\\CurrentVersion\\ProfileList\\";
780
781   // Windows automatically adds Wow6432Node when creating/deleting the HKLM key,
782   // but doesn't seem to do so when manually deleting the user-level keys it
783   // created.
784   base::string16 alternate_active_setup_path(active_setup_path);
785   alternate_active_setup_path.insert(arraysize("Software\\") - 1,
786                                      L"Wow6432Node\\");
787
788   // These two privileges are required by RegLoadKey() and RegUnloadKey() below.
789   ScopedTokenPrivilege se_restore_name_privilege(SE_RESTORE_NAME);
790   ScopedTokenPrivilege se_backup_name_privilege(SE_BACKUP_NAME);
791   if (!se_restore_name_privilege.is_enabled() ||
792       !se_backup_name_privilege.is_enabled()) {
793     // This is not a critical failure as those privileges aren't required to
794     // clean hives that are already loaded, but attempts to LoadRegKey() below
795     // will fail.
796     LOG(WARNING) << "Failed to enable privileges required to load registry "
797                     "hives.";
798   }
799
800   for (base::win::RegistryKeyIterator it(HKEY_LOCAL_MACHINE, kProfileList);
801        it.Valid(); ++it) {
802     const wchar_t* profile_sid = it.Name();
803
804     // First check if this user's registry hive needs to be loaded in
805     // HKEY_USERS.
806     base::win::RegKey user_reg_root_probe(
807         HKEY_USERS, profile_sid, KEY_READ);
808     bool loaded_hive = false;
809     if (!user_reg_root_probe.Valid()) {
810       VLOG(1) << "Attempting to load registry hive for " << profile_sid;
811
812       base::string16 reg_profile_info_path(kProfileList);
813       reg_profile_info_path.append(profile_sid);
814       base::win::RegKey reg_profile_info_key(
815           HKEY_LOCAL_MACHINE, reg_profile_info_path.c_str(), KEY_READ);
816
817       base::string16 profile_path;
818       LONG result = reg_profile_info_key.ReadValue(L"ProfileImagePath",
819                                                    &profile_path);
820       if (result != ERROR_SUCCESS) {
821         LOG(ERROR) << "Error reading ProfileImagePath: " << result;
822         continue;
823       }
824       base::FilePath registry_hive_file(profile_path);
825       registry_hive_file = registry_hive_file.AppendASCII("NTUSER.DAT");
826
827       result = RegLoadKey(HKEY_USERS, profile_sid,
828                           registry_hive_file.value().c_str());
829       if (result != ERROR_SUCCESS) {
830         LOG(ERROR) << "Error loading registry hive: " << result;
831         continue;
832       }
833
834       VLOG(1) << "Loaded registry hive for " << profile_sid;
835       loaded_hive = true;
836     }
837
838     base::win::RegKey user_reg_root(
839         HKEY_USERS, profile_sid, KEY_ALL_ACCESS);
840
841     LONG result = user_reg_root.DeleteKey(active_setup_path.c_str());
842     if (result != ERROR_SUCCESS) {
843       result = user_reg_root.DeleteKey(alternate_active_setup_path.c_str());
844       if (result != ERROR_SUCCESS && result != ERROR_FILE_NOT_FOUND) {
845         LOG(ERROR) << "Failed to delete key at " << active_setup_path
846                    << " and at " << alternate_active_setup_path
847                    << ", result: " << result;
848       }
849     }
850
851     if (loaded_hive) {
852       user_reg_root.Close();
853       if (RegUnLoadKey(HKEY_USERS, profile_sid) == ERROR_SUCCESS)
854         VLOG(1) << "Unloaded registry hive for " << profile_sid;
855       else
856         LOG(ERROR) << "Error unloading registry hive for " << profile_sid;
857     }
858   }
859 }
860
861 }  // namespace
862
863 DeleteResult DeleteChromeDirectoriesIfEmpty(
864     const base::FilePath& application_directory) {
865   DeleteResult result(DeleteEmptyDir(application_directory));
866   if (result == DELETE_SUCCEEDED) {
867     // Now check and delete if the parent directories are empty
868     // For example Google\Chrome or Chromium
869     const base::FilePath product_directory(application_directory.DirName());
870     if (!product_directory.empty()) {
871         result = DeleteEmptyDir(product_directory);
872         if (result == DELETE_SUCCEEDED) {
873           const base::FilePath vendor_directory(product_directory.DirName());
874           if (!vendor_directory.empty())
875             result = DeleteEmptyDir(vendor_directory);
876         }
877     }
878   }
879   if (result == DELETE_NOT_EMPTY)
880     result = DELETE_SUCCEEDED;
881   return result;
882 }
883
884 bool DeleteChromeRegistrationKeys(const InstallerState& installer_state,
885                                   BrowserDistribution* dist,
886                                   HKEY root,
887                                   const base::string16& browser_entry_suffix,
888                                   InstallStatus* exit_code) {
889   DCHECK(exit_code);
890   if (dist->GetDefaultBrowserControlPolicy() ==
891       BrowserDistribution::DEFAULT_BROWSER_UNSUPPORTED) {
892     // We should have never set those keys.
893     return true;
894   }
895
896   base::FilePath chrome_exe(installer_state.target_path().Append(kChromeExe));
897
898   // Delete Software\Classes\ChromeHTML.
899   const base::string16 prog_id(
900       dist->GetBrowserProgIdPrefix() + browser_entry_suffix);
901   base::string16 reg_prog_id(ShellUtil::kRegClasses);
902   reg_prog_id.push_back(base::FilePath::kSeparators[0]);
903   reg_prog_id.append(prog_id);
904   InstallUtil::DeleteRegistryKey(root, reg_prog_id);
905
906   // Delete Software\Classes\Chrome.
907   base::string16 reg_app_id(ShellUtil::kRegClasses);
908   reg_app_id.push_back(base::FilePath::kSeparators[0]);
909   // Append the requested suffix manually here (as ShellUtil::GetBrowserModelId
910   // would otherwise try to figure out the currently installed suffix).
911   reg_app_id.append(dist->GetBaseAppId() + browser_entry_suffix);
912   InstallUtil::DeleteRegistryKey(root, reg_app_id);
913
914   // Delete all Start Menu Internet registrations that refer to this Chrome.
915   {
916     using base::win::RegistryKeyIterator;
917     InstallUtil::ProgramCompare open_command_pred(chrome_exe);
918     base::string16 client_name;
919     base::string16 client_key;
920     base::string16 open_key;
921     for (RegistryKeyIterator iter(root, ShellUtil::kRegStartMenuInternet);
922          iter.Valid(); ++iter) {
923       client_name.assign(iter.Name());
924       client_key.assign(ShellUtil::kRegStartMenuInternet)
925           .append(1, L'\\')
926           .append(client_name);
927       open_key.assign(client_key).append(ShellUtil::kRegShellOpen);
928       if (InstallUtil::DeleteRegistryKeyIf(root, client_key, open_key, NULL,
929               open_command_pred) != InstallUtil::NOT_FOUND) {
930         // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
931         // references this Chrome (i.e., if it was made the default browser).
932         InstallUtil::DeleteRegistryValueIf(
933             root, ShellUtil::kRegStartMenuInternet, NULL,
934             InstallUtil::ValueEquals(client_name));
935         // Also delete the value for the default user if we're operating in
936         // HKLM.
937         if (root == HKEY_LOCAL_MACHINE) {
938           InstallUtil::DeleteRegistryValueIf(
939               HKEY_USERS,
940               base::string16(L".DEFAULT\\").append(
941                   ShellUtil::kRegStartMenuInternet).c_str(),
942               NULL, InstallUtil::ValueEquals(client_name));
943         }
944       }
945     }
946   }
947
948   // Delete Software\RegisteredApplications\Chromium
949   InstallUtil::DeleteRegistryValue(
950       root, ShellUtil::kRegRegisteredApplications,
951       dist->GetBaseAppName() + browser_entry_suffix);
952
953   // Delete the App Paths and Applications keys that let Explorer find Chrome:
954   // http://msdn.microsoft.com/en-us/library/windows/desktop/ee872121
955   base::string16 app_key(ShellUtil::kRegClasses);
956   app_key.push_back(base::FilePath::kSeparators[0]);
957   app_key.append(L"Applications");
958   app_key.push_back(base::FilePath::kSeparators[0]);
959   app_key.append(installer::kChromeExe);
960   InstallUtil::DeleteRegistryKey(root, app_key);
961
962   base::string16 app_path_key(ShellUtil::kAppPathsRegistryKey);
963   app_path_key.push_back(base::FilePath::kSeparators[0]);
964   app_path_key.append(installer::kChromeExe);
965   InstallUtil::DeleteRegistryKey(root, app_path_key);
966
967   // Cleanup OpenWithList and OpenWithProgids:
968   // http://msdn.microsoft.com/en-us/library/bb166549
969   base::string16 file_assoc_key;
970   base::string16 open_with_list_key;
971   base::string16 open_with_progids_key;
972   for (int i = 0; ShellUtil::kPotentialFileAssociations[i] != NULL; ++i) {
973     file_assoc_key.assign(ShellUtil::kRegClasses);
974     file_assoc_key.push_back(base::FilePath::kSeparators[0]);
975     file_assoc_key.append(ShellUtil::kPotentialFileAssociations[i]);
976     file_assoc_key.push_back(base::FilePath::kSeparators[0]);
977
978     open_with_list_key.assign(file_assoc_key);
979     open_with_list_key.append(L"OpenWithList");
980     open_with_list_key.push_back(base::FilePath::kSeparators[0]);
981     open_with_list_key.append(installer::kChromeExe);
982     InstallUtil::DeleteRegistryKey(root, open_with_list_key);
983
984     open_with_progids_key.assign(file_assoc_key);
985     open_with_progids_key.append(ShellUtil::kRegOpenWithProgids);
986     InstallUtil::DeleteRegistryValue(root, open_with_progids_key, prog_id);
987   }
988
989   // Cleanup in case Chrome had been made the default browser.
990
991   // Delete the default value of SOFTWARE\Clients\StartMenuInternet if it
992   // references this Chrome.  Do this explicitly here for the case where HKCU is
993   // being processed; the iteration above will have no hits since registration
994   // lives in HKLM.
995   InstallUtil::DeleteRegistryValueIf(
996       root, ShellUtil::kRegStartMenuInternet, NULL,
997       InstallUtil::ValueEquals(dist->GetBaseAppName() + browser_entry_suffix));
998
999   // Delete each protocol association if it references this Chrome.
1000   InstallUtil::ProgramCompare open_command_pred(chrome_exe);
1001   base::string16 parent_key(ShellUtil::kRegClasses);
1002   parent_key.push_back(base::FilePath::kSeparators[0]);
1003   const base::string16::size_type base_length = parent_key.size();
1004   base::string16 child_key;
1005   for (const wchar_t* const* proto =
1006            &ShellUtil::kPotentialProtocolAssociations[0];
1007        *proto != NULL;
1008        ++proto) {
1009     parent_key.resize(base_length);
1010     parent_key.append(*proto);
1011     child_key.assign(parent_key).append(ShellUtil::kRegShellOpen);
1012     InstallUtil::DeleteRegistryKeyIf(root, parent_key, child_key, NULL,
1013                                      open_command_pred);
1014   }
1015
1016   RemoveFiletypeRegistration(installer_state, root, browser_entry_suffix);
1017
1018   *exit_code = installer::UNINSTALL_SUCCESSFUL;
1019   return true;
1020 }
1021
1022 void RemoveChromeLegacyRegistryKeys(BrowserDistribution* dist,
1023                                     const base::string16& chrome_exe) {
1024   // We used to register Chrome to handle crx files, but this turned out
1025   // to be not worth the hassle. Remove these old registry entries if
1026   // they exist. See: http://codereview.chromium.org/210007
1027
1028 #if defined(GOOGLE_CHROME_BUILD)
1029 const wchar_t kChromeExtProgId[] = L"ChromeExt";
1030 #else
1031 const wchar_t kChromeExtProgId[] = L"ChromiumExt";
1032 #endif
1033
1034   HKEY roots[] = { HKEY_LOCAL_MACHINE, HKEY_CURRENT_USER };
1035   for (size_t i = 0; i < arraysize(roots); ++i) {
1036     base::string16 suffix;
1037     if (roots[i] == HKEY_LOCAL_MACHINE)
1038       suffix = ShellUtil::GetCurrentInstallationSuffix(dist, chrome_exe);
1039
1040     // Delete Software\Classes\ChromeExt,
1041     base::string16 ext_prog_id(ShellUtil::kRegClasses);
1042     ext_prog_id.push_back(base::FilePath::kSeparators[0]);
1043     ext_prog_id.append(kChromeExtProgId);
1044     ext_prog_id.append(suffix);
1045     InstallUtil::DeleteRegistryKey(roots[i], ext_prog_id);
1046
1047     // Delete Software\Classes\.crx,
1048     base::string16 ext_association(ShellUtil::kRegClasses);
1049     ext_association.append(L"\\");
1050     ext_association.append(L".crx");
1051     InstallUtil::DeleteRegistryKey(roots[i], ext_association);
1052   }
1053 }
1054
1055 void UninstallFirewallRules(BrowserDistribution* dist,
1056                             const base::FilePath& chrome_exe) {
1057   scoped_ptr<FirewallManager> manager =
1058       FirewallManager::Create(dist, chrome_exe);
1059   if (manager)
1060     manager->RemoveFirewallRules();
1061 }
1062
1063 InstallStatus UninstallProduct(const InstallationState& original_state,
1064                                const InstallerState& installer_state,
1065                                const base::FilePath& setup_exe,
1066                                const Product& product,
1067                                bool remove_all,
1068                                bool force_uninstall,
1069                                const CommandLine& cmd_line) {
1070   InstallStatus status = installer::UNINSTALL_CONFIRMED;
1071   BrowserDistribution* browser_dist = product.distribution();
1072   const base::string16 chrome_exe(
1073       installer_state.target_path().Append(installer::kChromeExe).value());
1074
1075   bool is_chrome = product.is_chrome();
1076
1077   VLOG(1) << "UninstallProduct: " << browser_dist->GetDisplayName();
1078
1079   if (force_uninstall) {
1080     // Since --force-uninstall command line option is used, we are going to
1081     // do silent uninstall. Try to close all running Chrome instances.
1082     // NOTE: We don't do this for Chrome Frame.
1083     if (is_chrome)
1084       CloseAllChromeProcesses();
1085   } else if (is_chrome) {
1086     // no --force-uninstall so lets show some UI dialog boxes.
1087     status = IsChromeActiveOrUserCancelled(installer_state, product);
1088     if (status != installer::UNINSTALL_CONFIRMED &&
1089         status != installer::UNINSTALL_DELETE_PROFILE)
1090       return status;
1091
1092     const base::string16 suffix(
1093         ShellUtil::GetCurrentInstallationSuffix(browser_dist, chrome_exe));
1094
1095     // Check if we need admin rights to cleanup HKLM (the conditions for
1096     // requiring a cleanup are the same as the conditions to do the actual
1097     // cleanup where DeleteChromeRegistrationKeys() is invoked for
1098     // HKEY_LOCAL_MACHINE below). If we do, try to launch another uninstaller
1099     // (silent) in elevated mode to do HKLM cleanup.
1100     // And continue uninstalling in the current process also to do HKCU cleanup.
1101     if (remove_all &&
1102         ShellUtil::QuickIsChromeRegisteredInHKLM(
1103             browser_dist, chrome_exe, suffix) &&
1104         !::IsUserAnAdmin() &&
1105         base::win::GetVersion() >= base::win::VERSION_VISTA &&
1106         !cmd_line.HasSwitch(installer::switches::kRunAsAdmin)) {
1107       CommandLine new_cmd(CommandLine::NO_PROGRAM);
1108       new_cmd.AppendArguments(cmd_line, true);
1109       // Append --run-as-admin flag to let the new instance of setup.exe know
1110       // that we already tried to launch ourselves as admin.
1111       new_cmd.AppendSwitch(installer::switches::kRunAsAdmin);
1112       // Append --remove-chrome-registration to remove registry keys only.
1113       new_cmd.AppendSwitch(installer::switches::kRemoveChromeRegistration);
1114       if (!suffix.empty()) {
1115         new_cmd.AppendSwitchNative(
1116             installer::switches::kRegisterChromeBrowserSuffix, suffix);
1117       }
1118       DWORD exit_code = installer::UNKNOWN_STATUS;
1119       InstallUtil::ExecuteExeAsAdmin(new_cmd, &exit_code);
1120     }
1121   }
1122
1123   if (is_chrome) {
1124     // Chrome is not in use so lets uninstall Chrome by deleting various files
1125     // and registry entries. Here we will just make best effort and keep going
1126     // in case of errors.
1127     ClearRlzProductState();
1128     // Delete the key that delegate_execute might make.
1129     if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
1130       InstallUtil::DeleteRegistryKey(HKEY_CURRENT_USER,
1131                                      chrome::kMetroRegistryPath);
1132     }
1133
1134     auto_launch_util::DisableAllAutoStartFeatures(
1135         base::ASCIIToUTF16(chrome::kInitialProfile));
1136
1137     // If user-level chrome is self-destructing as a result of encountering a
1138     // system-level chrome, retarget owned non-default shortcuts (app shortcuts,
1139     // profile shortcuts, etc.) to the system-level chrome.
1140     if (cmd_line.HasSwitch(installer::switches::kSelfDestruct) &&
1141         !installer_state.system_install()) {
1142       const base::FilePath system_chrome_path(
1143           GetChromeInstallPath(true, browser_dist).
1144               Append(installer::kChromeExe));
1145       VLOG(1) << "Retargeting user-generated Chrome shortcuts.";
1146       if (base::PathExists(system_chrome_path)) {
1147         RetargetUserShortcutsWithArgs(installer_state, product,
1148                                       base::FilePath(chrome_exe),
1149                                       system_chrome_path);
1150       } else {
1151         LOG(ERROR) << "Retarget failed: system-level Chrome not found.";
1152       }
1153     }
1154
1155     DeleteShortcuts(installer_state, product, base::FilePath(chrome_exe));
1156   }
1157
1158   // Delete the registry keys (Uninstall key and Version key).
1159   HKEY reg_root = installer_state.root_key();
1160
1161   // Note that we must retrieve the distribution-specific data before deleting
1162   // product.GetVersionKey().
1163   base::string16 distribution_data(browser_dist->GetDistributionData(reg_root));
1164
1165   // Remove Control Panel uninstall link.
1166   if (product.ShouldCreateUninstallEntry()) {
1167     InstallUtil::DeleteRegistryKey(reg_root,
1168                                    browser_dist->GetUninstallRegPath());
1169   }
1170
1171   // Remove Omaha product key.
1172   InstallUtil::DeleteRegistryKey(reg_root, browser_dist->GetVersionKey());
1173
1174   // Also try to delete the MSI value in the ClientState key (it might not be
1175   // there). This is due to a Google Update behaviour where an uninstall and a
1176   // rapid reinstall might result in stale values from the old ClientState key
1177   // being picked up on reinstall.
1178   product.SetMsiMarker(installer_state.system_install(), false);
1179
1180   InstallStatus ret = installer::UNKNOWN_STATUS;
1181
1182   if (is_chrome) {
1183     const base::string16 suffix(
1184         ShellUtil::GetCurrentInstallationSuffix(browser_dist, chrome_exe));
1185
1186     // Remove all Chrome registration keys.
1187     // Registration data is put in HKCU for both system level and user level
1188     // installs.
1189     DeleteChromeRegistrationKeys(installer_state, browser_dist,
1190                                  HKEY_CURRENT_USER, suffix, &ret);
1191
1192     // If the user's Chrome is registered with a suffix: it is possible that old
1193     // unsuffixed registrations were left in HKCU (e.g. if this install was
1194     // previously installed with no suffix in HKCU (old suffix rules if the user
1195     // is not an admin (or declined UAC at first run)) and later had to be
1196     // suffixed when fully registered in HKLM (e.g. when later making Chrome
1197     // default through the UI)).
1198     // Remove remaining HKCU entries with no suffix if any.
1199     if (!suffix.empty()) {
1200       DeleteChromeRegistrationKeys(installer_state, browser_dist,
1201                                    HKEY_CURRENT_USER, base::string16(), &ret);
1202
1203       // For similar reasons it is possible in very few installs (from
1204       // 21.0.1180.0 and fixed shortly after) to be installed with the new-style
1205       // suffix, but have some old-style suffix registrations left behind.
1206       base::string16 old_style_suffix;
1207       if (ShellUtil::GetOldUserSpecificRegistrySuffix(&old_style_suffix) &&
1208           suffix != old_style_suffix) {
1209         DeleteChromeRegistrationKeys(installer_state, browser_dist,
1210                                      HKEY_CURRENT_USER, old_style_suffix, &ret);
1211       }
1212     }
1213
1214     // Chrome is registered in HKLM for all system-level installs and for
1215     // user-level installs for which Chrome has been made the default browser.
1216     // Always remove the HKLM registration for system-level installs.  For
1217     // user-level installs, only remove it if both: 1) this uninstall isn't a
1218     // self destruct following the installation of a system-level Chrome
1219     // (because the system-level Chrome owns the HKLM registration now), and 2)
1220     // this user has made Chrome their default browser (i.e. has shell
1221     // integration entries registered with |suffix| (note: |suffix| will be the
1222     // empty string if required as it is obtained by
1223     // GetCurrentInstallationSuffix() above)).
1224     // TODO(gab): This can still leave parts of a suffixed install behind. To be
1225     // able to remove them we would need to be able to remove only suffixed
1226     // entries (as it is now some of the registry entries (e.g. App Paths) are
1227     // unsuffixed; thus removing suffixed installs is prohibited in HKLM if
1228     // !|remove_all| for now).
1229     if (installer_state.system_install() ||
1230         (remove_all &&
1231          ShellUtil::QuickIsChromeRegisteredInHKLM(
1232              browser_dist, chrome_exe, suffix))) {
1233       DeleteChromeRegistrationKeys(installer_state, browser_dist,
1234                                    HKEY_LOCAL_MACHINE, suffix, &ret);
1235     }
1236
1237     ProcessDelegateExecuteWorkItems(installer_state, product);
1238
1239     ProcessOnOsUpgradeWorkItems(installer_state, product);
1240
1241     UninstallActiveSetupEntries(installer_state, product);
1242
1243     UninstallFirewallRules(browser_dist, base::FilePath(chrome_exe));
1244
1245     // Notify the shell that associations have changed since Chrome was likely
1246     // unregistered.
1247     SHChangeNotify(SHCNE_ASSOCCHANGED, SHCNF_IDLIST, NULL, NULL);
1248
1249     // TODO(huangs): Implement actual migration code and remove the hack below.
1250     // Remove the "shadow" App Launcher registry keys.
1251     if (installer_state.is_multi_install()) {
1252       // If we're not uninstalling the legacy App Launcher, and if it was
1253       // not installed in the first place, then delete the "shadow" keys.
1254       chrome_launcher_support::InstallationState level_to_check =
1255           installer_state.system_install() ?
1256               chrome_launcher_support::INSTALLED_AT_SYSTEM_LEVEL :
1257               chrome_launcher_support::INSTALLED_AT_USER_LEVEL;
1258       bool has_legacy_app_launcher = level_to_check ==
1259           chrome_launcher_support::GetAppLauncherInstallationState();
1260       if (!has_legacy_app_launcher) {
1261         BrowserDistribution* shadow_app_launcher_dist =
1262             BrowserDistribution::GetSpecificDistribution(
1263                 BrowserDistribution::CHROME_APP_HOST);
1264         InstallUtil::DeleteRegistryKey(reg_root,
1265             shadow_app_launcher_dist->GetVersionKey());
1266       }
1267     }
1268   }
1269
1270   if (installer_state.is_multi_install())
1271     ProcessGoogleUpdateItems(original_state, installer_state, product);
1272
1273   // Get the state of the installed product (if any)
1274   const ProductState* product_state =
1275       original_state.GetProductState(installer_state.system_install(),
1276                                      browser_dist->GetType());
1277
1278   // Delete shared registry keys as well (these require admin rights) if
1279   // remove_all option is specified.
1280   if (remove_all) {
1281     if (!InstallUtil::IsChromeSxSProcess() && is_chrome) {
1282       // Delete media player registry key that exists only in HKLM.
1283       // We don't delete this key in SxS uninstall or Chrome Frame uninstall
1284       // as we never set the key for those products.
1285       base::string16 reg_path(installer::kMediaPlayerRegPath);
1286       reg_path.push_back(base::FilePath::kSeparators[0]);
1287       reg_path.append(installer::kChromeExe);
1288       InstallUtil::DeleteRegistryKey(HKEY_LOCAL_MACHINE, reg_path);
1289     }
1290
1291     // Unregister any dll servers that we may have registered for this
1292     // product.
1293     if (product_state != NULL) {
1294       std::vector<base::FilePath> com_dll_list;
1295       product.AddComDllList(&com_dll_list);
1296       base::FilePath dll_folder = installer_state.target_path().AppendASCII(
1297           product_state->version().GetString());
1298
1299       scoped_ptr<WorkItemList> unreg_work_item_list(
1300           WorkItem::CreateWorkItemList());
1301
1302       AddRegisterComDllWorkItems(dll_folder,
1303                                  com_dll_list,
1304                                  installer_state.system_install(),
1305                                  false,  // Unregister
1306                                  true,   // May fail
1307                                  unreg_work_item_list.get());
1308       unreg_work_item_list->Do();
1309     }
1310
1311     if (product.is_chrome_frame())
1312       ProcessIELowRightsPolicyWorkItems(installer_state);
1313   }
1314
1315   // Close any Chrome Frame helper processes that may be running.
1316   if (product.is_chrome_frame()) {
1317     VLOG(1) << "Closing the Chrome Frame helper process";
1318     CloseChromeFrameHelperProcess();
1319   }
1320
1321   if (product_state == NULL)
1322     return installer::UNINSTALL_SUCCESSFUL;
1323
1324   // Finally delete all the files from Chrome folder after moving setup.exe
1325   // and the user's Local State to a temp location.
1326   bool delete_profile = ShouldDeleteProfile(installer_state, cmd_line, status,
1327                                             product);
1328   ret = installer::UNINSTALL_SUCCESSFUL;
1329
1330   // When deleting files, we must make sure that we're either a "single"
1331   // (aka non-multi) installation or we are the Chrome Binaries.
1332
1333   base::FilePath user_data_dir(GetUserDataDir(product));
1334   base::FilePath backup_state_file;
1335   if (!user_data_dir.empty()) {
1336     backup_state_file = BackupLocalStateFile(user_data_dir);
1337   } else {
1338     LOG(ERROR) << "Could not retrieve the user's profile directory.";
1339     ret = installer::UNINSTALL_FAILED;
1340     delete_profile = false;
1341   }
1342
1343   if (product.is_chrome_app_host()) {
1344     DeleteAppHostFilesAndFolders(installer_state, product_state->version());
1345   } else if (!installer_state.is_multi_install() ||
1346              product.is_chrome_binaries()) {
1347     DeleteResult delete_result = DeleteChromeFilesAndFolders(
1348         installer_state, base::MakeAbsoluteFilePath(setup_exe));
1349     if (delete_result == DELETE_FAILED) {
1350       ret = installer::UNINSTALL_FAILED;
1351     } else if (delete_result == DELETE_REQUIRES_REBOOT) {
1352       ret = installer::UNINSTALL_REQUIRES_REBOOT;
1353     }
1354   }
1355
1356   if (delete_profile)
1357     DeleteUserDataDir(user_data_dir, product.is_chrome_frame());
1358
1359   if (!force_uninstall) {
1360     VLOG(1) << "Uninstallation complete. Launching post-uninstall operations.";
1361     browser_dist->DoPostUninstallOperations(product_state->version(),
1362         backup_state_file, distribution_data);
1363   }
1364
1365   // Try and delete the preserved local state once the post-install
1366   // operations are complete.
1367   if (!backup_state_file.empty())
1368     base::DeleteFile(backup_state_file, false);
1369
1370   return ret;
1371 }
1372
1373 void CleanUpInstallationDirectoryAfterUninstall(
1374     const InstallationState& original_state,
1375     const InstallerState& installer_state,
1376     const base::FilePath& setup_exe,
1377     InstallStatus* uninstall_status) {
1378   if (*uninstall_status != UNINSTALL_SUCCESSFUL &&
1379       *uninstall_status != UNINSTALL_REQUIRES_REBOOT) {
1380     return;
1381   }
1382   const base::FilePath target_path(installer_state.target_path());
1383   if (target_path.empty()) {
1384     LOG(ERROR) << "No installation destination path.";
1385     *uninstall_status = UNINSTALL_FAILED;
1386     return;
1387   }
1388   if (!target_path.IsParent(base::MakeAbsoluteFilePath(setup_exe))) {
1389     VLOG(1) << "setup.exe is not in target path. Skipping installer cleanup.";
1390     return;
1391   }
1392   base::FilePath install_directory(setup_exe.DirName());
1393
1394   bool remove_setup = true;
1395   bool remove_archive = true;
1396   CheckShouldRemoveSetupAndArchive(original_state, installer_state,
1397                                    &remove_setup, &remove_archive);
1398   if (!remove_archive)
1399     return;
1400
1401   if (remove_setup) {
1402     // In order to be able to remove the folder in which we're running, we
1403     // need to move setup.exe out of the install folder.
1404     // TODO(tommi): What if the temp folder is on a different volume?
1405     MoveSetupOutOfInstallFolder(installer_state, setup_exe);
1406   }
1407
1408   // Remove files from "...\<product>\Application\<version>\Installer"
1409   if (!RemoveInstallerFiles(install_directory, remove_setup)) {
1410     *uninstall_status = UNINSTALL_FAILED;
1411     return;
1412   }
1413
1414   if (!remove_setup)
1415     return;
1416
1417   // Try to remove the empty directory hierarchy.
1418
1419   // Delete "...\<product>\Application\<version>\Installer"
1420   if (DeleteEmptyDir(install_directory) != DELETE_SUCCEEDED) {
1421     *uninstall_status = UNINSTALL_FAILED;
1422     return;
1423   }
1424
1425   // Delete "...\<product>\Application\<version>"
1426   DeleteResult delete_result = DeleteEmptyDir(install_directory.DirName());
1427   if (delete_result == DELETE_FAILED ||
1428       (delete_result == DELETE_NOT_EMPTY &&
1429        *uninstall_status != UNINSTALL_REQUIRES_REBOOT)) {
1430     *uninstall_status = UNINSTALL_FAILED;
1431     return;
1432   }
1433
1434   if (*uninstall_status == UNINSTALL_REQUIRES_REBOOT) {
1435     // Delete the Application directory at reboot if empty.
1436     ScheduleFileSystemEntityForDeletion(target_path);
1437
1438     // If we need a reboot to continue, schedule the parent directories for
1439     // deletion unconditionally. If they are not empty, the session manager
1440     // will not delete them on reboot.
1441     ScheduleParentAndGrandparentForDeletion(target_path);
1442   } else if (DeleteChromeDirectoriesIfEmpty(target_path) == DELETE_FAILED) {
1443     *uninstall_status = UNINSTALL_FAILED;
1444   }
1445 }
1446
1447 }  // namespace installer