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