blob: b8f5c206cbf4d168b0587416861dc8be1cbc8f6c [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]e9613b52012-11-27 22:35:1325#include "chrome/browser/browser_process.h"
[email protected]245900672012-10-13 02:27:1126#include "chrome/browser/shell_integration.h"
[email protected]b50892c5f2012-05-13 07:34:1427#include "chrome/browser/ui/simple_message_box.h"
[email protected]fc14cef2009-01-27 22:17:2928#include "chrome/common/chrome_constants.h"
[email protected]b3d791c92012-10-05 19:01:2429#include "chrome/common/chrome_paths.h"
30#include "chrome/common/chrome_paths_internal.h"
[email protected]cfa118a2012-10-10 22:27:5331#include "chrome/common/chrome_switches.h"
[email protected]6927f2e02012-09-18 00:13:4932#include "chrome/installer/util/browser_distribution.h"
[email protected]786799692012-09-26 14:16:4833#include "chrome/installer/util/install_util.h"
[email protected]6927f2e02012-09-18 00:13:4934#include "chrome/installer/util/shell_util.h"
[email protected]0a194552011-09-14 17:53:3535#include "chrome/installer/util/wmi.h"
[email protected]b39ef1cb2011-10-25 04:46:5536#include "content/public/common/result_codes.h"
[email protected]34ac8f32009-02-22 23:03:2737#include "grit/chromium_strings.h"
38#include "grit/generated_resources.h"
[email protected]f394bc62012-08-22 21:42:2939#include "net/base/escape.h"
[email protected]c051a1b2011-01-21 23:30:1740#include "ui/base/l10n/l10n_util.h"
[email protected]e7661062011-01-19 22:16:5341#include "ui/base/win/hwnd_util.h"
[email protected]fc14cef2009-01-27 22:17:2942
43namespace {
44
[email protected]2b7ff5b92012-07-17 22:45:1845const char kLockfile[] = "lockfile";
46
[email protected]f394bc62012-08-22 21:42:2947const char kSearchUrl[] =
48 "http://www.google.com/search?q=%s&sourceid=chrome&ie=UTF-8";
49
[email protected]b3d791c92012-10-05 19:01:2450const int kImmersiveChromeInitTimeout = 500;
51
[email protected]f891fb32009-04-08 00:20:3252// Checks the visibility of the enumerated window and signals once a visible
[email protected]fc14cef2009-01-27 22:17:2953// window has been found.
54BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
55 bool* result = reinterpret_cast<bool*>(param);
56 *result = IsWindowVisible(window) != 0;
57 // Stops enumeration if a visible window has been found.
58 return !*result;
59}
60
[email protected]e3ce40a2012-01-31 03:03:0361// This function thunks to the object's version of the windowproc, taking in
62// consideration that there are several messages being dispatched before
63// WM_NCCREATE which we let windows handle.
64LRESULT CALLBACK ThunkWndProc(HWND hwnd, UINT message,
65 WPARAM wparam, LPARAM lparam) {
66 ProcessSingleton* singleton =
67 reinterpret_cast<ProcessSingleton*>(ui::GetWindowUserData(hwnd));
68 if (message == WM_NCCREATE) {
69 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lparam);
70 singleton = reinterpret_cast<ProcessSingleton*>(cs->lpCreateParams);
71 CHECK(singleton);
72 ui::SetWindowUserData(hwnd, singleton);
73 } else if (!singleton) {
74 return ::DefWindowProc(hwnd, message, wparam, lparam);
75 }
76 return singleton->WndProc(hwnd, message, wparam, lparam);
77}
78
[email protected]edf04b512012-02-23 09:47:4379bool ParseCommandLine(const COPYDATASTRUCT* cds,
80 CommandLine* parsed_command_line,
81 FilePath* current_directory) {
82 // We should have enough room for the shortest command (min_message_size)
83 // and also be a multiple of wchar_t bytes. The shortest command
84 // possible is L"START\0\0" (empty current directory and command line).
85 static const int min_message_size = 7;
86 if (cds->cbData < min_message_size * sizeof(wchar_t) ||
87 cds->cbData % sizeof(wchar_t) != 0) {
88 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
89 return false;
90 }
91
92 // We split the string into 4 parts on NULLs.
93 DCHECK(cds->lpData);
94 const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
95 cds->cbData / sizeof(wchar_t));
96 const std::wstring::size_type first_null = msg.find_first_of(L'\0');
97 if (first_null == 0 || first_null == std::wstring::npos) {
98 // no NULL byte, don't know what to do
99 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
100 ", first null = " << first_null;
101 return false;
102 }
103
104 // Decode the command, which is everything until the first NULL.
105 if (msg.substr(0, first_null) == L"START") {
106 // Another instance is starting parse the command line & do what it would
107 // have done.
108 VLOG(1) << "Handling STARTUP request from another process";
109 const std::wstring::size_type second_null =
110 msg.find_first_of(L'\0', first_null + 1);
111 if (second_null == std::wstring::npos ||
112 first_null == msg.length() - 1 || second_null == msg.length()) {
113 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
114 "parts separated by NULLs";
115 return false;
116 }
117
118 // Get current directory.
119 *current_directory = FilePath(msg.substr(first_null + 1,
120 second_null - first_null));
121
122 const std::wstring::size_type third_null =
123 msg.find_first_of(L'\0', second_null + 1);
124 if (third_null == std::wstring::npos ||
125 third_null == msg.length()) {
126 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
127 "parts separated by NULLs";
128 }
129
130 // Get command line.
131 const std::wstring cmd_line =
132 msg.substr(second_null + 1, third_null - second_null);
133 *parsed_command_line = CommandLine::FromString(cmd_line);
134 return true;
135 }
136 return false;
137}
138
[email protected]6927f2e02012-09-18 00:13:49139bool ActivateMetroChrome() {
140 FilePath chrome_exe;
141 if (!PathService::Get(base::FILE_EXE, &chrome_exe)) {
142 NOTREACHED() << "Failed to get chrome exe path";
143 return false;
144 }
145 string16 app_id = ShellUtil::GetBrowserModelId(
[email protected]786799692012-09-26 14:16:48146 BrowserDistribution::GetDistribution(),
147 InstallUtil::IsPerUserInstall(chrome_exe.value().c_str()));
[email protected]6927f2e02012-09-18 00:13:49148 if (app_id.empty()) {
149 NOTREACHED() << "Failed to get chrome app user model id.";
150 return false;
151 }
152
153 base::win::ScopedComPtr<IApplicationActivationManager> activation_manager;
154 HRESULT hr = activation_manager.CreateInstance(
155 CLSID_ApplicationActivationManager);
156 if (!activation_manager) {
157 NOTREACHED() << "Failed to cocreate activation manager. Error: " << hr;
158 return false;
159 }
160
161 unsigned long pid = 0;
162 hr = activation_manager->ActivateApplication(app_id.c_str(),
163 L"open",
164 AO_NONE,
165 &pid);
166 if (FAILED(hr)) {
167 NOTREACHED() << "Failed to activate metro chrome. Error: " << hr;
168 return false;
169 }
170 return true;
171}
172
[email protected]b3d791c92012-10-05 19:01:24173// Returns true if Chrome needs to be relaunched into Windows 8 immersive mode.
174// Following conditions apply:-
175// 1. Windows 8 or greater.
176// 2. Not in Windows 8 immersive mode.
[email protected]245900672012-10-13 02:27:11177// 3. Chrome is default browser.
178// 4. Process integrity level is not high.
179// 5. The profile data directory is the default directory.
180// 6. Last used mode was immersive/machine is a tablet.
[email protected]b3d791c92012-10-05 19:01:24181// TODO(ananta)
182// Move this function to a common place as the Windows 8 delegate_execute
183// handler can possibly use this.
184bool ShouldLaunchInWindows8ImmersiveMode(const FilePath& user_data_dir) {
185#if defined(USE_AURA)
186 return false;
187#endif
188
189 if (base::win::GetVersion() < base::win::VERSION_WIN8)
190 return false;
191
192 if (base::win::IsProcessImmersive(base::GetCurrentProcessHandle()))
193 return false;
194
[email protected]245900672012-10-13 02:27:11195 if (!ShellIntegration::IsDefaultBrowser())
196 return false;
197
[email protected]b3d791c92012-10-05 19:01:24198 base::IntegrityLevel integrity_level = base::INTEGRITY_UNKNOWN;
199 base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(),
200 &integrity_level);
201 if (integrity_level == base::HIGH_INTEGRITY)
202 return false;
203
204 FilePath default_user_data_dir;
205 if (!chrome::GetDefaultUserDataDirectory(&default_user_data_dir))
206 return false;
207
208 if (default_user_data_dir != user_data_dir)
209 return false;
210
[email protected]7e416002012-10-13 05:19:21211 // TODO(gab): This is a temporary solution to avoid activating Metro Chrome
212 // when chrome.exe is invoked with one of the short-lived commands below. The
213 // long-term and correct solution is to only check/activate Chrome later;
214 // after handling of these short-lived commands has occured
215 // (http://crbug.com/155585).
216 // This is a 1:1 mapping of the switches that force an early exit of Chrome in
217 // ChromeBrowserMainParts::PreMainMessageLoopRunImpl().
218 if (CommandLine::ForCurrentProcess()->HasSwitch(switches::kUninstall) ||
219 CommandLine::ForCurrentProcess()->HasSwitch(switches::kHideIcons) ||
220 CommandLine::ForCurrentProcess()->HasSwitch(switches::kShowIcons) ||
221 CommandLine::ForCurrentProcess()->HasSwitch(
222 switches::kMakeDefaultBrowser) ||
223 CommandLine::ForCurrentProcess()->HasSwitch(switches::kPackExtension)) {
[email protected]cfa118a2012-10-10 22:27:53224 return false;
[email protected]7e416002012-10-13 05:19:21225 }
[email protected]cfa118a2012-10-10 22:27:53226
[email protected]e212a3062012-11-14 15:59:07227 base::win::RegKey reg_key;
[email protected]7e416002012-10-13 05:19:21228 DWORD reg_value = 0;
[email protected]e212a3062012-11-14 15:59:07229 if (reg_key.Create(HKEY_CURRENT_USER, chrome::kMetroRegistryPath,
230 KEY_READ) == ERROR_SUCCESS &&
[email protected]7e416002012-10-13 05:19:21231 reg_key.ReadValueDW(chrome::kLaunchModeValue,
232 &reg_value) == ERROR_SUCCESS) {
[email protected]e212a3062012-11-14 15:59:07233 return reg_value == 1;
[email protected]b3d791c92012-10-05 19:01:24234 }
235 return base::win::IsMachineATablet();
236}
237
[email protected]fc14cef2009-01-27 22:17:29238} // namespace
239
[email protected]0a194552011-09-14 17:53:35240// Microsoft's Softricity virtualization breaks the sandbox processes.
241// So, if we detect the Softricity DLL we use WMI Win32_Process.Create to
242// break out of the virtualization environment.
243// http://code.google.com/p/chromium/issues/detail?id=43650
244bool ProcessSingleton::EscapeVirtualization(const FilePath& user_data_dir) {
245 if (::GetModuleHandle(L"sftldr_wow64.dll") ||
246 ::GetModuleHandle(L"sftldr.dll")) {
247 int process_id;
248 if (!installer::WMIProcess::Launch(GetCommandLineW(), &process_id))
249 return false;
250 is_virtualized_ = true;
251 // The new window was spawned from WMI, and won't be in the foreground.
252 // So, first we sleep while the new chrome.exe instance starts (because
253 // WaitForInputIdle doesn't work here). Then we poll for up to two more
254 // seconds and make the window foreground if we find it (or we give up).
255 HWND hwnd = 0;
256 ::Sleep(90);
257 for (int tries = 200; tries; --tries) {
258 hwnd = FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass,
259 user_data_dir.value().c_str());
260 if (hwnd) {
261 ::SetForegroundWindow(hwnd);
262 break;
263 }
264 ::Sleep(10);
265 }
266 return true;
267 }
268 return false;
269}
270
[email protected]f891fb32009-04-08 00:20:32271// Look for a Chrome instance that uses the same profile directory.
[email protected]9a182832012-02-10 18:45:58272// If there isn't one, create a message window with its title set to
273// the profile directory path.
[email protected]7c47ae3e2009-02-18 00:34:21274ProcessSingleton::ProcessSingleton(const FilePath& user_data_dir)
[email protected]0a194552011-09-14 17:53:35275 : window_(NULL), locked_(false), foreground_window_(NULL),
[email protected]2b7ff5b92012-07-17 22:45:18276 is_virtualized_(false), lock_file_(INVALID_HANDLE_VALUE) {
[email protected]b3d791c92012-10-05 19:01:24277 // For Windows 8 and above check if we need to relaunch into Windows 8
278 // immersive mode.
279 if (ShouldLaunchInWindows8ImmersiveMode(user_data_dir)) {
280 bool immersive_chrome_launched = ActivateMetroChrome();
281 if (!immersive_chrome_launched) {
282 LOG(WARNING) << "Failed to launch immersive chrome";
283 } else {
284 // Sleep to allow the immersive chrome process to create its initial
285 // message window.
286 SleepEx(kImmersiveChromeInitTimeout, FALSE);
287 }
288 }
[email protected]bbef41f02010-03-04 16:16:19289 remote_window_ = FindWindowEx(HWND_MESSAGE, NULL,
290 chrome::kMessageWindowClass,
[email protected]8a205c02011-02-04 20:41:33291 user_data_dir.value().c_str());
[email protected]0a194552011-09-14 17:53:35292 if (!remote_window_ && !EscapeVirtualization(user_data_dir)) {
[email protected]bbef41f02010-03-04 16:16:19293 // Make sure we will be the one and only process creating the window.
294 // We use a named Mutex since we are protecting against multi-process
295 // access. As documented, it's clearer to NOT request ownership on creation
296 // since it isn't guaranteed we will get it. It is better to create it
297 // without ownership and explicitly get the ownership afterward.
[email protected]e132804b2010-12-22 12:48:25298 std::wstring mutex_name(L"Local\\ChromeProcessSingletonStartup!");
[email protected]b90d7e802011-01-09 16:32:20299 base::win::ScopedHandle only_me(
300 CreateMutex(NULL, FALSE, mutex_name.c_str()));
[email protected]bbef41f02010-03-04 16:16:19301 DCHECK(only_me.Get() != NULL) << "GetLastError = " << GetLastError();
302
303 // This is how we acquire the mutex (as opposed to the initial ownership).
304 DWORD result = WaitForSingleObject(only_me, INFINITE);
305 DCHECK(result == WAIT_OBJECT_0) << "Result = " << result <<
306 "GetLastError = " << GetLastError();
307
308 // We now own the mutex so we are the only process that can create the
309 // window at this time, but we must still check if someone created it
310 // between the time where we looked for it above and the time the mutex
311 // was given to us.
312 remote_window_ = FindWindowEx(HWND_MESSAGE, NULL,
313 chrome::kMessageWindowClass,
[email protected]8a205c02011-02-04 20:41:33314 user_data_dir.value().c_str());
[email protected]9a182832012-02-10 18:45:58315 if (!remote_window_) {
[email protected]2b7ff5b92012-07-17 22:45:18316 // We have to make sure there is no Chrome instance running on another
317 // machine that uses the same profile.
318 FilePath lock_file_path = user_data_dir.AppendASCII(kLockfile);
319 lock_file_ = CreateFile(lock_file_path.value().c_str(),
320 GENERIC_WRITE,
321 FILE_SHARE_READ,
322 NULL,
323 CREATE_ALWAYS,
324 FILE_ATTRIBUTE_NORMAL | FILE_FLAG_DELETE_ON_CLOSE,
325 NULL);
326 DWORD error = GetLastError();
327 LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
328 error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
329 LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
330 << "Lock file can not be created! Error code: " << error;
[email protected]9a182832012-02-10 18:45:58331
[email protected]2b7ff5b92012-07-17 22:45:18332 if (lock_file_ != INVALID_HANDLE_VALUE) {
333 HINSTANCE hinst = base::GetModuleFromAddress(&ThunkWndProc);
[email protected]9a182832012-02-10 18:45:58334
[email protected]2b7ff5b92012-07-17 22:45:18335 WNDCLASSEX wc = {0};
336 wc.cbSize = sizeof(wc);
337 wc.lpfnWndProc = base::win::WrappedWindowProc<ThunkWndProc>;
338 wc.hInstance = hinst;
339 wc.lpszClassName = chrome::kMessageWindowClass;
340 ATOM clazz = ::RegisterClassEx(&wc);
341 DCHECK(clazz);
342
343 // Set the window's title to the path of our user data directory so
344 // other Chrome instances can decide if they should forward to us.
345 window_ = ::CreateWindow(MAKEINTATOM(clazz),
346 user_data_dir.value().c_str(),
347 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, this);
348 CHECK(window_);
349 }
[email protected]9a182832012-02-10 18:45:58350 }
[email protected]bbef41f02010-03-04 16:16:19351 BOOL success = ReleaseMutex(only_me);
352 DCHECK(success) << "GetLastError = " << GetLastError();
353 }
[email protected]fc14cef2009-01-27 22:17:29354}
355
[email protected]7c47ae3e2009-02-18 00:34:21356ProcessSingleton::~ProcessSingleton() {
[email protected]a9b36c92012-06-18 08:47:57357 // We need to unregister the window as late as possible so that we can detect
358 // another instance of chrome running. Otherwise we may end up writing out
359 // data while a new chrome is starting up.
360 if (window_) {
361 ::DestroyWindow(window_);
362 ::UnregisterClass(chrome::kMessageWindowClass,
363 base::GetModuleFromAddress(&ThunkWndProc));
364 }
[email protected]2b7ff5b92012-07-17 22:45:18365 if (lock_file_ != INVALID_HANDLE_VALUE)
366 CloseHandle(lock_file_);
[email protected]fc14cef2009-01-27 22:17:29367}
368
[email protected]9f20a6d02009-08-21 01:18:37369ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
[email protected]0a194552011-09-14 17:53:35370 if (is_virtualized_)
371 return PROCESS_NOTIFIED; // We already spawned the process in this case.
[email protected]e9613b52012-11-27 22:35:13372 if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
[email protected]2b7ff5b92012-07-17 22:45:18373 return LOCK_ERROR;
[email protected]e9613b52012-11-27 22:35:13374 } else if (!remote_window_) {
375 g_browser_process->PlatformSpecificCommandLineProcessing(
376 *CommandLine::ForCurrentProcess());
[email protected]9f20a6d02009-08-21 01:18:37377 return PROCESS_NONE;
[email protected]e9613b52012-11-27 22:35:13378 }
[email protected]fc14cef2009-01-27 22:17:29379
[email protected]f394bc62012-08-22 21:42:29380 DWORD process_id = 0;
381 DWORD thread_id = GetWindowThreadProcessId(remote_window_, &process_id);
382 // It is possible that the process owning this window may have died by now.
383 if (!thread_id || !process_id) {
384 remote_window_ = NULL;
385 return PROCESS_NONE;
386 }
387
388 if (base::win::IsMetroProcess()) {
389 // Interesting corner case. We are launched as a metro process but we
390 // found another chrome running. Since metro enforces single instance then
391 // the other chrome must be desktop chrome and this must be a search charm
392 // activation. This scenario is unique; other cases should be properly
393 // handled by the delegate_execute which will not activate a second chrome.
394 string16 terms;
395 base::win::MetroLaunchType launch = base::win::GetMetroLaunchParams(&terms);
396 if (launch != base::win::METRO_SEARCH) {
397 LOG(WARNING) << "In metro mode, but and launch is " << launch;
398 } else {
399 std::string query = net::EscapeQueryParamValue(UTF16ToUTF8(terms), true);
400 std::string url = base::StringPrintf(kSearchUrl, query.c_str());
401 SHELLEXECUTEINFOA sei = { sizeof(sei) };
402 sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
403 sei.nShow = SW_SHOWNORMAL;
404 sei.lpFile = url.c_str();
405 OutputDebugStringA(sei.lpFile);
406 sei.lpDirectory = "";
407 ::ShellExecuteExA(&sei);
408 }
409 return PROCESS_NOTIFIED;
410 }
411 // Non-metro mode, send our command line to the other chrome message window.
[email protected]fc14cef2009-01-27 22:17:29412 // format is "START\0<<<current directory>>>\0<<<commandline>>>".
413 std::wstring to_send(L"START\0", 6); // want the NULL in the string.
[email protected]b9696482010-11-30 23:56:18414 FilePath cur_dir;
[email protected]fc14cef2009-01-27 22:17:29415 if (!PathService::Get(base::DIR_CURRENT, &cur_dir))
[email protected]9f20a6d02009-08-21 01:18:37416 return PROCESS_NONE;
[email protected]b9696482010-11-30 23:56:18417 to_send.append(cur_dir.value());
[email protected]fc14cef2009-01-27 22:17:29418 to_send.append(L"\0", 1); // Null separator.
419 to_send.append(GetCommandLineW());
420 to_send.append(L"\0", 1); // Null separator.
421
422 // Allow the current running browser window making itself the foreground
423 // window (otherwise it will just flash in the taskbar).
[email protected]fc14cef2009-01-27 22:17:29424 AllowSetForegroundWindow(process_id);
425
[email protected]fc14cef2009-01-27 22:17:29426 COPYDATASTRUCT cds;
427 cds.dwData = 0;
428 cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
429 cds.lpData = const_cast<wchar_t*>(to_send.c_str());
430 DWORD_PTR result = 0;
431 if (SendMessageTimeout(remote_window_,
432 WM_COPYDATA,
433 NULL,
434 reinterpret_cast<LPARAM>(&cds),
435 SMTO_ABORTIFHUNG,
[email protected]8b08cbd2009-08-04 05:34:19436 kTimeoutInSeconds * 1000,
[email protected]fc14cef2009-01-27 22:17:29437 &result)) {
[email protected]0815b6d2009-02-11 00:39:37438 // It is possible that the process owning this window may have died by now.
439 if (!result) {
440 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37441 return PROCESS_NONE;
[email protected]0815b6d2009-02-11 00:39:37442 }
[email protected]6927f2e02012-09-18 00:13:49443
444 base::win::ScopedHandle process_handle;
445 if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
446 base::OpenProcessHandleWithAccess(
447 process_id, PROCESS_QUERY_INFORMATION,
448 process_handle.Receive())) {
449 if (base::win::IsProcessImmersive(process_handle.Get()))
450 ActivateMetroChrome();
451 }
[email protected]9f20a6d02009-08-21 01:18:37452 return PROCESS_NOTIFIED;
[email protected]fc14cef2009-01-27 22:17:29453 }
454
[email protected]0815b6d2009-02-11 00:39:37455 // It is possible that the process owning this window may have died by now.
456 if (!IsWindow(remote_window_)) {
457 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37458 return PROCESS_NONE;
[email protected]0815b6d2009-02-11 00:39:37459 }
460
[email protected]fc14cef2009-01-27 22:17:29461 // The window is hung. Scan for every window to find a visible one.
462 bool visible_window = false;
463 EnumThreadWindows(thread_id,
464 &BrowserWindowEnumeration,
465 reinterpret_cast<LPARAM>(&visible_window));
466
467 // If there is a visible browser window, ask the user before killing it.
[email protected]d33220292012-07-04 01:41:27468 if (visible_window && chrome::ShowMessageBox(NULL,
[email protected]5da155e2012-05-26 16:31:16469 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
470 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE),
[email protected]d33220292012-07-04 01:41:27471 chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) {
[email protected]5da155e2012-05-26 16:31:16472 // The user denied. Quit silently.
473 return PROCESS_NOTIFIED;
[email protected]fc14cef2009-01-27 22:17:29474 }
475
476 // Time to take action. Kill the browser process.
[email protected]1fcfb202011-07-19 19:53:14477 base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true);
[email protected]fc14cef2009-01-27 22:17:29478 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37479 return PROCESS_NONE;
[email protected]fc14cef2009-01-27 22:17:29480}
481
[email protected]5d364542012-04-05 07:15:39482ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate(
483 const NotificationCallback& notification_callback) {
[email protected]4a44bc32010-05-28 22:22:44484 NotifyResult result = NotifyOtherProcess();
485 if (result != PROCESS_NONE)
486 return result;
[email protected]5d364542012-04-05 07:15:39487 return Create(notification_callback) ? PROCESS_NONE : PROFILE_IN_USE;
[email protected]4a44bc32010-05-28 22:22:44488}
489
[email protected]9a182832012-02-10 18:45:58490// On Windows, there is no need to call Create() since the message
491// window is created in the constructor but to avoid having more
492// platform specific code in browser_main.cc we tolerate calls to
[email protected]5d364542012-04-05 07:15:39493// Create().
494bool ProcessSingleton::Create(
495 const NotificationCallback& notification_callback) {
[email protected]fc14cef2009-01-27 22:17:29496 DCHECK(!remote_window_);
[email protected]5d364542012-04-05 07:15:39497 DCHECK(notification_callback_.is_null());
498
499 if (window_ != NULL)
500 notification_callback_ = notification_callback;
501
[email protected]9a182832012-02-10 18:45:58502 return window_ != NULL;
[email protected]fc14cef2009-01-27 22:17:29503}
504
[email protected]9f20a6d02009-08-21 01:18:37505void ProcessSingleton::Cleanup() {
506}
507
[email protected]7c47ae3e2009-02-18 00:34:21508LRESULT ProcessSingleton::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) {
[email protected]175a7a22009-05-03 15:57:53509 // If locked, it means we are not ready to process this message because
[email protected]afd20c022010-06-10 00:48:20510 // we are probably in a first run critical phase.
[email protected]175a7a22009-05-03 15:57:53511 if (locked_) {
[email protected]95259c62011-10-25 23:23:53512#if defined(USE_AURA)
513 NOTIMPLEMENTED();
514#else
[email protected]175a7a22009-05-03 15:57:53515 // Attempt to place ourselves in the foreground / flash the task bar.
[email protected]edf04b512012-02-23 09:47:43516 if (foreground_window_ != NULL && IsWindow(foreground_window_)) {
[email protected]175a7a22009-05-03 15:57:53517 SetForegroundWindow(foreground_window_);
[email protected]edf04b512012-02-23 09:47:43518 } else {
519 // Read the command line and store it. It will be replayed when the
520 // ProcessSingleton becomes unlocked.
521 CommandLine parsed_command_line(CommandLine::NO_PROGRAM);
522 FilePath current_directory;
523 if (ParseCommandLine(cds, &parsed_command_line, &current_directory))
524 saved_startup_messages_.push_back(
525 std::make_pair(parsed_command_line.argv(), current_directory));
526 }
[email protected]95259c62011-10-25 23:23:53527#endif
[email protected]175a7a22009-05-03 15:57:53528 return TRUE;
529 }
530
[email protected]edf04b512012-02-23 09:47:43531 CommandLine parsed_command_line(CommandLine::NO_PROGRAM);
532 FilePath current_directory;
533 if (!ParseCommandLine(cds, &parsed_command_line, &current_directory))
[email protected]fc14cef2009-01-27 22:17:29534 return TRUE;
[email protected]5d364542012-04-05 07:15:39535 return notification_callback_.Run(parsed_command_line, current_directory) ?
536 TRUE : FALSE;
[email protected]fc14cef2009-01-27 22:17:29537}
538
[email protected]e3ce40a2012-01-31 03:03:03539LRESULT ProcessSingleton::WndProc(HWND hwnd, UINT message,
540 WPARAM wparam, LPARAM lparam) {
[email protected]fc14cef2009-01-27 22:17:29541 switch (message) {
542 case WM_COPYDATA:
543 return OnCopyData(reinterpret_cast<HWND>(wparam),
544 reinterpret_cast<COPYDATASTRUCT*>(lparam));
545 default:
546 break;
547 }
548
549 return ::DefWindowProc(hwnd, message, wparam, lparam);
550}