1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "chrome/browser/chrome_browser_main_win.h" 6 7#include <windows.h> 8#include <shellapi.h> 9 10#include <algorithm> 11 12#include "base/command_line.h" 13#include "base/environment.h" 14#include "base/files/file_path.h" 15#include "base/i18n/rtl.h" 16#include "base/memory/scoped_ptr.h" 17#include "base/path_service.h" 18#include "base/scoped_native_library.h" 19#include "base/strings/string_number_conversions.h" 20#include "base/strings/utf_string_conversions.h" 21#include "base/win/metro.h" 22#include "base/win/windows_version.h" 23#include "base/win/wrapped_window_proc.h" 24#include "chrome/browser/browser_util_win.h" 25#include "chrome/browser/profiles/profile_info_cache.h" 26#include "chrome/browser/profiles/profile_shortcut_manager.h" 27#include "chrome/browser/shell_integration.h" 28#include "chrome/browser/ui/simple_message_box.h" 29#include "chrome/browser/ui/uninstall_browser_prompt.h" 30#include "chrome/common/chrome_constants.h" 31#include "chrome/common/chrome_result_codes.h" 32#include "chrome/common/chrome_switches.h" 33#include "chrome/common/env_vars.h" 34#include "chrome/installer/launcher_support/chrome_launcher_support.h" 35#include "chrome/installer/util/browser_distribution.h" 36#include "chrome/installer/util/helper.h" 37#include "chrome/installer/util/install_util.h" 38#include "chrome/installer/util/l10n_string_util.h" 39#include "chrome/installer/util/shell_util.h" 40#include "content/public/common/main_function_params.h" 41#include "grit/app_locale_settings.h" 42#include "grit/chromium_strings.h" 43#include "grit/generated_resources.h" 44#include "installer_util_strings/installer_util_strings.h" 45#include "ui/base/cursor/cursor_loader_win.h" 46#include "ui/base/l10n/l10n_util.h" 47#include "ui/base/l10n/l10n_util_win.h" 48#include "ui/base/ui_base_switches.h" 49#include "ui/base/win/message_box_win.h" 50#include "ui/gfx/platform_font_win.h" 51 52namespace { 53 54typedef HRESULT (STDAPICALLTYPE* RegisterApplicationRestartProc)( 55 const wchar_t* command_line, 56 DWORD flags); 57 58void InitializeWindowProcExceptions() { 59 // Get the breakpad pointer from chrome.exe 60 base::win::WinProcExceptionFilter exception_filter = 61 reinterpret_cast<base::win::WinProcExceptionFilter>( 62 ::GetProcAddress(::GetModuleHandle( 63 chrome::kBrowserProcessExecutableName), 64 "CrashForException")); 65 exception_filter = base::win::SetWinProcExceptionFilter(exception_filter); 66 DCHECK(!exception_filter); 67} 68 69// gfx::Font callbacks 70void AdjustUIFont(LOGFONT* logfont) { 71 l10n_util::AdjustUIFont(logfont); 72} 73 74int GetMinimumFontSize() { 75 int min_font_size; 76 base::StringToInt(l10n_util::GetStringUTF16(IDS_MINIMUM_UI_FONT_SIZE), 77 &min_font_size); 78 return min_font_size; 79} 80 81class TranslationDelegate : public installer::TranslationDelegate { 82 public: 83 virtual string16 GetLocalizedString(int installer_string_id) OVERRIDE; 84}; 85 86} // namespace 87 88void ShowCloseBrowserFirstMessageBox() { 89 int message_id = IDS_UNINSTALL_CLOSE_APP; 90 if (base::win::GetVersion() >= base::win::VERSION_WIN8 && 91 (ShellIntegration::GetDefaultBrowser() == ShellIntegration::IS_DEFAULT)) { 92 message_id = IDS_UNINSTALL_CLOSE_APP_IMMERSIVE; 93 } 94 chrome::ShowMessageBox(NULL, 95 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 96 l10n_util::GetStringUTF16(message_id), 97 chrome::MESSAGE_BOX_TYPE_WARNING); 98} 99 100int DoUninstallTasks(bool chrome_still_running) { 101 // We want to show a warning to user (and exit) if Chrome is already running 102 // *before* we show the uninstall confirmation dialog box. But while the 103 // uninstall confirmation dialog is up, user might start Chrome, so we 104 // check once again after user acknowledges Uninstall dialog. 105 if (chrome_still_running) { 106 ShowCloseBrowserFirstMessageBox(); 107 return chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE; 108 } 109 int result = chrome::ShowUninstallBrowserPrompt( 110 !chrome_launcher_support::IsAppLauncherPresent()); 111 // Don't offer to delete the profile if the App Launcher is also installed. 112 if (browser_util::IsBrowserAlreadyRunning()) { 113 ShowCloseBrowserFirstMessageBox(); 114 return chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE; 115 } 116 117 if (result != chrome::RESULT_CODE_UNINSTALL_USER_CANCEL) { 118 // The following actions are just best effort. 119 VLOG(1) << "Executing uninstall actions"; 120 if (!first_run::RemoveSentinel()) 121 VLOG(1) << "Failed to delete sentinel file."; 122 base::FilePath chrome_exe; 123 if (PathService::Get(base::FILE_EXE, &chrome_exe)) { 124 ShellUtil::ShortcutLocation user_shortcut_locations[] = { 125 ShellUtil::SHORTCUT_LOCATION_DESKTOP, 126 ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH, 127 ShellUtil::SHORTCUT_LOCATION_START_MENU, 128 }; 129 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 130 for (size_t i = 0; i < arraysize(user_shortcut_locations); ++i) { 131 if (!ShellUtil::RemoveShortcuts(user_shortcut_locations[i], dist, 132 ShellUtil::CURRENT_USER, chrome_exe)) { 133 VLOG(1) << "Failed to delete shortcut at location " 134 << user_shortcut_locations[i]; 135 } 136 } 137 } else { 138 NOTREACHED(); 139 } 140 } 141 return result; 142} 143 144// ChromeBrowserMainPartsWin --------------------------------------------------- 145 146ChromeBrowserMainPartsWin::ChromeBrowserMainPartsWin( 147 const content::MainFunctionParams& parameters) 148 : ChromeBrowserMainParts(parameters) { 149 if (base::win::IsMetroProcess()) { 150 typedef const wchar_t* (*GetMetroSwitches)(void); 151 GetMetroSwitches metro_switches_proc = reinterpret_cast<GetMetroSwitches>( 152 GetProcAddress(base::win::GetMetroModule(), 153 "GetMetroCommandLineSwitches")); 154 if (metro_switches_proc) { 155 string16 metro_switches = (*metro_switches_proc)(); 156 if (!metro_switches.empty()) { 157 CommandLine extra_switches(CommandLine::NO_PROGRAM); 158 extra_switches.ParseFromString(metro_switches); 159 CommandLine::ForCurrentProcess()->AppendArguments(extra_switches, 160 false); 161 } 162 } 163 } 164} 165 166ChromeBrowserMainPartsWin::~ChromeBrowserMainPartsWin() { 167} 168 169void ChromeBrowserMainPartsWin::ToolkitInitialized() { 170 ChromeBrowserMainParts::ToolkitInitialized(); 171 gfx::PlatformFontWin::adjust_font_callback = &AdjustUIFont; 172 gfx::PlatformFontWin::get_minimum_font_size_callback = &GetMinimumFontSize; 173#if defined(USE_AURA) 174 ui::CursorLoaderWin::SetCursorResourceModule(chrome::kBrowserResourcesDll); 175#endif 176} 177 178void ChromeBrowserMainPartsWin::PreMainMessageLoopStart() { 179 // installer_util references strings that are normally compiled into 180 // setup.exe. In Chrome, these strings are in the locale files. 181 SetupInstallerUtilStrings(); 182 183 ChromeBrowserMainParts::PreMainMessageLoopStart(); 184 if (!parameters().ui_task) { 185 // Make sure that we know how to handle exceptions from the message loop. 186 InitializeWindowProcExceptions(); 187 } 188} 189 190int ChromeBrowserMainPartsWin::PreCreateThreads() { 191 int rv = ChromeBrowserMainParts::PreCreateThreads(); 192 193 // TODO(viettrungluu): why don't we run this earlier? 194 if (!parsed_command_line().HasSwitch(switches::kNoErrorDialogs) && 195 base::win::GetVersion() < base::win::VERSION_XP) { 196 chrome::ShowMessageBox(NULL, 197 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 198 l10n_util::GetStringUTF16(IDS_UNSUPPORTED_OS_PRE_WIN_XP), 199 chrome::MESSAGE_BOX_TYPE_WARNING); 200 } 201 202 return rv; 203} 204 205void ChromeBrowserMainPartsWin::ShowMissingLocaleMessageBox() { 206 ui::MessageBox(NULL, ASCIIToUTF16(chrome_browser::kMissingLocaleDataMessage), 207 ASCIIToUTF16(chrome_browser::kMissingLocaleDataTitle), 208 MB_OK | MB_ICONERROR | MB_TOPMOST); 209} 210 211// static 212void ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment( 213 const CommandLine& parsed_command_line) { 214 // Clear this var so child processes don't show the dialog by default. 215 scoped_ptr<base::Environment> env(base::Environment::Create()); 216 env->UnSetVar(env_vars::kShowRestart); 217 218 // For non-interactive tests we don't restart on crash. 219 if (env->HasVar(env_vars::kHeadless)) 220 return; 221 222 // If the known command-line test options are used we don't create the 223 // environment block which means we don't get the restart dialog. 224 if (parsed_command_line.HasSwitch(switches::kBrowserCrashTest) || 225 parsed_command_line.HasSwitch(switches::kBrowserAssertTest) || 226 parsed_command_line.HasSwitch(switches::kNoErrorDialogs)) 227 return; 228 229 // The encoding we use for the info is "title|context|direction" where 230 // direction is either env_vars::kRtlLocale or env_vars::kLtrLocale depending 231 // on the current locale. 232 string16 dlg_strings(l10n_util::GetStringUTF16(IDS_CRASH_RECOVERY_TITLE)); 233 dlg_strings.push_back('|'); 234 string16 adjusted_string( 235 l10n_util::GetStringUTF16(IDS_CRASH_RECOVERY_CONTENT)); 236 base::i18n::AdjustStringForLocaleDirection(&adjusted_string); 237 dlg_strings.append(adjusted_string); 238 dlg_strings.push_back('|'); 239 dlg_strings.append(ASCIIToUTF16( 240 base::i18n::IsRTL() ? env_vars::kRtlLocale : env_vars::kLtrLocale)); 241 242 env->SetVar(env_vars::kRestartInfo, UTF16ToUTF8(dlg_strings)); 243} 244 245// static 246void ChromeBrowserMainPartsWin::RegisterApplicationRestart( 247 const CommandLine& parsed_command_line) { 248 DCHECK(base::win::GetVersion() >= base::win::VERSION_VISTA); 249 base::ScopedNativeLibrary library(base::FilePath(L"kernel32.dll")); 250 // Get the function pointer for RegisterApplicationRestart. 251 RegisterApplicationRestartProc register_application_restart = 252 reinterpret_cast<RegisterApplicationRestartProc>( 253 library.GetFunctionPointer("RegisterApplicationRestart")); 254 if (!register_application_restart) { 255 LOG(WARNING) << "Cannot find RegisterApplicationRestart in kernel32.dll"; 256 return; 257 } 258 // The Windows Restart Manager expects a string of command line flags only, 259 // without the program. 260 CommandLine command_line(CommandLine::NO_PROGRAM); 261 command_line.AppendArguments(parsed_command_line, false); 262 if (!command_line.HasSwitch(switches::kRestoreLastSession)) 263 command_line.AppendSwitch(switches::kRestoreLastSession); 264 265 // Restart Chrome if the computer is restarted as the result of an update. 266 // This could be extended to handle crashes, hangs, and patches. 267 HRESULT hr = register_application_restart( 268 command_line.GetCommandLineString().c_str(), 269 RESTART_NO_CRASH | RESTART_NO_HANG | RESTART_NO_PATCH); 270 if (FAILED(hr)) { 271 if (hr == E_INVALIDARG) { 272 LOG(WARNING) << "Command line too long for RegisterApplicationRestart"; 273 } else { 274 NOTREACHED() << "RegisterApplicationRestart failed. hr: " << hr << 275 ", command_line: " << command_line.GetCommandLineString(); 276 } 277 } 278} 279 280// static 281int ChromeBrowserMainPartsWin::HandleIconsCommands( 282 const CommandLine& parsed_command_line) { 283 if (parsed_command_line.HasSwitch(switches::kHideIcons)) { 284 string16 cp_applet; 285 base::win::Version version = base::win::GetVersion(); 286 if (version >= base::win::VERSION_VISTA) { 287 cp_applet.assign(L"Programs and Features"); // Windows Vista and later. 288 } else if (version >= base::win::VERSION_XP) { 289 cp_applet.assign(L"Add/Remove Programs"); // Windows XP. 290 } else { 291 return chrome::RESULT_CODE_UNSUPPORTED_PARAM; // Not supported 292 } 293 294 const string16 msg = 295 l10n_util::GetStringFUTF16(IDS_HIDE_ICONS_NOT_SUPPORTED, cp_applet); 296 const string16 caption = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME); 297 const UINT flags = MB_OKCANCEL | MB_ICONWARNING | MB_TOPMOST; 298 if (IDOK == ui::MessageBox(NULL, msg, caption, flags)) 299 ShellExecute(NULL, NULL, L"appwiz.cpl", NULL, NULL, SW_SHOWNORMAL); 300 301 // Exit as we are not launching the browser. 302 return content::RESULT_CODE_NORMAL_EXIT; 303 } 304 // We don't hide icons so we shouldn't do anything special to show them 305 return chrome::RESULT_CODE_UNSUPPORTED_PARAM; 306} 307 308// static 309bool ChromeBrowserMainPartsWin::CheckMachineLevelInstall() { 310 // TODO(tommi): Check if using the default distribution is always the right 311 // thing to do. 312 BrowserDistribution* dist = BrowserDistribution::GetDistribution(); 313 Version version; 314 InstallUtil::GetChromeVersion(dist, true, &version); 315 if (version.IsValid()) { 316 base::FilePath exe_path; 317 PathService::Get(base::DIR_EXE, &exe_path); 318 std::wstring exe = exe_path.value(); 319 base::FilePath user_exe_path(installer::GetChromeInstallPath(false, dist)); 320 if (base::FilePath::CompareEqualIgnoreCase(exe, user_exe_path.value())) { 321 bool is_metro = base::win::IsMetroProcess(); 322 if (!is_metro) { 323 // The dialog cannot be shown in Win8 Metro as doing so hangs Chrome on 324 // an invisible dialog. 325 // TODO (gab): Get rid of this dialog altogether and auto-launch 326 // system-level Chrome instead. 327 const string16 text = 328 l10n_util::GetStringUTF16(IDS_MACHINE_LEVEL_INSTALL_CONFLICT); 329 const string16 caption = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME); 330 const UINT flags = MB_OK | MB_ICONERROR | MB_TOPMOST; 331 ui::MessageBox(NULL, text, caption, flags); 332 } 333 CommandLine uninstall_cmd( 334 InstallUtil::GetChromeUninstallCmd(false, dist->GetType())); 335 if (!uninstall_cmd.GetProgram().empty()) { 336 uninstall_cmd.AppendSwitch(installer::switches::kSelfDestruct); 337 uninstall_cmd.AppendSwitch(installer::switches::kForceUninstall); 338 uninstall_cmd.AppendSwitch( 339 installer::switches::kDoNotRemoveSharedItems); 340 341 const base::FilePath setup_exe(uninstall_cmd.GetProgram()); 342 const string16 params(uninstall_cmd.GetArgumentsString()); 343 344 SHELLEXECUTEINFO sei = { sizeof(sei) }; 345 sei.fMask = SEE_MASK_NOASYNC; 346 sei.nShow = SW_SHOWNORMAL; 347 sei.lpFile = setup_exe.value().c_str(); 348 sei.lpParameters = params.c_str(); 349 // On Windows 8 SEE_MASK_FLAG_LOG_USAGE is necessary to guarantee we 350 // flip to the Desktop when launching. 351 if (is_metro) 352 sei.fMask |= SEE_MASK_FLAG_LOG_USAGE; 353 354 if (!::ShellExecuteEx(&sei)) 355 DPCHECK(false); 356 } 357 return true; 358 } 359 } 360 return false; 361} 362 363string16 TranslationDelegate::GetLocalizedString(int installer_string_id) { 364 int resource_id = 0; 365 switch (installer_string_id) { 366 // HANDLE_STRING is used by the DO_INSTALLER_STRING_MAPPING macro which is in 367 // the generated header installer_util_strings.h. 368#define HANDLE_STRING(base_id, chrome_id) \ 369 case base_id: \ 370 resource_id = chrome_id; \ 371 break; 372 DO_INSTALLER_STRING_MAPPING 373#undef HANDLE_STRING 374 default: 375 NOTREACHED(); 376 } 377 if (resource_id) 378 return l10n_util::GetStringUTF16(resource_id); 379 return string16(); 380} 381 382// static 383void ChromeBrowserMainPartsWin::SetupInstallerUtilStrings() { 384 CR_DEFINE_STATIC_LOCAL(TranslationDelegate, delegate, ()); 385 installer::SetTranslationDelegate(&delegate); 386} 387