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.
5 #include "chrome/browser/chrome_browser_main_win.h"
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_module_verifier_win.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"
57 typedef HRESULT (STDAPICALLTYPE* RegisterApplicationRestartProc)(
58 const wchar_t* command_line,
61 void InitializeWindowProcExceptions() {
62 // Get the breakpad pointer from chrome.exe
63 base::win::WinProcExceptionFilter exception_filter =
64 reinterpret_cast<base::win::WinProcExceptionFilter>(
65 ::GetProcAddress(::GetModuleHandle(
66 chrome::kBrowserProcessExecutableName),
67 "CrashForException"));
68 exception_filter = base::win::SetWinProcExceptionFilter(exception_filter);
69 DCHECK(!exception_filter);
72 // gfx::Font callbacks
73 void AdjustUIFont(LOGFONT* logfont) {
74 l10n_util::AdjustUIFont(logfont);
77 int GetMinimumFontSize() {
79 base::StringToInt(l10n_util::GetStringUTF16(IDS_MINIMUM_UI_FONT_SIZE),
84 class TranslationDelegate : public installer::TranslationDelegate {
86 virtual string16 GetLocalizedString(int installer_string_id) OVERRIDE;
91 void ShowCloseBrowserFirstMessageBox() {
92 int message_id = IDS_UNINSTALL_CLOSE_APP;
93 if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
94 (ShellIntegration::GetDefaultBrowser() == ShellIntegration::IS_DEFAULT)) {
95 message_id = IDS_UNINSTALL_CLOSE_APP_IMMERSIVE;
97 chrome::ShowMessageBox(NULL,
98 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
99 l10n_util::GetStringUTF16(message_id),
100 chrome::MESSAGE_BOX_TYPE_WARNING);
103 int DoUninstallTasks(bool chrome_still_running) {
104 // We want to show a warning to user (and exit) if Chrome is already running
105 // *before* we show the uninstall confirmation dialog box. But while the
106 // uninstall confirmation dialog is up, user might start Chrome, so we
107 // check once again after user acknowledges Uninstall dialog.
108 if (chrome_still_running) {
109 ShowCloseBrowserFirstMessageBox();
110 return chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE;
112 int result = chrome::ShowUninstallBrowserPrompt(
113 !chrome_launcher_support::IsAppLauncherPresent());
114 // Don't offer to delete the profile if the App Launcher is also installed.
115 if (browser_util::IsBrowserAlreadyRunning()) {
116 ShowCloseBrowserFirstMessageBox();
117 return chrome::RESULT_CODE_UNINSTALL_CHROME_ALIVE;
120 if (result != chrome::RESULT_CODE_UNINSTALL_USER_CANCEL) {
121 // The following actions are just best effort.
122 VLOG(1) << "Executing uninstall actions";
123 if (!first_run::RemoveSentinel())
124 VLOG(1) << "Failed to delete sentinel file.";
125 base::FilePath chrome_exe;
126 if (PathService::Get(base::FILE_EXE, &chrome_exe)) {
127 ShellUtil::ShortcutLocation user_shortcut_locations[] = {
128 ShellUtil::SHORTCUT_LOCATION_DESKTOP,
129 ShellUtil::SHORTCUT_LOCATION_QUICK_LAUNCH,
130 ShellUtil::SHORTCUT_LOCATION_START_MENU,
132 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
133 for (size_t i = 0; i < arraysize(user_shortcut_locations); ++i) {
134 if (!ShellUtil::RemoveShortcuts(user_shortcut_locations[i], dist,
135 ShellUtil::CURRENT_USER, chrome_exe)) {
136 VLOG(1) << "Failed to delete shortcut at location "
137 << user_shortcut_locations[i];
147 void MaybeEnableHighResolutionTimeEverywhere() {
148 chrome::VersionInfo::Channel channel = chrome::VersionInfo::GetChannel();
149 bool user_enabled = CommandLine::ForCurrentProcess()->HasSwitch(
150 switches::kEnableHighResolutionTime);
151 if (user_enabled || channel == chrome::VersionInfo::CHANNEL_CANARY) {
152 bool is_enabled = base::TimeTicks::SetNowIsHighResNowIfSupported();
153 if (is_enabled && !user_enabled) {
154 // Ensure that all of the renderers will enable it too.
155 CommandLine::ForCurrentProcess()->AppendSwitch(
156 switches::kEnableHighResolutionTime);
161 // ChromeBrowserMainPartsWin ---------------------------------------------------
163 ChromeBrowserMainPartsWin::ChromeBrowserMainPartsWin(
164 const content::MainFunctionParams& parameters)
165 : ChromeBrowserMainParts(parameters) {
166 MaybeEnableHighResolutionTimeEverywhere();
167 if (base::win::IsMetroProcess()) {
168 typedef const wchar_t* (*GetMetroSwitches)(void);
169 GetMetroSwitches metro_switches_proc = reinterpret_cast<GetMetroSwitches>(
170 GetProcAddress(base::win::GetMetroModule(),
171 "GetMetroCommandLineSwitches"));
172 if (metro_switches_proc) {
173 string16 metro_switches = (*metro_switches_proc)();
174 if (!metro_switches.empty()) {
175 CommandLine extra_switches(CommandLine::NO_PROGRAM);
176 extra_switches.ParseFromString(metro_switches);
177 CommandLine::ForCurrentProcess()->AppendArguments(extra_switches,
184 ChromeBrowserMainPartsWin::~ChromeBrowserMainPartsWin() {
187 void ChromeBrowserMainPartsWin::ToolkitInitialized() {
188 ChromeBrowserMainParts::ToolkitInitialized();
189 gfx::PlatformFontWin::adjust_font_callback = &AdjustUIFont;
190 gfx::PlatformFontWin::get_minimum_font_size_callback = &GetMinimumFontSize;
191 #if defined(USE_AURA)
192 ui::CursorLoaderWin::SetCursorResourceModule(chrome::kBrowserResourcesDll);
196 void ChromeBrowserMainPartsWin::PreMainMessageLoopStart() {
197 // installer_util references strings that are normally compiled into
198 // setup.exe. In Chrome, these strings are in the locale files.
199 SetupInstallerUtilStrings();
201 ChromeBrowserMainParts::PreMainMessageLoopStart();
202 if (!parameters().ui_task) {
203 // Make sure that we know how to handle exceptions from the message loop.
204 InitializeWindowProcExceptions();
208 int ChromeBrowserMainPartsWin::PreCreateThreads() {
209 int rv = ChromeBrowserMainParts::PreCreateThreads();
211 // TODO(viettrungluu): why don't we run this earlier?
212 if (!parsed_command_line().HasSwitch(switches::kNoErrorDialogs) &&
213 base::win::GetVersion() < base::win::VERSION_XP) {
214 chrome::ShowMessageBox(NULL,
215 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
216 l10n_util::GetStringUTF16(IDS_UNSUPPORTED_OS_PRE_WIN_XP),
217 chrome::MESSAGE_BOX_TYPE_WARNING);
223 void ChromeBrowserMainPartsWin::ShowMissingLocaleMessageBox() {
224 ui::MessageBox(NULL, ASCIIToUTF16(chrome_browser::kMissingLocaleDataMessage),
225 ASCIIToUTF16(chrome_browser::kMissingLocaleDataTitle),
226 MB_OK | MB_ICONERROR | MB_TOPMOST);
229 void ChromeBrowserMainPartsWin::PostBrowserStart() {
230 ChromeBrowserMainParts::PostBrowserStart();
232 // Set up a task to verify installed modules in the current process. Use a
233 // delay to reduce the impact on startup time.
234 content::BrowserThread::GetMessageLoopProxyForThread(
235 content::BrowserThread::UI)->PostDelayedTask(
237 base::Bind(&BeginModuleVerification),
238 base::TimeDelta::FromSeconds(45));
242 void ChromeBrowserMainPartsWin::PrepareRestartOnCrashEnviroment(
243 const CommandLine& parsed_command_line) {
244 // Clear this var so child processes don't show the dialog by default.
245 scoped_ptr<base::Environment> env(base::Environment::Create());
246 env->UnSetVar(env_vars::kShowRestart);
248 // For non-interactive tests we don't restart on crash.
249 if (env->HasVar(env_vars::kHeadless))
252 // If the known command-line test options are used we don't create the
253 // environment block which means we don't get the restart dialog.
254 if (parsed_command_line.HasSwitch(switches::kBrowserCrashTest) ||
255 parsed_command_line.HasSwitch(switches::kBrowserAssertTest) ||
256 parsed_command_line.HasSwitch(switches::kNoErrorDialogs))
259 // The encoding we use for the info is "title|context|direction" where
260 // direction is either env_vars::kRtlLocale or env_vars::kLtrLocale depending
261 // on the current locale.
262 string16 dlg_strings(l10n_util::GetStringUTF16(IDS_CRASH_RECOVERY_TITLE));
263 dlg_strings.push_back('|');
264 string16 adjusted_string(
265 l10n_util::GetStringUTF16(IDS_CRASH_RECOVERY_CONTENT));
266 base::i18n::AdjustStringForLocaleDirection(&adjusted_string);
267 dlg_strings.append(adjusted_string);
268 dlg_strings.push_back('|');
269 dlg_strings.append(ASCIIToUTF16(
270 base::i18n::IsRTL() ? env_vars::kRtlLocale : env_vars::kLtrLocale));
272 env->SetVar(env_vars::kRestartInfo, UTF16ToUTF8(dlg_strings));
276 void ChromeBrowserMainPartsWin::RegisterApplicationRestart(
277 const CommandLine& parsed_command_line) {
278 DCHECK(base::win::GetVersion() >= base::win::VERSION_VISTA);
279 base::ScopedNativeLibrary library(base::FilePath(L"kernel32.dll"));
280 // Get the function pointer for RegisterApplicationRestart.
281 RegisterApplicationRestartProc register_application_restart =
282 reinterpret_cast<RegisterApplicationRestartProc>(
283 library.GetFunctionPointer("RegisterApplicationRestart"));
284 if (!register_application_restart) {
285 LOG(WARNING) << "Cannot find RegisterApplicationRestart in kernel32.dll";
288 // The Windows Restart Manager expects a string of command line flags only,
289 // without the program.
290 CommandLine command_line(CommandLine::NO_PROGRAM);
291 command_line.AppendArguments(parsed_command_line, false);
292 if (!command_line.HasSwitch(switches::kRestoreLastSession))
293 command_line.AppendSwitch(switches::kRestoreLastSession);
295 // Restart Chrome if the computer is restarted as the result of an update.
296 // This could be extended to handle crashes, hangs, and patches.
297 HRESULT hr = register_application_restart(
298 command_line.GetCommandLineString().c_str(),
299 RESTART_NO_CRASH | RESTART_NO_HANG | RESTART_NO_PATCH);
301 if (hr == E_INVALIDARG) {
302 LOG(WARNING) << "Command line too long for RegisterApplicationRestart";
304 NOTREACHED() << "RegisterApplicationRestart failed. hr: " << hr <<
305 ", command_line: " << command_line.GetCommandLineString();
311 int ChromeBrowserMainPartsWin::HandleIconsCommands(
312 const CommandLine& parsed_command_line) {
313 if (parsed_command_line.HasSwitch(switches::kHideIcons)) {
315 base::win::Version version = base::win::GetVersion();
316 if (version >= base::win::VERSION_VISTA) {
317 cp_applet.assign(L"Programs and Features"); // Windows Vista and later.
318 } else if (version >= base::win::VERSION_XP) {
319 cp_applet.assign(L"Add/Remove Programs"); // Windows XP.
321 return chrome::RESULT_CODE_UNSUPPORTED_PARAM; // Not supported
325 l10n_util::GetStringFUTF16(IDS_HIDE_ICONS_NOT_SUPPORTED, cp_applet);
326 const string16 caption = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
327 const UINT flags = MB_OKCANCEL | MB_ICONWARNING | MB_TOPMOST;
328 if (IDOK == ui::MessageBox(NULL, msg, caption, flags))
329 ShellExecute(NULL, NULL, L"appwiz.cpl", NULL, NULL, SW_SHOWNORMAL);
331 // Exit as we are not launching the browser.
332 return content::RESULT_CODE_NORMAL_EXIT;
334 // We don't hide icons so we shouldn't do anything special to show them
335 return chrome::RESULT_CODE_UNSUPPORTED_PARAM;
339 bool ChromeBrowserMainPartsWin::CheckMachineLevelInstall() {
340 // TODO(tommi): Check if using the default distribution is always the right
342 BrowserDistribution* dist = BrowserDistribution::GetDistribution();
344 InstallUtil::GetChromeVersion(dist, true, &version);
345 if (version.IsValid()) {
346 base::FilePath exe_path;
347 PathService::Get(base::DIR_EXE, &exe_path);
348 std::wstring exe = exe_path.value();
349 base::FilePath user_exe_path(installer::GetChromeInstallPath(false, dist));
350 if (base::FilePath::CompareEqualIgnoreCase(exe, user_exe_path.value())) {
351 bool is_metro = base::win::IsMetroProcess();
353 // The dialog cannot be shown in Win8 Metro as doing so hangs Chrome on
354 // an invisible dialog.
355 // TODO (gab): Get rid of this dialog altogether and auto-launch
356 // system-level Chrome instead.
357 const string16 text =
358 l10n_util::GetStringUTF16(IDS_MACHINE_LEVEL_INSTALL_CONFLICT);
359 const string16 caption = l10n_util::GetStringUTF16(IDS_PRODUCT_NAME);
360 const UINT flags = MB_OK | MB_ICONERROR | MB_TOPMOST;
361 ui::MessageBox(NULL, text, caption, flags);
363 CommandLine uninstall_cmd(
364 InstallUtil::GetChromeUninstallCmd(false, dist->GetType()));
365 if (!uninstall_cmd.GetProgram().empty()) {
366 uninstall_cmd.AppendSwitch(installer::switches::kSelfDestruct);
367 uninstall_cmd.AppendSwitch(installer::switches::kForceUninstall);
368 uninstall_cmd.AppendSwitch(
369 installer::switches::kDoNotRemoveSharedItems);
371 const base::FilePath setup_exe(uninstall_cmd.GetProgram());
372 const string16 params(uninstall_cmd.GetArgumentsString());
374 SHELLEXECUTEINFO sei = { sizeof(sei) };
375 sei.fMask = SEE_MASK_NOASYNC;
376 sei.nShow = SW_SHOWNORMAL;
377 sei.lpFile = setup_exe.value().c_str();
378 sei.lpParameters = params.c_str();
379 // On Windows 8 SEE_MASK_FLAG_LOG_USAGE is necessary to guarantee we
380 // flip to the Desktop when launching.
382 sei.fMask |= SEE_MASK_FLAG_LOG_USAGE;
384 if (!::ShellExecuteEx(&sei))
393 string16 TranslationDelegate::GetLocalizedString(int installer_string_id) {
395 switch (installer_string_id) {
396 // HANDLE_STRING is used by the DO_INSTALLER_STRING_MAPPING macro which is in
397 // the generated header installer_util_strings.h.
398 #define HANDLE_STRING(base_id, chrome_id) \
400 resource_id = chrome_id; \
402 DO_INSTALLER_STRING_MAPPING
408 return l10n_util::GetStringUTF16(resource_id);
413 void ChromeBrowserMainPartsWin::SetupInstallerUtilStrings() {
414 CR_DEFINE_STATIC_LOCAL(TranslationDelegate, delegate, ());
415 installer::SetTranslationDelegate(&delegate);