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