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