blob: 43adccc5b8203129f61d7ac92cacb2604e4f3615 [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>
8
[email protected]fc14cef2009-01-27 22:17:299#include "base/base_paths.h"
10#include "base/command_line.h"
[email protected]57999812013-02-24 05:40:5211#include "base/files/file_path.h"
[email protected]9e9b6e8e2009-12-02 08:45:0112#include "base/path_service.h"
[email protected]fc14cef2009-01-27 22:17:2913#include "base/process_util.h"
[email protected]f394bc62012-08-22 21:42:2914#include "base/stringprintf.h"
[email protected]0f26d7b2011-01-05 19:10:4415#include "base/utf_string_conversions.h"
[email protected]f394bc62012-08-22 21:42:2916#include "base/win/metro.h"
[email protected]b3d791c92012-10-05 19:01:2417#include "base/win/registry.h"
[email protected]b90d7e802011-01-09 16:32:2018#include "base/win/scoped_handle.h"
[email protected]b3d791c92012-10-05 19:01:2419#include "base/win/win_util.h"
[email protected]6927f2e02012-09-18 00:13:4920#include "base/win/windows_version.h"
[email protected]ecb924c2011-03-17 00:34:0921#include "base/win/wrapped_window_proc.h"
[email protected]e9613b52012-11-27 22:35:1322#include "chrome/browser/browser_process.h"
[email protected]245900672012-10-13 02:27:1123#include "chrome/browser/shell_integration.h"
[email protected]281b5da2013-03-05 06:13:4624#include "chrome/browser/ui/metro_chrome_win.h"
[email protected]b50892c5f2012-05-13 07:34:1425#include "chrome/browser/ui/simple_message_box.h"
[email protected]fc14cef2009-01-27 22:17:2926#include "chrome/common/chrome_constants.h"
[email protected]b3d791c92012-10-05 19:01:2427#include "chrome/common/chrome_paths.h"
28#include "chrome/common/chrome_paths_internal.h"
[email protected]cfa118a2012-10-10 22:27:5329#include "chrome/common/chrome_switches.h"
[email protected]0a194552011-09-14 17:53:3530#include "chrome/installer/util/wmi.h"
[email protected]b39ef1cb2011-10-25 04:46:5531#include "content/public/common/result_codes.h"
[email protected]34ac8f32009-02-22 23:03:2732#include "grit/chromium_strings.h"
33#include "grit/generated_resources.h"
[email protected]f394bc62012-08-22 21:42:2934#include "net/base/escape.h"
[email protected]c051a1b2011-01-21 23:30:1735#include "ui/base/l10n/l10n_util.h"
[email protected]e7661062011-01-19 22:16:5336#include "ui/base/win/hwnd_util.h"
[email protected]fc14cef2009-01-27 22:17:2937
38namespace {
39
[email protected]2b7ff5b92012-07-17 22:45:1840const char kLockfile[] = "lockfile";
41
[email protected]f394bc62012-08-22 21:42:2942const char kSearchUrl[] =
43 "http://www.google.com/search?q=%s&sourceid=chrome&ie=UTF-8";
44
[email protected]14649ecd2012-12-05 01:00:2445const int kMetroChromeActivationTimeoutMs = 3000;
46
47// A helper class that acquires the given |mutex| while the AutoLockMutex is in
48// scope.
49class AutoLockMutex {
50 public:
51 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
[email protected]b9057302013-03-28 12:40:3852 DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
[email protected]14649ecd2012-12-05 01:00:2453 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
54 }
55
56 ~AutoLockMutex() {
[email protected]b9057302013-03-28 12:40:3857 BOOL released = ::ReleaseMutex(mutex_);
[email protected]14649ecd2012-12-05 01:00:2458 DPCHECK(released);
59 }
60
61 private:
62 HANDLE mutex_;
63 DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
64};
65
66// A helper class that releases the given |mutex| while the AutoUnlockMutex is
67// in scope and immediately re-acquires it when going out of scope.
68class AutoUnlockMutex {
69 public:
70 explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
[email protected]b9057302013-03-28 12:40:3871 BOOL released = ::ReleaseMutex(mutex_);
[email protected]14649ecd2012-12-05 01:00:2472 DPCHECK(released);
73 }
74
75 ~AutoUnlockMutex() {
[email protected]b9057302013-03-28 12:40:3876 DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
[email protected]14649ecd2012-12-05 01:00:2477 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
78 }
79
80 private:
81 HANDLE mutex_;
82 DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
83};
[email protected]b3d791c92012-10-05 19:01:2484
[email protected]f891fb32009-04-08 00:20:3285// Checks the visibility of the enumerated window and signals once a visible
[email protected]fc14cef2009-01-27 22:17:2986// window has been found.
87BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
88 bool* result = reinterpret_cast<bool*>(param);
[email protected]b9057302013-03-28 12:40:3889 *result = ::IsWindowVisible(window) != 0;
[email protected]fc14cef2009-01-27 22:17:2990 // Stops enumeration if a visible window has been found.
91 return !*result;
92}
93
[email protected]e3ce40a2012-01-31 03:03:0394// This function thunks to the object's version of the windowproc, taking in
95// consideration that there are several messages being dispatched before
96// WM_NCCREATE which we let windows handle.
97LRESULT CALLBACK ThunkWndProc(HWND hwnd, UINT message,
98 WPARAM wparam, LPARAM lparam) {
99 ProcessSingleton* singleton =
100 reinterpret_cast<ProcessSingleton*>(ui::GetWindowUserData(hwnd));
101 if (message == WM_NCCREATE) {
102 CREATESTRUCT* cs = reinterpret_cast<CREATESTRUCT*>(lparam);
103 singleton = reinterpret_cast<ProcessSingleton*>(cs->lpCreateParams);
104 CHECK(singleton);
105 ui::SetWindowUserData(hwnd, singleton);
106 } else if (!singleton) {
107 return ::DefWindowProc(hwnd, message, wparam, lparam);
108 }
109 return singleton->WndProc(hwnd, message, wparam, lparam);
110}
111
[email protected]edf04b512012-02-23 09:47:43112bool ParseCommandLine(const COPYDATASTRUCT* cds,
113 CommandLine* parsed_command_line,
[email protected]650b2d52013-02-10 03:41:45114 base::FilePath* current_directory) {
[email protected]edf04b512012-02-23 09:47:43115 // We should have enough room for the shortest command (min_message_size)
116 // and also be a multiple of wchar_t bytes. The shortest command
117 // possible is L"START\0\0" (empty current directory and command line).
118 static const int min_message_size = 7;
119 if (cds->cbData < min_message_size * sizeof(wchar_t) ||
120 cds->cbData % sizeof(wchar_t) != 0) {
121 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
122 return false;
123 }
124
125 // We split the string into 4 parts on NULLs.
126 DCHECK(cds->lpData);
127 const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
128 cds->cbData / sizeof(wchar_t));
129 const std::wstring::size_type first_null = msg.find_first_of(L'\0');
130 if (first_null == 0 || first_null == std::wstring::npos) {
131 // no NULL byte, don't know what to do
132 LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
133 ", first null = " << first_null;
134 return false;
135 }
136
137 // Decode the command, which is everything until the first NULL.
138 if (msg.substr(0, first_null) == L"START") {
139 // Another instance is starting parse the command line & do what it would
140 // have done.
141 VLOG(1) << "Handling STARTUP request from another process";
142 const std::wstring::size_type second_null =
143 msg.find_first_of(L'\0', first_null + 1);
144 if (second_null == std::wstring::npos ||
145 first_null == msg.length() - 1 || second_null == msg.length()) {
146 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
147 "parts separated by NULLs";
148 return false;
149 }
150
151 // Get current directory.
[email protected]650b2d52013-02-10 03:41:45152 *current_directory = base::FilePath(msg.substr(first_null + 1,
[email protected]b9057302013-03-28 12:40:38153 second_null - first_null));
[email protected]edf04b512012-02-23 09:47:43154
155 const std::wstring::size_type third_null =
156 msg.find_first_of(L'\0', second_null + 1);
157 if (third_null == std::wstring::npos ||
158 third_null == msg.length()) {
159 LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
160 "parts separated by NULLs";
161 }
162
163 // Get command line.
164 const std::wstring cmd_line =
165 msg.substr(second_null + 1, third_null - second_null);
166 *parsed_command_line = CommandLine::FromString(cmd_line);
167 return true;
168 }
169 return false;
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.
[email protected]650b2d52013-02-10 03:41:45183bool ShouldLaunchInWindows8ImmersiveMode(const base::FilePath& user_data_dir) {
[email protected]b3d791c92012-10-05 19:01:24184#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]89886652012-12-11 18:09:07194 if (ShellIntegration::GetDefaultBrowser() != ShellIntegration::IS_DEFAULT)
[email protected]245900672012-10-13 02:27:11195 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
[email protected]650b2d52013-02-10 03:41:45203 base::FilePath default_user_data_dir;
[email protected]b3d791c92012-10-05 19:01:24204 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]e212a3062012-11-14 15:59:07210 base::win::RegKey reg_key;
[email protected]7e416002012-10-13 05:19:21211 DWORD reg_value = 0;
[email protected]e212a3062012-11-14 15:59:07212 if (reg_key.Create(HKEY_CURRENT_USER, chrome::kMetroRegistryPath,
213 KEY_READ) == ERROR_SUCCESS &&
[email protected]7e416002012-10-13 05:19:21214 reg_key.ReadValueDW(chrome::kLaunchModeValue,
215 &reg_value) == ERROR_SUCCESS) {
[email protected]e212a3062012-11-14 15:59:07216 return reg_value == 1;
[email protected]b3d791c92012-10-05 19:01:24217 }
218 return base::win::IsMachineATablet();
219}
220
[email protected]fc14cef2009-01-27 22:17:29221} // namespace
222
[email protected]0a194552011-09-14 17:53:35223// Microsoft's Softricity virtualization breaks the sandbox processes.
224// So, if we detect the Softricity DLL we use WMI Win32_Process.Create to
225// break out of the virtualization environment.
226// http://code.google.com/p/chromium/issues/detail?id=43650
[email protected]650b2d52013-02-10 03:41:45227bool ProcessSingleton::EscapeVirtualization(
228 const base::FilePath& user_data_dir) {
[email protected]0a194552011-09-14 17:53:35229 if (::GetModuleHandle(L"sftldr_wow64.dll") ||
230 ::GetModuleHandle(L"sftldr.dll")) {
231 int process_id;
[email protected]b9057302013-03-28 12:40:38232 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id))
[email protected]0a194552011-09-14 17:53:35233 return false;
234 is_virtualized_ = true;
235 // The new window was spawned from WMI, and won't be in the foreground.
236 // So, first we sleep while the new chrome.exe instance starts (because
237 // WaitForInputIdle doesn't work here). Then we poll for up to two more
238 // seconds and make the window foreground if we find it (or we give up).
239 HWND hwnd = 0;
240 ::Sleep(90);
241 for (int tries = 200; tries; --tries) {
[email protected]b9057302013-03-28 12:40:38242 hwnd = ::FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass,
243 user_data_dir.value().c_str());
[email protected]0a194552011-09-14 17:53:35244 if (hwnd) {
245 ::SetForegroundWindow(hwnd);
246 break;
247 }
248 ::Sleep(10);
249 }
250 return true;
251 }
252 return false;
253}
254
[email protected]dd85d452013-03-28 12:39:59255ProcessSingleton::ProcessSingleton(
256 const base::FilePath& user_data_dir,
257 const NotificationCallback& notification_callback)
[email protected]9a47c432013-04-19 20:33:55258 : window_(NULL), notification_callback_(notification_callback),
[email protected]14649ecd2012-12-05 01:00:24259 is_virtualized_(false), lock_file_(INVALID_HANDLE_VALUE),
260 user_data_dir_(user_data_dir) {
[email protected]fc14cef2009-01-27 22:17:29261}
262
[email protected]7c47ae3e2009-02-18 00:34:21263ProcessSingleton::~ProcessSingleton() {
[email protected]a9b36c92012-06-18 08:47:57264 // We need to unregister the window as late as possible so that we can detect
265 // another instance of chrome running. Otherwise we may end up writing out
266 // data while a new chrome is starting up.
267 if (window_) {
268 ::DestroyWindow(window_);
269 ::UnregisterClass(chrome::kMessageWindowClass,
270 base::GetModuleFromAddress(&ThunkWndProc));
271 }
[email protected]2b7ff5b92012-07-17 22:45:18272 if (lock_file_ != INVALID_HANDLE_VALUE)
[email protected]b9057302013-03-28 12:40:38273 ::CloseHandle(lock_file_);
[email protected]fc14cef2009-01-27 22:17:29274}
275
[email protected]14649ecd2012-12-05 01:00:24276// Code roughly based on Mozilla.
[email protected]9f20a6d02009-08-21 01:18:37277ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
[email protected]0a194552011-09-14 17:53:35278 if (is_virtualized_)
279 return PROCESS_NOTIFIED; // We already spawned the process in this case.
[email protected]e9613b52012-11-27 22:35:13280 if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
[email protected]2b7ff5b92012-07-17 22:45:18281 return LOCK_ERROR;
[email protected]e9613b52012-11-27 22:35:13282 } else if (!remote_window_) {
[email protected]9f20a6d02009-08-21 01:18:37283 return PROCESS_NONE;
[email protected]e9613b52012-11-27 22:35:13284 }
[email protected]fc14cef2009-01-27 22:17:29285
[email protected]f394bc62012-08-22 21:42:29286 DWORD process_id = 0;
[email protected]b9057302013-03-28 12:40:38287 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
[email protected]f394bc62012-08-22 21:42:29288 // It is possible that the process owning this window may have died by now.
289 if (!thread_id || !process_id) {
290 remote_window_ = NULL;
291 return PROCESS_NONE;
292 }
293
294 if (base::win::IsMetroProcess()) {
295 // Interesting corner case. We are launched as a metro process but we
296 // found another chrome running. Since metro enforces single instance then
297 // the other chrome must be desktop chrome and this must be a search charm
298 // activation. This scenario is unique; other cases should be properly
299 // handled by the delegate_execute which will not activate a second chrome.
300 string16 terms;
301 base::win::MetroLaunchType launch = base::win::GetMetroLaunchParams(&terms);
302 if (launch != base::win::METRO_SEARCH) {
303 LOG(WARNING) << "In metro mode, but and launch is " << launch;
304 } else {
305 std::string query = net::EscapeQueryParamValue(UTF16ToUTF8(terms), true);
306 std::string url = base::StringPrintf(kSearchUrl, query.c_str());
307 SHELLEXECUTEINFOA sei = { sizeof(sei) };
308 sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
309 sei.nShow = SW_SHOWNORMAL;
310 sei.lpFile = url.c_str();
[email protected]b9057302013-03-28 12:40:38311 ::OutputDebugStringA(sei.lpFile);
[email protected]f394bc62012-08-22 21:42:29312 sei.lpDirectory = "";
313 ::ShellExecuteExA(&sei);
314 }
315 return PROCESS_NOTIFIED;
316 }
[email protected]ec6e7d72012-12-06 01:30:42317
[email protected]f394bc62012-08-22 21:42:29318 // Non-metro mode, send our command line to the other chrome message window.
[email protected]fc14cef2009-01-27 22:17:29319 // format is "START\0<<<current directory>>>\0<<<commandline>>>".
320 std::wstring to_send(L"START\0", 6); // want the NULL in the string.
[email protected]650b2d52013-02-10 03:41:45321 base::FilePath cur_dir;
[email protected]fc14cef2009-01-27 22:17:29322 if (!PathService::Get(base::DIR_CURRENT, &cur_dir))
[email protected]9f20a6d02009-08-21 01:18:37323 return PROCESS_NONE;
[email protected]b9696482010-11-30 23:56:18324 to_send.append(cur_dir.value());
[email protected]fc14cef2009-01-27 22:17:29325 to_send.append(L"\0", 1); // Null separator.
[email protected]b9057302013-03-28 12:40:38326 to_send.append(::GetCommandLineW());
[email protected]fc14cef2009-01-27 22:17:29327 to_send.append(L"\0", 1); // Null separator.
328
[email protected]ec6e7d72012-12-06 01:30:42329 base::win::ScopedHandle process_handle;
330 if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
331 base::OpenProcessHandleWithAccess(
332 process_id, PROCESS_QUERY_INFORMATION,
333 process_handle.Receive()) &&
334 base::win::IsProcessImmersive(process_handle.Get())) {
[email protected]281b5da2013-03-05 06:13:46335 chrome::ActivateMetroChrome();
[email protected]ec6e7d72012-12-06 01:30:42336 }
337
[email protected]fc14cef2009-01-27 22:17:29338 // Allow the current running browser window making itself the foreground
339 // window (otherwise it will just flash in the taskbar).
[email protected]b9057302013-03-28 12:40:38340 ::AllowSetForegroundWindow(process_id);
[email protected]fc14cef2009-01-27 22:17:29341
[email protected]fc14cef2009-01-27 22:17:29342 COPYDATASTRUCT cds;
343 cds.dwData = 0;
344 cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
345 cds.lpData = const_cast<wchar_t*>(to_send.c_str());
346 DWORD_PTR result = 0;
[email protected]b9057302013-03-28 12:40:38347 if (::SendMessageTimeout(remote_window_,
348 WM_COPYDATA,
349 NULL,
350 reinterpret_cast<LPARAM>(&cds),
351 SMTO_ABORTIFHUNG,
352 kTimeoutInSeconds * 1000,
353 &result)) {
[email protected]0815b6d2009-02-11 00:39:37354 // It is possible that the process owning this window may have died by now.
355 if (!result) {
356 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37357 return PROCESS_NONE;
[email protected]0815b6d2009-02-11 00:39:37358 }
[email protected]9f20a6d02009-08-21 01:18:37359 return PROCESS_NOTIFIED;
[email protected]fc14cef2009-01-27 22:17:29360 }
361
[email protected]0815b6d2009-02-11 00:39:37362 // It is possible that the process owning this window may have died by now.
[email protected]b9057302013-03-28 12:40:38363 if (!::IsWindow(remote_window_)) {
[email protected]0815b6d2009-02-11 00:39:37364 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37365 return PROCESS_NONE;
[email protected]0815b6d2009-02-11 00:39:37366 }
367
[email protected]fc14cef2009-01-27 22:17:29368 // The window is hung. Scan for every window to find a visible one.
369 bool visible_window = false;
[email protected]b9057302013-03-28 12:40:38370 ::EnumThreadWindows(thread_id,
371 &BrowserWindowEnumeration,
372 reinterpret_cast<LPARAM>(&visible_window));
[email protected]fc14cef2009-01-27 22:17:29373
374 // If there is a visible browser window, ask the user before killing it.
[email protected]d33220292012-07-04 01:41:27375 if (visible_window && chrome::ShowMessageBox(NULL,
[email protected]5da155e2012-05-26 16:31:16376 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
377 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE),
[email protected]d33220292012-07-04 01:41:27378 chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) {
[email protected]5da155e2012-05-26 16:31:16379 // The user denied. Quit silently.
380 return PROCESS_NOTIFIED;
[email protected]fc14cef2009-01-27 22:17:29381 }
382
383 // Time to take action. Kill the browser process.
[email protected]1fcfb202011-07-19 19:53:14384 base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true);
[email protected]fc14cef2009-01-27 22:17:29385 remote_window_ = NULL;
[email protected]9f20a6d02009-08-21 01:18:37386 return PROCESS_NONE;
[email protected]fc14cef2009-01-27 22:17:29387}
388
[email protected]dd85d452013-03-28 12:39:59389ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcessOrCreate() {
[email protected]14649ecd2012-12-05 01:00:24390 ProcessSingleton::NotifyResult result = PROCESS_NONE;
[email protected]dd85d452013-03-28 12:39:59391 if (!Create()) {
[email protected]14649ecd2012-12-05 01:00:24392 result = NotifyOtherProcess();
393 if (result == PROCESS_NONE)
394 result = PROFILE_IN_USE;
[email protected]351f7d42012-12-07 22:42:02395 } else {
396 g_browser_process->PlatformSpecificCommandLineProcessing(
397 *CommandLine::ForCurrentProcess());
[email protected]14649ecd2012-12-05 01:00:24398 }
399 return result;
[email protected]4a44bc32010-05-28 22:22:44400}
401
[email protected]14649ecd2012-12-05 01:00:24402// Look for a Chrome instance that uses the same profile directory. If there
403// isn't one, create a message window with its title set to the profile
404// directory path.
[email protected]dd85d452013-03-28 12:39:59405bool ProcessSingleton::Create() {
[email protected]14649ecd2012-12-05 01:00:24406 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!";
407 static const wchar_t kMetroActivationEventName[] =
408 L"Local\\ChromeProcessSingletonStartupMetroActivation!";
409
[email protected]b9057302013-03-28 12:40:38410 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL,
411 chrome::kMessageWindowClass,
412 user_data_dir_.value().c_str());
[email protected]14649ecd2012-12-05 01:00:24413 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
414 // Make sure we will be the one and only process creating the window.
415 // We use a named Mutex since we are protecting against multi-process
416 // access. As documented, it's clearer to NOT request ownership on creation
417 // since it isn't guaranteed we will get it. It is better to create it
418 // without ownership and explicitly get the ownership afterward.
[email protected]b9057302013-03-28 12:40:38419 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
[email protected]14649ecd2012-12-05 01:00:24420 DPCHECK(only_me.IsValid());
421
422 AutoLockMutex auto_lock_only_me(only_me);
423
424 // We now own the mutex so we are the only process that can create the
425 // window at this time, but we must still check if someone created it
426 // between the time where we looked for it above and the time the mutex
427 // was given to us.
[email protected]b9057302013-03-28 12:40:38428 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL,
429 chrome::kMessageWindowClass,
430 user_data_dir_.value().c_str());
[email protected]14649ecd2012-12-05 01:00:24431
432
433 // In Win8+, a new Chrome process launched in Desktop mode may need to be
434 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for
435 // heuristics). To accomplish this, the current Chrome activates Metro
436 // Chrome, releases the startup mutex, and waits for metro Chrome to take
437 // the singleton. From that point onward, the command line for this Chrome
438 // process will be sent to Metro Chrome by the usual channels.
439 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 &&
440 !base::win::IsMetroProcess()) {
441 // |metro_activation_event| is created right before activating a Metro
442 // Chrome (note that there can only be one Metro Chrome process; by OS
443 // design); all following Desktop processes will then wait for this event
444 // to be signaled by Metro Chrome which will do so as soon as it grabs
445 // this singleton (should any of the waiting processes timeout waiting for
446 // the signal they will try to grab the singleton for themselves which
447 // will result in a forced Desktop Chrome launch in the worst case).
448 base::win::ScopedHandle metro_activation_event(
[email protected]b9057302013-03-28 12:40:38449 ::OpenEvent(SYNCHRONIZE, FALSE, kMetroActivationEventName));
[email protected]14649ecd2012-12-05 01:00:24450 if (!metro_activation_event.IsValid() &&
451 ShouldLaunchInWindows8ImmersiveMode(user_data_dir_)) {
452 // No Metro activation is under way, but the desire is to launch in
453 // Metro mode: activate and rendez-vous with the activated process.
454 metro_activation_event.Set(
[email protected]b9057302013-03-28 12:40:38455 ::CreateEvent(NULL, TRUE, FALSE, kMetroActivationEventName));
[email protected]281b5da2013-03-05 06:13:46456 if (!chrome::ActivateMetroChrome()) {
[email protected]14649ecd2012-12-05 01:00:24457 // Failed to launch immersive Chrome, default to launching on Desktop.
458 LOG(ERROR) << "Failed to launch immersive chrome";
459 metro_activation_event.Close();
460 }
461 }
462
463 if (metro_activation_event.IsValid()) {
464 // Release |only_me| (to let Metro Chrome grab this singleton) and wait
465 // until the event is signaled (i.e. Metro Chrome was successfully
466 // activated). Ignore timeout waiting for |metro_activation_event|.
467 {
468 AutoUnlockMutex auto_unlock_only_me(only_me);
469
[email protected]b9057302013-03-28 12:40:38470 DWORD result = ::WaitForSingleObject(metro_activation_event,
471 kMetroChromeActivationTimeoutMs);
[email protected]14649ecd2012-12-05 01:00:24472 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
473 << "Result = " << result;
474 }
475
476 // Check if this singleton was successfully grabbed by another process
477 // (hopefully Metro Chrome). Failing to do so, this process will grab
478 // the singleton and launch in Desktop mode.
[email protected]b9057302013-03-28 12:40:38479 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL,
480 chrome::kMessageWindowClass,
481 user_data_dir_.value().c_str());
[email protected]14649ecd2012-12-05 01:00:24482 }
483 }
484
485 if (!remote_window_) {
486 // We have to make sure there is no Chrome instance running on another
487 // machine that uses the same profile.
[email protected]650b2d52013-02-10 03:41:45488 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
[email protected]b9057302013-03-28 12:40:38489 lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
490 GENERIC_WRITE,
491 FILE_SHARE_READ,
492 NULL,
493 CREATE_ALWAYS,
494 FILE_ATTRIBUTE_NORMAL |
495 FILE_FLAG_DELETE_ON_CLOSE,
496 NULL);
497 DWORD error = ::GetLastError();
[email protected]14649ecd2012-12-05 01:00:24498 LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
499 error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
500 LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
501 << "Lock file can not be created! Error code: " << error;
502
503 if (lock_file_ != INVALID_HANDLE_VALUE) {
504 HINSTANCE hinst = base::GetModuleFromAddress(&ThunkWndProc);
505
506 WNDCLASSEX wc = {0};
507 wc.cbSize = sizeof(wc);
508 wc.lpfnWndProc = base::win::WrappedWindowProc<ThunkWndProc>;
509 wc.hInstance = hinst;
510 wc.lpszClassName = chrome::kMessageWindowClass;
511 ATOM clazz = ::RegisterClassEx(&wc);
512 DCHECK(clazz);
513
514 // Set the window's title to the path of our user data directory so
515 // other Chrome instances can decide if they should forward to us.
516 window_ = ::CreateWindow(MAKEINTATOM(clazz),
517 user_data_dir_.value().c_str(),
518 0, 0, 0, 0, 0, HWND_MESSAGE, 0, hinst, this);
519 CHECK(window_);
520 }
521
522 if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
523 // Make sure no one is still waiting on Metro activation whether it
524 // succeeded (i.e., this is the Metro process) or failed.
525 base::win::ScopedHandle metro_activation_event(
[email protected]b9057302013-03-28 12:40:38526 ::OpenEvent(EVENT_MODIFY_STATE, FALSE, kMetroActivationEventName));
[email protected]14649ecd2012-12-05 01:00:24527 if (metro_activation_event.IsValid())
[email protected]b9057302013-03-28 12:40:38528 ::SetEvent(metro_activation_event);
[email protected]14649ecd2012-12-05 01:00:24529 }
530 }
531 }
532
[email protected]9a182832012-02-10 18:45:58533 return window_ != NULL;
[email protected]fc14cef2009-01-27 22:17:29534}
535
[email protected]9f20a6d02009-08-21 01:18:37536void ProcessSingleton::Cleanup() {
537}
538
[email protected]7c47ae3e2009-02-18 00:34:21539LRESULT ProcessSingleton::OnCopyData(HWND hwnd, const COPYDATASTRUCT* cds) {
[email protected]edf04b512012-02-23 09:47:43540 CommandLine parsed_command_line(CommandLine::NO_PROGRAM);
[email protected]650b2d52013-02-10 03:41:45541 base::FilePath current_directory;
[email protected]edf04b512012-02-23 09:47:43542 if (!ParseCommandLine(cds, &parsed_command_line, &current_directory))
[email protected]fc14cef2009-01-27 22:17:29543 return TRUE;
[email protected]5d364542012-04-05 07:15:39544 return notification_callback_.Run(parsed_command_line, current_directory) ?
545 TRUE : FALSE;
[email protected]fc14cef2009-01-27 22:17:29546}
547
[email protected]e3ce40a2012-01-31 03:03:03548LRESULT ProcessSingleton::WndProc(HWND hwnd, UINT message,
549 WPARAM wparam, LPARAM lparam) {
[email protected]fc14cef2009-01-27 22:17:29550 switch (message) {
551 case WM_COPYDATA:
552 return OnCopyData(reinterpret_cast<HWND>(wparam),
553 reinterpret_cast<COPYDATASTRUCT*>(lparam));
554 default:
555 break;
556 }
557
558 return ::DefWindowProc(hwnd, message, wparam, lparam);
559}