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