blob: d7e3609dc6cb5c640a4c35c9d410da7a98c23625 [file] [log] [blame]
[email protected]e3ce40a2012-01-31 03:03:031// Copyright (c) 2012 The Chromium Authors. All rights reserved.
[email protected]fc14cef2009-01-27 22:17:292// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]7c47ae3e2009-02-18 00:34:215#include "chrome/browser/process_singleton.h"
[email protected]fc14cef2009-01-27 22:17:296
[email protected]b1d7844b2012-08-22 22:37:477#include <shellapi.h>
[email protected]6927f2e02012-09-18 00:13:498#include <shobjidl.h>
[email protected]b1d7844b2012-08-22 22:37:479
[email protected]fc14cef2009-01-27 22:17:2910#include "base/base_paths.h"
11#include "base/command_line.h"
[email protected]f805fe82010-08-03 22:47:1012#include "base/file_path.h"
[email protected]9e9b6e8e2009-12-02 08:45:0113#include "base/path_service.h"
[email protected]fc14cef2009-01-27 22:17:2914#include "base/process_util.h"
[email protected]f394bc62012-08-22 21:42:2915#include "base/stringprintf.h"
[email protected]0f26d7b2011-01-05 19:10:4416#include "base/utf_string_conversions.h"
[email protected]f394bc62012-08-22 21:42:2917#include "base/win/metro.h"
[email protected]b3d791c92012-10-05 19:01:2418#include "base/win/registry.h"
[email protected]6927f2e02012-09-18 00:13:4919#include "base/win/scoped_com_initializer.h"
20#include "base/win/scoped_comptr.h"
[email protected]b90d7e802011-01-09 16:32:2021#include "base/win/scoped_handle.h"
[email protected]b3d791c92012-10-05 19:01:2422#include "base/win/win_util.h"
[email protected]6927f2e02012-09-18 00:13:4923#include "base/win/windows_version.h"
[email protected]ecb924c2011-03-17 00:34:0924#include "base/win/wrapped_window_proc.h"
[email protected]245900672012-10-13 02:27:1125#include "chrome/browser/shell_integration.h"
[email protected]b50892c5f2012-05-13 07:34:1426#include "chrome/browser/ui/simple_message_box.h"
[email protected]fc14cef2009-01-27 22:17:2927#include "chrome/common/chrome_constants.h"
[email protected]b3d791c92012-10-05 19:01:2428#include "chrome/common/chrome_paths.h"
29#include "chrome/common/chrome_paths_internal.h"
[email protected]cfa118a2012-10-10 22:27:5330#include "chrome/common/chrome_switches.h"
[email protected]6927f2e02012-09-18 00:13:4931#include "chrome/installer/util/browser_distribution.h"
[email protected]786799692012-09-26 14:16:4832#include "chrome/installer/util/install_util.h"
[email protected]6927f2e02012-09-18 00:13:4933#include "chrome/installer/util/shell_util.h"
[email protected]0a194552011-09-14 17:53:3534#include "chrome/installer/util/wmi.h"
[email protected]b39ef1cb2011-10-25 04:46:5535#include "content/public/common/result_codes.h"
[email protected]34ac8f32009-02-22 23:03:2736#include "grit/chromium_strings.h"
37#include "grit/generated_resources.h"
[email protected]f394bc62012-08-22 21:42:2938#include "net/base/escape.h"
[email protected]c051a1b2011-01-21 23:30:1739#include "ui/base/l10n/l10n_util.h"
[email protected]e7661062011-01-19 22:16:5340#include "ui/base/win/hwnd_util.h"
[email protected]fc14cef2009-01-27 22:17:2941
42namespace {
43
[email protected]2b7ff5b92012-07-17 22:45:1844const char kLockfile[] = "lockfile";
45
[email protected]f394bc62012-08-22 21:42:2946const char kSearchUrl[] =
47 "http://www.google.com/search?q=%s&sourceid=chrome&ie=UTF-8";
48
[email protected]b3d791c92012-10-05 19:01:2449const int kImmersiveChromeInitTimeout = 500;
50
[email protected]f891fb32009-04-08 00:20:3251// Checks the visibility of the enumerated window and signals once a visible
[email protected]fc14cef2009-01-27 22:17:2952// window has been found.
53BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
54 bool* result = reinterpret_cast<bool*>(param);
55 *result = IsWindowVisible(window) != 0;
56 // Stops enumeration if a visible window has been found.
57 return !*result;
58}
59
[email protected]e3ce40a2012-01-31 03:03:0360// This function thunks to the object's version of the windowproc, taking in
61// consideration that there are several messages being dispatched before
62// WM_NCCREATE which we let windows handle.
63LRESULT CALLBACK ThunkWndProc(HWND hwnd, UINT message,
64 WPARAM wparam, LPARAM lparam) {
65 ProcessSingleton* singleton =
66 reinterpret_cast<ProcessSingleton*>(ui::GetWindowUserData(hwnd));
67 if (message == WM_NCCREATE) {
68 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lparam);
69 singleton = reinterpret_cast<ProcessSingleton*>(cs->lpCreateParams);
70 CHECK(singleton);
71 ui::SetWindowUserData(hwnd, singleton);
72 } else if (!singleton) {
73 return ::DefWindowProc(hwnd, message, wparam, lparam);
74 }
75 return singleton->WndProc(hwnd, message, wparam, lparam);
76}
77
[email protected]edf04b512012-02-23 09:47:4378bool ParseCommandLine(const COPYDATASTRUCT* cds,
79 CommandLine* parsed_command_line,
80 FilePath* current_directory) {
81 // We should have enough room for the shortest command (min_message_size)
82 // and also be a multiple of wchar_t bytes. The shortest command
83 // possible is L"START\0\0" (empty current directory and command line).
84 static const int min_message_size = 7;
85 if (cds->cbData < min_message_size * sizeof(wchar_t) ||
86 cds->cbData % sizeof(wchar_t) != 0) {
87 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
88 return false;
89 }
90
91 // We split the string into 4 parts on NULLs.
92 DCHECK(cds->lpData);
93 const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
94 cds->cbData / sizeof(wchar_t));
95 const std::wstring::size_type first_null = msg.find_first_of(L'\0');
96 if (first_null == 0 || first_null == std::wstring::npos) {
97 // no NULL byte, don't know what to do
98 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
99 ", first null = " << first_null;
100 return false;
101 }
102
103 // Decode the command, which is everything until the first NULL.
104 if (msg.substr(0, first_null) == L"START") {
105 // Another instance is starting parse the command line & do what it would
106 // have done.
107 VLOG(1) << "Handling STARTUP request from another process";
108 const std::wstring::size_type second_null =
109 msg.find_first_of(L'\0', first_null + 1);
110 if (second_null == std::wstring::npos ||
111 first_null == msg.length() - 1 || second_null == msg.length()) {
112 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
113 "parts separated by NULLs";
114 return false;
115 }
116
117 // Get current directory.
118 *current_directory = FilePath(msg.substr(first_null + 1,
119 second_null - first_null));
120
121 const std::wstring::size_type third_null =
122 msg.find_first_of(L'\0', second_null + 1);
123 if (third_null == std::wstring::npos ||
124 third_null == msg.length()) {
125 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
126 "parts separated by NULLs";
127 }
128
129 // Get command line.
130 const std::wstring cmd_line =
131 msg.substr(second_null + 1, third_null - second_null);
132 *parsed_command_line = CommandLine::FromString(cmd_line);
133 return true;
134 }
135 return false;
136}
137
[email protected]6927f2e02012-09-18 00:13:49138bool ActivateMetroChrome() {
139 FilePath chrome_exe;
140 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
141 NOTREACHED() << "Failed to get chrome exe path";
142 return false;
143 }
144 string16 app_id = ShellUtil::GetBrowserModelId(
[email protected]786799692012-09-26 14:16:48145 BrowserDistribution::GetDistribution(),
146 InstallUtil::IsPerUserInstall(chrome_exe.value().c_str()));
[email protected]6927f2e02012-09-18 00:13:49147 if (app_id.empty()) {
148 NOTREACHED() << "Failed to get chrome app user model id.";
149 return false;
150 }
151
152 base::win::ScopedComPtr<IApplicationActivationManager> activation_manager;
153 HRESULT hr = activation_manager.CreateInstance(
154 CLSID_ApplicationActivationManager);
155 if (!activation_manager) {
156 NOTREACHED() << "Failed to cocreate activation manager. Error: " << hr;
157 return false;
158 }
159
160 unsigned long pid = 0;
161 hr = activation_manager->ActivateApplication(app_id.c_str(),
162 L"open",
163 AO_NONE,
164 &pid);
165 if (FAILED(hr)) {
166 NOTREACHED() << "Failed to activate metro chrome. Error: " << hr;
167 return false;
168 }
169 return true;
170}
171
[email protected]b3d791c92012-10-05 19:01:24172// Returns true if Chrome needs to be relaunched into Windows 8 immersive mode.
173// Following conditions apply:-
174// 1. Windows 8 or greater.
175// 2. Not in Windows 8 immersive mode.
[email protected]245900672012-10-13 02:27:11176// 3. Chrome is default browser.
177// 4. Process integrity level is not high.
178// 5. The profile data directory is the default directory.
179// 6. Last used mode was immersive/machine is a tablet.
[email protected]b3d791c92012-10-05 19:01:24180// TODO(ananta)
181// Move this function to a common place as the Windows 8 delegate_execute
182// handler can possibly use this.
183bool ShouldLaunchInWindows8ImmersiveMode(const FilePath& user_data_dir) {
184#if defined(USE_AURA)
185 return false;
186#endif
187
188 if (base::win::GetVersion() < base::win::VERSION_WIN8)
189 return false;
190
191 if (base::win::IsProcessImmersive(base::GetCurrentProcessHandle()))
192 return false;
193
[email protected]245900672012-10-13 02:27:11194 if (!ShellIntegration::IsDefaultBrowser())
195 return false;
196
[email protected]b3d791c92012-10-05 19:01:24197 base::IntegrityLevel integrity_level = base::INTEGRITY_UNKNOWN;
198 base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(),
199 &integrity_level);
200 if (integrity_level == base::HIGH_INTEGRITY)
201 return false;
202
203 FilePath default_user_data_dir;
204 if (!chrome::GetDefaultUserDataDirectory(&default_user_data_dir))
205 return false;
206
207 if (default_user_data_dir != user_data_dir)
208 return false;
209
[email protected]cfa118a2012-10-10 22:27:53210 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUninstall))
211 return false;
212
[email protected]b3d791c92012-10-05 19:01:24213 base::win::RegKey reg_key;
214 LONG key_result = reg_key.Create(HKEY_CURRENT_USER,
215 chrome::kMetroRegistryPath,
216 KEY_READ);
217 if (key_result == ERROR_SUCCESS) {
218 DWORD reg_value = 0;
[email protected]cfa118a2012-10-10 22:27:53219 if (reg_key.ReadValueDW(chrome::kLaunchModeValue, &reg_value)
220 == ERROR_SUCCESS) {
221 return reg_value == 1;
222 }
[email protected]b3d791c92012-10-05 19:01:24223 }
224 return base::win::IsMachineATablet();
225}
226
[email protected]fc14cef2009-01-27 22:17:29227} // namespace
228
[email protected]0a194552011-09-14 17:53:35229// Microsoft's Softricity virtualization breaks the sandbox processes.
230// So, if we detect the Softricity DLL we use WMI Win32_Process.Create to
231// break out of the virtualization environment.
232// http://code.google.com/p/chromium/issues/detail?id=43650
233bool ProcessSingleton::EscapeVirtualization(const FilePath& user_data_dir) {
234 if (::GetModuleHandle(L"sftldr_wow64.dll") ||
235 ::GetModuleHandle(L"sftldr.dll")) {
236 int process_id;
237 if (!installer::WMIProcess::Launch(GetCommandLineW(), &process_id))
238 return false;
239 is_virtualized_ = true;
240 // The new window was spawned from WMI, and won't be in the foreground.
241 // So, first we sleep while the new chrome.exe instance starts (because
242 // WaitForInputIdle doesn't work here). Then we poll for up to two more
243 // seconds and make the window foreground if we find it (or we give up).
244 HWND hwnd = 0;
245 ::Sleep(90);
246 for (int tries = 200; tries; --tries) {
247 hwnd = FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass,
248 user_data_dir.value().c_str());
249 if (hwnd) {
250 ::SetForegroundWindow(hwnd);
251 break;
252 }
253 ::Sleep(10);
254 }
255 return true;
256 }
257 return false;
258}
259
[email protected]f891fb32009-04-08 00:20:32260// Look for a Chrome instance that uses the same profile directory.
[email protected]9a182832012-02-10 18:45:58261// If there isn't one, create a message window with its title set to
262// the profile directory path.
[email protected]7c47ae3e2009-02-18 00:34:21263ProcessSingleton::ProcessSingleton(const FilePath& user_data_dir)
[email protected]0a194552011-09-14 17:53:35264 : window_(NULL), locked_(false), foreground_window_(NULL),
[email protected]2b7ff5b92012-07-17 22:45:18265 is_virtualized_(false), lock_file_(INVALID_HANDLE_VALUE) {
[email protected]b3d791c92012-10-05 19:01:24266 // For Windows 8 and above check if we need to relaunch into Windows 8
267 // immersive mode.
268 if (ShouldLaunchInWindows8ImmersiveMode(user_data_dir)) {
269 bool immersive_chrome_launched = ActivateMetroChrome();
270 if (!immersive_chrome_launched) {
271 LOG(WARNING) << "Failed to launch immersive chrome";
272 } else {
273 // Sleep to allow the immersive chrome process to create its initial
274 // message window.
275 SleepEx(kImmersiveChromeInitTimeout, FALSE);
276 }
277 }
[email protected]bbef41f02010-03-04 16:16:19278 remote_window_ = FindWindowEx(HWND_MESSAGE, NULL,
279 chrome::kMessageWindowClass,
[email protected]8a205c02011-02-04 20:41:33280 user_data_dir.value().c_str());
[email protected]0a194552011-09-14 17:53:35281 if (!remote_window_ && !EscapeVirtualization(user_data_dir)) {
[email protected]bbef41f02010-03-04 16:16:19282 // Make sure we will be the one and only process creating the window.
283 // We use a named Mutex since we are protecting against multi-process
284 // access. As documented, it's clearer to NOT request ownership on creation
285 // since it isn't guaranteed we will get it. It is better to create it
286 // without ownership and explicitly get the ownership afterward.
[email protected]e132804b2010-12-22 12:48:25287 std::wstring mutex_name(L"Local\\ChromeProcessSingletonStartup!");
[email protected]b90d7e802011-01-09 16:32:20288 base::win::ScopedHandle only_me(
289 CreateMutex(NULL, FALSE, mutex_name.c_str()));
[email protected]bbef41f02010-03-04 16:16:19290 DCHECK(only_me.Get() != NULL) << "GetLastError = " << GetLastError();
291
292 // This is how we acquire the mutex (as opposed to the initial ownership).
293 DWORD result = WaitForSingleObject(only_me, INFINITE);
294 DCHECK(result == WAIT_OBJECT_0) << "Result = " << result <<
295 "GetLastError = " << GetLastError();
296
297 // We now own the mutex so we are the only process that can create the
298 // window at this time, but we must still check if someone created it
299 // between the time where we looked for it above and the time the mutex
300 // was given to us.
301 remote_window_ = FindWindowEx(HWND_MESSAGE, NULL,
302 chrome::kMessageWindowClass,
[email protected]8a205c02011-02-04 20:41:33303 user_data_dir.value().c_str());
[email protected]9a182832012-02-10 18:45:58304 if (!remote_window_) {
[email protected]2b7ff5b92012-07-17 22:45:18305 // We have to make sure there is no Chrome instance running on another
306 // machine that uses the same profile.
307 FilePath lock_file_path = user_data_dir.AppendASCII(kLockfile);
308 lock_file_ = CreateFile(lock_file_path.value().c_str(),
309 GENERIC_WRITE,
310 FILE_SHARE_READ,
311 NULL,
312 CREATE_ALWAYS,
313 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
314 NULL);
315 DWORD error = GetLastError();
316 LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
317 error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
318 LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
319 << "Lock file can not be created! Error code: " << error;
[email protected]9a182832012-02-10 18:45:58320
[email protected]2b7ff5b92012-07-17 22:45:18321 if (lock_file_ != INVALID_HANDLE_VALUE) {
322 HINSTANCE hinst = base::GetModuleFromAddress(&ThunkWndProc);
[email protected]9a182832012-02-10 18:45:58323
[email protected]2b7ff5b92012-07-17 22:45:18324 WNDCLASSEX wc = {0};
325 wc.cbSize = sizeof(wc);
326 wc.lpfnWndProc = base::win::WrappedWindowProc<ThunkWndProc>;
327 wc.hInstance = hinst;
328 wc.lpszClassName = chrome::kMessageWindowClass;
329 ATOM clazz = ::RegisterClassEx(&wc);
330 DCHECK(clazz);
331
332 // Set the window's title to the path of our user data directory so
333 // other Chrome instances can decide if they should forward to us.
334 window_ = ::CreateWindow(MAKEINTATOM(clazz),
335 user_data_dir.value().c_str(),
336 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, this);
337 CHECK(window_);
338 }
[email protected]9a182832012-02-10 18:45:58339 }
[email protected]bbef41f02010-03-04 16:16:19340 BOOL success = ReleaseMutex(only_me);
341 DCHECK(success) << "GetLastError = " << GetLastError();
342 }
[email protected]fc14cef2009-01-27 22:17:29343}
344
[email protected]7c47ae3e2009-02-18 00:34:21345ProcessSingleton::~ProcessSingleton() {
[email protected]a9b36c92012-06-18 08:47:57346 // We need to unregister the window as late as possible so that we can detect
347 // another instance of chrome running. Otherwise we may end up writing out
348 // data while a new chrome is starting up.
349 if (window_) {
350 ::DestroyWindow(window_);
351 ::UnregisterClass(chrome::kMessageWindowClass,
352 base::GetModuleFromAddress(&ThunkWndProc));
353 }
[email protected]2b7ff5b92012-07-17 22:45:18354 if (lock_file_ != INVALID_HANDLE_VALUE)
355 CloseHandle(lock_file_);
[email protected]fc14cef2009-01-27 22:17:29356}
357
[email protected]9f20a6d02009-08-21 01:18:37358ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
[email protected]0a194552011-09-14 17:53:35359 if (is_virtualized_)
360 return PROCESS_NOTIFIED; // We already spawned the process in this case.
[email protected]2b7ff5b92012-07-17 22:45:18361 if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_)
362 return LOCK_ERROR;
[email protected]0a194552011-09-14 17:53:35363 else if (!remote_window_)
[email protected]9f20a6d02009-08-21 01:18:37364 return PROCESS_NONE;
[email protected]fc14cef2009-01-27 22:17:29365
[email protected]f394bc62012-08-22 21:42:29366 DWORD process_id = 0;
367 DWORD thread_id = GetWindowThreadProcessId(remote_window_, &process_id);
368 // It is possible that the process owning this window may have died by now.
369 if (!thread_id || !process_id) {
370 remote_window_ = NULL;
371 return PROCESS_NONE;
372 }
373
374 if (base::win::IsMetroProcess()) {
375 // Interesting corner case. We are launched as a metro process but we
376 // found another chrome running. Since metro enforces single instance then
377 // the other chrome must be desktop chrome and this must be a search charm
378 // activation. This scenario is unique; other cases should be properly
379 // handled by the delegate_execute which will not activate a second chrome.
380 string16 terms;
381 base::win::MetroLaunchType launch = base::win::GetMetroLaunchParams(&terms);
382 if (launch != base::win::METRO_SEARCH) {
383 LOG(WARNING) << "In metro mode, but and launch is " << launch;
384 } else {
385 std::string query = net::EscapeQueryParamValue(UTF16ToUTF8(terms), true);
386 std::string url = base::StringPrintf(kSearchUrl, query.c_str());
387 SHELLEXECUTEINFOA sei = { sizeof(sei) };
388 sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
389 sei.nShow = SW_SHOWNORMAL;
390 sei.lpFile = url.c_str();
391 OutputDebugStringA(sei.lpFile);
392 sei.lpDirectory = "";
393 ::ShellExecuteExA(&sei);
394 }
395 return PROCESS_NOTIFIED;
396 }
397 // Non-metro mode, send our command line to the other chrome message window.
[email protected]fc14cef2009-01-27 22:17:29398 // format is "START\0<<<current directory>>>\0<<<commandline>>>".
399 std::wstring to_send(L"START\0", 6); // want the NULL in the string.
[email protected]b9696482010-11-30 23:56:18400 FilePath cur_dir;
[email protected]fc14cef2009-01-27 22:17:29401 if (!PathService::Get(base::DIR_CURRENT, &cur_dir))
[email protected]9f20a6d02009-08-21 01:18:37402 return PROCESS_NONE;
[email protected]b9696482010-11-30 23:56:18403 to_send.append(cur_dir.value());
[email protected]fc14cef2009-01-27 22:17:29404 to_send.append(L"\0", 1); // Null separator.
405 to_send.append(GetCommandLineW());
406 to_send.append(L"\0", 1); // Null separator.
407
408 // Allow the current running browser window making itself the foreground
409 // window (otherwise it will just flash in the taskbar).
[email protected]fc14cef2009-01-27 22:17:29410 AllowSetForegroundWindow(process_id);
411
[email protected]fc14cef2009-01-27 22:17:29412 COPYDATASTRUCT cds;
413 cds.dwData = 0;
414 cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
415 cds.lpData = const_cast<wchar_t*>(to_send.c_str());
416 DWORD_PTR result = 0;
417 if (SendMessageTimeout(remote_window_,
418 WM_COPYDATA,
419 NULL,
420 reinterpret_cast<LPARAM>(&cds),
421 SMTO_ABORTIFHUNG,
[email protected]8b08cbd2009-08-04 05:34:19422 kTimeoutInSeconds * 1000,
[email protected]fc14cef2009-01-27 22:17:29423 &result)) {
[email protected]0815b6d2009-02-11 00:39:37424 // It is possible that the process owning this window may have died by now.
425 if (!result) {
426 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37427 return PROCESS_NONE;
[email protected]0815b6d2009-02-11 00:39:37428 }
[email protected]6927f2e02012-09-18 00:13:49429
430 base::win::ScopedHandle process_handle;
431 if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
432 base::OpenProcessHandleWithAccess(
433 process_id, PROCESS_QUERY_INFORMATION,
434 process_handle.Receive())) {
435 if (base::win::IsProcessImmersive(process_handle.Get()))
436 ActivateMetroChrome();
437 }
[email protected]9f20a6d02009-08-21 01:18:37438 return PROCESS_NOTIFIED;
[email protected]fc14cef2009-01-27 22:17:29439 }
440
[email protected]0815b6d2009-02-11 00:39:37441 // It is possible that the process owning this window may have died by now.
442 if (!IsWindow(remote_window_)) {
443 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37444 return PROCESS_NONE;
[email protected]0815b6d2009-02-11 00:39:37445 }
446
[email protected]fc14cef2009-01-27 22:17:29447 // The window is hung. Scan for every window to find a visible one.
448 bool visible_window = false;
449 EnumThreadWindows(thread_id,
450 &BrowserWindowEnumeration,
451 reinterpret_cast<LPARAM>(&visible_window));
452
453 // If there is a visible browser window, ask the user before killing it.
[email protected]d33220292012-07-04 01:41:27454 if (visible_window && chrome::ShowMessageBox(NULL,
[email protected]5da155e2012-05-26 16:31:16455 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
456 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE),
[email protected]d33220292012-07-04 01:41:27457 chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) {
[email protected]5da155e2012-05-26 16:31:16458 // The user denied. Quit silently.
459 return PROCESS_NOTIFIED;
[email protected]fc14cef2009-01-27 22:17:29460 }
461
462 // Time to take action. Kill the browser process.
[email protected]1fcfb202011-07-19 19:53:14463 base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true);
[email protected]fc14cef2009-01-27 22:17:29464 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37465 return PROCESS_NONE;
[email protected]fc14cef2009-01-27 22:17:29466}
467
[email protected]5d364542012-04-05 07:15:39468ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate(
469 const NotificationCallback& notification_callback) {
[email protected]4a44bc32010-05-28 22:22:44470 NotifyResult result = NotifyOtherProcess();
471 if (result != PROCESS_NONE)
472 return result;
[email protected]5d364542012-04-05 07:15:39473 return Create(notification_callback) ? PROCESS_NONE : PROFILE_IN_USE;
[email protected]4a44bc32010-05-28 22:22:44474}
475
[email protected]9a182832012-02-10 18:45:58476// On Windows, there is no need to call Create() since the message
477// window is created in the constructor but to avoid having more
478// platform specific code in browser_main.cc we tolerate calls to
[email protected]5d364542012-04-05 07:15:39479// Create().
480bool ProcessSingleton::Create(
481 const NotificationCallback& notification_callback) {
[email protected]fc14cef2009-01-27 22:17:29482 DCHECK(!remote_window_);
[email protected]5d364542012-04-05 07:15:39483 DCHECK(notification_callback_.is_null());
484
485 if (window_ != NULL)
486 notification_callback_ = notification_callback;
487
[email protected]9a182832012-02-10 18:45:58488 return window_ != NULL;
[email protected]fc14cef2009-01-27 22:17:29489}
490
[email protected]9f20a6d02009-08-21 01:18:37491void ProcessSingleton::Cleanup() {
492}
493
[email protected]7c47ae3e2009-02-18 00:34:21494LRESULT ProcessSingleton::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) {
[email protected]175a7a22009-05-03 15:57:53495 // If locked, it means we are not ready to process this message because
[email protected]afd20c022010-06-10 00:48:20496 // we are probably in a first run critical phase.
[email protected]175a7a22009-05-03 15:57:53497 if (locked_) {
[email protected]95259c62011-10-25 23:23:53498#if defined(USE_AURA)
499 NOTIMPLEMENTED();
500#else
[email protected]175a7a22009-05-03 15:57:53501 // Attempt to place ourselves in the foreground / flash the task bar.
[email protected]edf04b512012-02-23 09:47:43502 if (foreground_window_ != NULL && IsWindow(foreground_window_)) {
[email protected]175a7a22009-05-03 15:57:53503 SetForegroundWindow(foreground_window_);
[email protected]edf04b512012-02-23 09:47:43504 } else {
505 // Read the command line and store it. It will be replayed when the
506 // ProcessSingleton becomes unlocked.
507 CommandLine parsed_command_line(CommandLine::NO_PROGRAM);
508 FilePath current_directory;
509 if (ParseCommandLine(cds, &parsed_command_line, &current_directory))
510 saved_startup_messages_.push_back(
511 std::make_pair(parsed_command_line.argv(), current_directory));
512 }
[email protected]95259c62011-10-25 23:23:53513#endif
[email protected]175a7a22009-05-03 15:57:53514 return TRUE;
515 }
516
[email protected]edf04b512012-02-23 09:47:43517 CommandLine parsed_command_line(CommandLine::NO_PROGRAM);
518 FilePath current_directory;
519 if (!ParseCommandLine(cds, &parsed_command_line, &current_directory))
[email protected]fc14cef2009-01-27 22:17:29520 return TRUE;
[email protected]5d364542012-04-05 07:15:39521 return notification_callback_.Run(parsed_command_line, current_directory) ?
522 TRUE : FALSE;
[email protected]fc14cef2009-01-27 22:17:29523}
524
[email protected]e3ce40a2012-01-31 03:03:03525LRESULT ProcessSingleton::WndProc(HWND hwnd, UINT message,
526 WPARAM wparam, LPARAM lparam) {
[email protected]fc14cef2009-01-27 22:17:29527 switch (message) {
528 case WM_COPYDATA:
529 return OnCopyData(reinterpret_cast<HWND>(wparam),
530 reinterpret_cast<COPYDATASTRUCT*>(lparam));
531 default:
532 break;
533 }
534
535 return ::DefWindowProc(hwnd, message, wparam, lparam);
536}