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