OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/process_singleton.h" | 5 #include "chrome/browser/process_singleton.h" |
6 | 6 |
7 #include <shellapi.h> | 7 #include <shellapi.h> |
8 | 8 |
9 #include "base/base_paths.h" | 9 #include "base/base_paths.h" |
10 #include "base/command_line.h" | 10 #include "base/command_line.h" |
11 #include "base/files/file_path.h" | 11 #include "base/files/file_path.h" |
12 #include "base/path_service.h" | 12 #include "base/path_service.h" |
13 #include "base/process_util.h" | 13 #include "base/process_util.h" |
14 #include "base/stringprintf.h" | 14 #include "base/stringprintf.h" |
15 #include "base/utf_string_conversions.h" | 15 #include "base/utf_string_conversions.h" |
16 #include "base/win/metro.h" | 16 #include "base/win/metro.h" |
17 #include "base/win/registry.h" | 17 #include "base/win/registry.h" |
18 #include "base/win/scoped_handle.h" | 18 #include "base/win/scoped_handle.h" |
19 #include "base/win/win_util.h" | 19 #include "base/win/win_util.h" |
20 #include "base/win/windows_version.h" | 20 #include "base/win/windows_version.h" |
21 #include "base/win/wrapped_window_proc.h" | 21 #include "base/win/wrapped_window_proc.h" |
22 #include "chrome/browser/browser_process.h" | 22 #include "chrome/browser/browser_process.h" |
| 23 #include "chrome/browser/chrome_process_finder_win.h" |
23 #include "chrome/browser/shell_integration.h" | 24 #include "chrome/browser/shell_integration.h" |
24 #include "chrome/browser/ui/metro_chrome_win.h" | 25 #include "chrome/browser/ui/metro_chrome_win.h" |
25 #include "chrome/browser/ui/simple_message_box.h" | 26 #include "chrome/browser/ui/simple_message_box.h" |
26 #include "chrome/common/chrome_constants.h" | 27 #include "chrome/common/chrome_constants.h" |
27 #include "chrome/common/chrome_paths.h" | 28 #include "chrome/common/chrome_paths.h" |
28 #include "chrome/common/chrome_paths_internal.h" | 29 #include "chrome/common/chrome_paths_internal.h" |
29 #include "chrome/common/chrome_switches.h" | 30 #include "chrome/common/chrome_switches.h" |
30 #include "chrome/installer/util/wmi.h" | 31 #include "chrome/installer/util/wmi.h" |
31 #include "content/public/common/result_codes.h" | 32 #include "content/public/common/result_codes.h" |
32 #include "grit/chromium_strings.h" | 33 #include "grit/chromium_strings.h" |
33 #include "grit/generated_resources.h" | 34 #include "grit/generated_resources.h" |
34 #include "net/base/escape.h" | 35 #include "net/base/escape.h" |
35 #include "ui/base/l10n/l10n_util.h" | 36 #include "ui/base/l10n/l10n_util.h" |
36 #include "ui/base/win/hwnd_util.h" | 37 #include "ui/base/win/hwnd_util.h" |
37 | 38 |
38 namespace { | 39 namespace { |
39 | 40 |
40 const char kLockfile[] = "lockfile"; | 41 const char kLockfile[] = "lockfile"; |
41 | 42 |
42 const char kSearchUrl[] = | |
43 "http://www.google.com/search?q=%s&sourceid=chrome&ie=UTF-8"; | |
44 | |
45 const int kMetroChromeActivationTimeoutMs = 3000; | 43 const int kMetroChromeActivationTimeoutMs = 3000; |
46 | 44 |
47 // A helper class that acquires the given |mutex| while the AutoLockMutex is in | 45 // A helper class that acquires the given |mutex| while the AutoLockMutex is in |
48 // scope. | 46 // scope. |
49 class AutoLockMutex { | 47 class AutoLockMutex { |
50 public: | 48 public: |
51 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) { | 49 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) { |
52 DWORD result = ::WaitForSingleObject(mutex_, INFINITE); | 50 DWORD result = ::WaitForSingleObject(mutex_, INFINITE); |
53 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result; | 51 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result; |
54 } | 52 } |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
236 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id)) | 234 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id)) |
237 return false; | 235 return false; |
238 is_virtualized_ = true; | 236 is_virtualized_ = true; |
239 // The new window was spawned from WMI, and won't be in the foreground. | 237 // The new window was spawned from WMI, and won't be in the foreground. |
240 // So, first we sleep while the new chrome.exe instance starts (because | 238 // So, first we sleep while the new chrome.exe instance starts (because |
241 // WaitForInputIdle doesn't work here). Then we poll for up to two more | 239 // WaitForInputIdle doesn't work here). Then we poll for up to two more |
242 // seconds and make the window foreground if we find it (or we give up). | 240 // seconds and make the window foreground if we find it (or we give up). |
243 HWND hwnd = 0; | 241 HWND hwnd = 0; |
244 ::Sleep(90); | 242 ::Sleep(90); |
245 for (int tries = 200; tries; --tries) { | 243 for (int tries = 200; tries; --tries) { |
246 hwnd = ::FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass, | 244 hwnd = chrome::FindRunningChromeWindow(user_data_dir); |
247 user_data_dir.value().c_str()); | |
248 if (hwnd) { | 245 if (hwnd) { |
249 ::SetForegroundWindow(hwnd); | 246 ::SetForegroundWindow(hwnd); |
250 break; | 247 break; |
251 } | 248 } |
252 ::Sleep(10); | 249 ::Sleep(10); |
253 } | 250 } |
254 return true; | 251 return true; |
255 } | 252 } |
256 return false; | 253 return false; |
257 } | 254 } |
(...skipping 30 matching lines...) Expand all Loading... |
288 } | 285 } |
289 | 286 |
290 DWORD process_id = 0; | 287 DWORD process_id = 0; |
291 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id); | 288 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id); |
292 // It is possible that the process owning this window may have died by now. | 289 // It is possible that the process owning this window may have died by now. |
293 if (!thread_id || !process_id) { | 290 if (!thread_id || !process_id) { |
294 remote_window_ = NULL; | 291 remote_window_ = NULL; |
295 return PROCESS_NONE; | 292 return PROCESS_NONE; |
296 } | 293 } |
297 | 294 |
298 if (base::win::IsMetroProcess()) { | 295 if (chrome::AttemptToNotifyRunningChrome(remote_window_)) |
299 // Interesting corner case. We are launched as a metro process but we | |
300 // found another chrome running. Since metro enforces single instance then | |
301 // the other chrome must be desktop chrome and this must be a search charm | |
302 // activation. This scenario is unique; other cases should be properly | |
303 // handled by the delegate_execute which will not activate a second chrome. | |
304 string16 terms; | |
305 base::win::MetroLaunchType launch = base::win::GetMetroLaunchParams(&terms); | |
306 if (launch != base::win::METRO_SEARCH) { | |
307 LOG(WARNING) << "In metro mode, but and launch is " << launch; | |
308 } else { | |
309 std::string query = net::EscapeQueryParamValue(UTF16ToUTF8(terms), true); | |
310 std::string url = base::StringPrintf(kSearchUrl, query.c_str()); | |
311 SHELLEXECUTEINFOA sei = { sizeof(sei) }; | |
312 sei.fMask = SEE_MASK_FLAG_LOG_USAGE; | |
313 sei.nShow = SW_SHOWNORMAL; | |
314 sei.lpFile = url.c_str(); | |
315 ::OutputDebugStringA(sei.lpFile); | |
316 sei.lpDirectory = ""; | |
317 ::ShellExecuteExA(&sei); | |
318 } | |
319 return PROCESS_NOTIFIED; | 296 return PROCESS_NOTIFIED; |
320 } | |
321 | |
322 // Non-metro mode, send our command line to the other chrome message window. | |
323 // format is "START\0<<<current directory>>>\0<<<commandline>>>". | |
324 std::wstring to_send(L"START\0", 6); // want the NULL in the string. | |
325 base::FilePath cur_dir; | |
326 if (!PathService::Get(base::DIR_CURRENT, &cur_dir)) | |
327 return PROCESS_NONE; | |
328 to_send.append(cur_dir.value()); | |
329 to_send.append(L"\0", 1); // Null separator. | |
330 to_send.append(::GetCommandLineW()); | |
331 to_send.append(L"\0", 1); // Null separator. | |
332 | |
333 base::win::ScopedHandle process_handle; | |
334 if (base::win::GetVersion() >= base::win::VERSION_WIN8 && | |
335 base::OpenProcessHandleWithAccess( | |
336 process_id, PROCESS_QUERY_INFORMATION, | |
337 process_handle.Receive()) && | |
338 base::win::IsProcessImmersive(process_handle.Get())) { | |
339 chrome::ActivateMetroChrome(); | |
340 } | |
341 | |
342 // Allow the current running browser window making itself the foreground | |
343 // window (otherwise it will just flash in the taskbar). | |
344 ::AllowSetForegroundWindow(process_id); | |
345 | |
346 COPYDATASTRUCT cds; | |
347 cds.dwData = 0; | |
348 cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t)); | |
349 cds.lpData = const_cast<wchar_t*>(to_send.c_str()); | |
350 DWORD_PTR result = 0; | |
351 if (::SendMessageTimeout(remote_window_, | |
352 WM_COPYDATA, | |
353 NULL, | |
354 reinterpret_cast<LPARAM>(&cds), | |
355 SMTO_ABORTIFHUNG, | |
356 kTimeoutInSeconds * 1000, | |
357 &result)) { | |
358 // It is possible that the process owning this window may have died by now. | |
359 if (!result) { | |
360 remote_window_ = NULL; | |
361 return PROCESS_NONE; | |
362 } | |
363 return PROCESS_NOTIFIED; | |
364 } | |
365 | 297 |
366 // It is possible that the process owning this window may have died by now. | 298 // It is possible that the process owning this window may have died by now. |
367 if (!::IsWindow(remote_window_)) { | 299 if (!::IsWindow(remote_window_)) { |
368 remote_window_ = NULL; | 300 remote_window_ = NULL; |
369 return PROCESS_NONE; | 301 return PROCESS_NONE; |
370 } | 302 } |
371 | 303 |
372 // The window is hung. Scan for every window to find a visible one. | 304 // The window is hung. Scan for every window to find a visible one. |
373 bool visible_window = false; | 305 bool visible_window = false; |
374 ::EnumThreadWindows(thread_id, | 306 ::EnumThreadWindows(thread_id, |
(...skipping 29 matching lines...) Expand all Loading... |
404 } | 336 } |
405 | 337 |
406 // Look for a Chrome instance that uses the same profile directory. If there | 338 // Look for a Chrome instance that uses the same profile directory. If there |
407 // isn't one, create a message window with its title set to the profile | 339 // isn't one, create a message window with its title set to the profile |
408 // directory path. | 340 // directory path. |
409 bool ProcessSingleton::Create() { | 341 bool ProcessSingleton::Create() { |
410 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; | 342 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; |
411 static const wchar_t kMetroActivationEventName[] = | 343 static const wchar_t kMetroActivationEventName[] = |
412 L"Local\\ChromeProcessSingletonStartupMetroActivation!"; | 344 L"Local\\ChromeProcessSingletonStartupMetroActivation!"; |
413 | 345 |
414 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, | 346 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); |
415 chrome::kMessageWindowClass, | |
416 user_data_dir_.value().c_str()); | |
417 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) { | 347 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) { |
418 // Make sure we will be the one and only process creating the window. | 348 // Make sure we will be the one and only process creating the window. |
419 // We use a named Mutex since we are protecting against multi-process | 349 // We use a named Mutex since we are protecting against multi-process |
420 // access. As documented, it's clearer to NOT request ownership on creation | 350 // access. As documented, it's clearer to NOT request ownership on creation |
421 // since it isn't guaranteed we will get it. It is better to create it | 351 // since it isn't guaranteed we will get it. It is better to create it |
422 // without ownership and explicitly get the ownership afterward. | 352 // without ownership and explicitly get the ownership afterward. |
423 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); | 353 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); |
424 DPCHECK(only_me.IsValid()); | 354 DPCHECK(only_me.IsValid()); |
425 | 355 |
426 AutoLockMutex auto_lock_only_me(only_me); | 356 AutoLockMutex auto_lock_only_me(only_me); |
427 | 357 |
428 // We now own the mutex so we are the only process that can create the | 358 // We now own the mutex so we are the only process that can create the |
429 // window at this time, but we must still check if someone created it | 359 // window at this time, but we must still check if someone created it |
430 // between the time where we looked for it above and the time the mutex | 360 // between the time where we looked for it above and the time the mutex |
431 // was given to us. | 361 // was given to us. |
432 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, | 362 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); |
433 chrome::kMessageWindowClass, | |
434 user_data_dir_.value().c_str()); | |
435 | 363 |
436 | 364 |
437 // In Win8+, a new Chrome process launched in Desktop mode may need to be | 365 // In Win8+, a new Chrome process launched in Desktop mode may need to be |
438 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for | 366 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for |
439 // heuristics). To accomplish this, the current Chrome activates Metro | 367 // heuristics). To accomplish this, the current Chrome activates Metro |
440 // Chrome, releases the startup mutex, and waits for metro Chrome to take | 368 // Chrome, releases the startup mutex, and waits for metro Chrome to take |
441 // the singleton. From that point onward, the command line for this Chrome | 369 // the singleton. From that point onward, the command line for this Chrome |
442 // process will be sent to Metro Chrome by the usual channels. | 370 // process will be sent to Metro Chrome by the usual channels. |
443 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 && | 371 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 && |
444 !base::win::IsMetroProcess()) { | 372 !base::win::IsMetroProcess()) { |
(...skipping 28 matching lines...) Expand all Loading... |
473 | 401 |
474 DWORD result = ::WaitForSingleObject(metro_activation_event, | 402 DWORD result = ::WaitForSingleObject(metro_activation_event, |
475 kMetroChromeActivationTimeoutMs); | 403 kMetroChromeActivationTimeoutMs); |
476 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) | 404 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) |
477 << "Result = " << result; | 405 << "Result = " << result; |
478 } | 406 } |
479 | 407 |
480 // Check if this singleton was successfully grabbed by another process | 408 // Check if this singleton was successfully grabbed by another process |
481 // (hopefully Metro Chrome). Failing to do so, this process will grab | 409 // (hopefully Metro Chrome). Failing to do so, this process will grab |
482 // the singleton and launch in Desktop mode. | 410 // the singleton and launch in Desktop mode. |
483 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, | 411 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); |
484 chrome::kMessageWindowClass, | |
485 user_data_dir_.value().c_str()); | |
486 } | 412 } |
487 } | 413 } |
488 | 414 |
489 if (!remote_window_) { | 415 if (!remote_window_) { |
490 // We have to make sure there is no Chrome instance running on another | 416 // We have to make sure there is no Chrome instance running on another |
491 // machine that uses the same profile. | 417 // machine that uses the same profile. |
492 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); | 418 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); |
493 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), | 419 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), |
494 GENERIC_WRITE, | 420 GENERIC_WRITE, |
495 FILE_SHARE_READ, | 421 FILE_SHARE_READ, |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
554 switch (message) { | 480 switch (message) { |
555 case WM_COPYDATA: | 481 case WM_COPYDATA: |
556 return OnCopyData(reinterpret_cast<HWND>(wparam), | 482 return OnCopyData(reinterpret_cast<HWND>(wparam), |
557 reinterpret_cast<COPYDATASTRUCT*>(lparam)); | 483 reinterpret_cast<COPYDATASTRUCT*>(lparam)); |
558 default: | 484 default: |
559 break; | 485 break; |
560 } | 486 } |
561 | 487 |
562 return ::DefWindowProc(hwnd, message, wparam, lparam); | 488 return ::DefWindowProc(hwnd, message, wparam, lparam); |
563 } | 489 } |
OLD | NEW |