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_info.h" | 13 #include "base/process_info.h" |
14 #include "base/process_util.h" | 14 #include "base/process_util.h" |
15 #include "base/stringprintf.h" | 15 #include "base/stringprintf.h" |
16 #include "base/strings/string_number_conversions.h" | 16 #include "base/strings/string_number_conversions.h" |
17 #include "base/time.h" | 17 #include "base/time.h" |
18 #include "base/utf_string_conversions.h" | 18 #include "base/utf_string_conversions.h" |
19 #include "base/win/metro.h" | 19 #include "base/win/metro.h" |
20 #include "base/win/registry.h" | 20 #include "base/win/registry.h" |
21 #include "base/win/scoped_handle.h" | 21 #include "base/win/scoped_handle.h" |
22 #include "base/win/win_util.h" | 22 #include "base/win/win_util.h" |
23 #include "base/win/windows_version.h" | 23 #include "base/win/windows_version.h" |
24 #include "base/win/wrapped_window_proc.h" | 24 #include "base/win/wrapped_window_proc.h" |
25 #include "chrome/browser/browser_process.h" | 25 #include "chrome/browser/browser_process.h" |
26 #include "chrome/browser/chrome_process_finder_win.h" | |
27 #include "chrome/browser/metro_utils/metro_chrome_win.h" | |
26 #include "chrome/browser/shell_integration.h" | 28 #include "chrome/browser/shell_integration.h" |
27 #include "chrome/browser/ui/metro_chrome_win.h" | |
28 #include "chrome/browser/ui/simple_message_box.h" | 29 #include "chrome/browser/ui/simple_message_box.h" |
29 #include "chrome/common/chrome_constants.h" | 30 #include "chrome/common/chrome_constants.h" |
30 #include "chrome/common/chrome_paths.h" | 31 #include "chrome/common/chrome_paths.h" |
31 #include "chrome/common/chrome_paths_internal.h" | 32 #include "chrome/common/chrome_paths_internal.h" |
32 #include "chrome/common/chrome_switches.h" | 33 #include "chrome/common/chrome_switches.h" |
33 #include "chrome/installer/util/wmi.h" | 34 #include "chrome/installer/util/wmi.h" |
34 #include "content/public/common/result_codes.h" | 35 #include "content/public/common/result_codes.h" |
35 #include "grit/chromium_strings.h" | 36 #include "grit/chromium_strings.h" |
36 #include "grit/generated_resources.h" | 37 #include "grit/generated_resources.h" |
37 #include "net/base/escape.h" | 38 #include "net/base/escape.h" |
38 #include "ui/base/l10n/l10n_util.h" | 39 #include "ui/base/l10n/l10n_util.h" |
39 #include "ui/base/win/hwnd_util.h" | 40 #include "ui/base/win/hwnd_util.h" |
40 | 41 |
41 #if defined(USE_AURA) | 42 #if defined(USE_AURA) |
42 #include "chrome/browser/browser_process_platform_part_aurawin.h" | 43 #include "chrome/browser/browser_process_platform_part_aurawin.h" |
43 #else | 44 #else |
44 #include "chrome/browser/browser_process_platform_part.h" | 45 #include "chrome/browser/browser_process_platform_part.h" |
45 #endif | 46 #endif |
46 | 47 |
47 namespace { | 48 namespace { |
48 | 49 |
49 const char kLockfile[] = "lockfile"; | 50 const char kLockfile[] = "lockfile"; |
50 | 51 |
51 const char kSearchUrl[] = | |
52 "http://www.google.com/search?q=%s&sourceid=chrome&ie=UTF-8"; | |
53 | |
54 const int kMetroChromeActivationTimeoutMs = 3000; | 52 const int kMetroChromeActivationTimeoutMs = 3000; |
55 | 53 |
56 // A helper class that acquires the given |mutex| while the AutoLockMutex is in | 54 // A helper class that acquires the given |mutex| while the AutoLockMutex is in |
57 // scope. | 55 // scope. |
58 class AutoLockMutex { | 56 class AutoLockMutex { |
59 public: | 57 public: |
60 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) { | 58 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) { |
61 DWORD result = ::WaitForSingleObject(mutex_, INFINITE); | 59 DWORD result = ::WaitForSingleObject(mutex_, INFINITE); |
62 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result; | 60 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result; |
63 } | 61 } |
(...skipping 181 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
245 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id)) | 243 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id)) |
246 return false; | 244 return false; |
247 is_virtualized_ = true; | 245 is_virtualized_ = true; |
248 // The new window was spawned from WMI, and won't be in the foreground. | 246 // The new window was spawned from WMI, and won't be in the foreground. |
249 // So, first we sleep while the new chrome.exe instance starts (because | 247 // So, first we sleep while the new chrome.exe instance starts (because |
250 // WaitForInputIdle doesn't work here). Then we poll for up to two more | 248 // WaitForInputIdle doesn't work here). Then we poll for up to two more |
251 // seconds and make the window foreground if we find it (or we give up). | 249 // seconds and make the window foreground if we find it (or we give up). |
252 HWND hwnd = 0; | 250 HWND hwnd = 0; |
253 ::Sleep(90); | 251 ::Sleep(90); |
254 for (int tries = 200; tries; --tries) { | 252 for (int tries = 200; tries; --tries) { |
255 hwnd = ::FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass, | 253 hwnd = chrome::FindRunningChromeWindow(user_data_dir); |
256 user_data_dir.value().c_str()); | |
257 if (hwnd) { | 254 if (hwnd) { |
258 ::SetForegroundWindow(hwnd); | 255 ::SetForegroundWindow(hwnd); |
259 break; | 256 break; |
260 } | 257 } |
261 ::Sleep(10); | 258 ::Sleep(10); |
262 } | 259 } |
263 return true; | 260 return true; |
264 } | 261 } |
265 return false; | 262 return false; |
266 } | 263 } |
(...skipping 23 matching lines...) Expand all Loading... | |
290 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() { | 287 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() { |
291 if (is_virtualized_) | 288 if (is_virtualized_) |
292 return PROCESS_NOTIFIED; // We already spawned the process in this case. | 289 return PROCESS_NOTIFIED; // We already spawned the process in this case. |
293 if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) { | 290 if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) { |
294 return LOCK_ERROR; | 291 return LOCK_ERROR; |
295 } else if (!remote_window_) { | 292 } else if (!remote_window_) { |
296 return PROCESS_NONE; | 293 return PROCESS_NONE; |
297 } | 294 } |
298 | 295 |
299 DWORD process_id = 0; | 296 DWORD process_id = 0; |
300 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id); | 297 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id); |
gab
2013/05/21 19:48:40
This variable is now only needed in the NOTIFY_WIN
koz (OOO until 15th September)
2013/05/23 06:11:49
Done, and removed the 0 checks below as they occur
| |
301 // 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. |
302 if (!thread_id || !process_id) { | 299 if (!thread_id || !process_id) { |
303 remote_window_ = NULL; | 300 remote_window_ = NULL; |
304 return PROCESS_NONE; | 301 return PROCESS_NONE; |
305 } | 302 } |
306 | 303 |
307 if (base::win::IsMetroProcess()) { | 304 switch (chrome::AttemptToNotifyRunningChrome(remote_window_)) { |
308 // Interesting corner case. We are launched as a metro process but we | 305 case chrome::NOTIFY_SUCCESS: |
309 // found another chrome running. Since metro enforces single instance then | 306 return PROCESS_NOTIFIED; |
310 // the other chrome must be desktop chrome and this must be a search charm | 307 case chrome::NOTIFY_FAILED: |
311 // activation. This scenario is unique; other cases should be properly | |
312 // handled by the delegate_execute which will not activate a second chrome. | |
313 string16 terms; | |
314 base::win::MetroLaunchType launch = base::win::GetMetroLaunchParams(&terms); | |
315 if (launch != base::win::METRO_SEARCH) { | |
316 LOG(WARNING) << "In metro mode, but and launch is " << launch; | |
317 } else { | |
318 std::string query = net::EscapeQueryParamValue(UTF16ToUTF8(terms), true); | |
319 std::string url = base::StringPrintf(kSearchUrl, query.c_str()); | |
320 SHELLEXECUTEINFOA sei = { sizeof(sei) }; | |
321 sei.fMask = SEE_MASK_FLAG_LOG_USAGE; | |
322 sei.nShow = SW_SHOWNORMAL; | |
323 sei.lpFile = url.c_str(); | |
324 ::OutputDebugStringA(sei.lpFile); | |
325 sei.lpDirectory = ""; | |
326 ::ShellExecuteExA(&sei); | |
327 } | |
328 return PROCESS_NOTIFIED; | |
329 } | |
330 | |
331 // Non-metro mode, send our command line to the other chrome message window. | |
332 // format is "START\0<<<current directory>>>\0<<<commandline>>>". | |
333 std::wstring to_send(L"START\0", 6); // want the NULL in the string. | |
334 base::FilePath cur_dir; | |
335 if (!PathService::Get(base::DIR_CURRENT, &cur_dir)) | |
336 return PROCESS_NONE; | |
337 to_send.append(cur_dir.value()); | |
338 to_send.append(L"\0", 1); // Null separator. | |
339 to_send.append(::GetCommandLineW()); | |
340 // Add the process start time as a flag. | |
341 to_send.append(L" --"); | |
342 to_send.append(ASCIIToWide(switches::kOriginalProcessStartTime)); | |
343 to_send.append(L"="); | |
344 to_send.append(base::Int64ToString16( | |
345 base::CurrentProcessInfo::CreationTime()->ToInternalValue())); | |
346 to_send.append(L"\0", 1); // Null separator. | |
347 | |
348 base::win::ScopedHandle process_handle; | |
349 if (base::win::GetVersion() >= base::win::VERSION_WIN8 && | |
350 base::OpenProcessHandleWithAccess( | |
351 process_id, PROCESS_QUERY_INFORMATION, | |
352 process_handle.Receive()) && | |
353 base::win::IsProcessImmersive(process_handle.Get())) { | |
354 chrome::ActivateMetroChrome(); | |
355 } | |
356 | |
357 // Allow the current running browser window making itself the foreground | |
358 // window (otherwise it will just flash in the taskbar). | |
359 ::AllowSetForegroundWindow(process_id); | |
360 | |
361 COPYDATASTRUCT cds; | |
362 cds.dwData = 0; | |
363 cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t)); | |
364 cds.lpData = const_cast<wchar_t*>(to_send.c_str()); | |
365 DWORD_PTR result = 0; | |
366 if (::SendMessageTimeout(remote_window_, | |
367 WM_COPYDATA, | |
368 NULL, | |
369 reinterpret_cast<LPARAM>(&cds), | |
370 SMTO_ABORTIFHUNG, | |
371 kTimeoutInSeconds * 1000, | |
372 &result)) { | |
373 // It is possible that the process owning this window may have died by now. | |
374 if (!result) { | |
375 remote_window_ = NULL; | 308 remote_window_ = NULL; |
376 return PROCESS_NONE; | 309 return PROCESS_NONE; |
377 } | 310 case chrome::NOTIFY_WINDOW_HUNG: |
378 return PROCESS_NOTIFIED; | 311 remote_window_ = NULL; |
379 } | 312 break; |
380 | |
381 // It is possible that the process owning this window may have died by now. | |
382 if (!::IsWindow(remote_window_)) { | |
383 remote_window_ = NULL; | |
384 return PROCESS_NONE; | |
385 } | 313 } |
386 | 314 |
387 // The window is hung. Scan for every window to find a visible one. | 315 // The window is hung. Scan for every window to find a visible one. |
388 bool visible_window = false; | 316 bool visible_window = false; |
389 ::EnumThreadWindows(thread_id, | 317 ::EnumThreadWindows(thread_id, |
390 &BrowserWindowEnumeration, | 318 &BrowserWindowEnumeration, |
391 reinterpret_cast<LPARAM>(&visible_window)); | 319 reinterpret_cast<LPARAM>(&visible_window)); |
392 | 320 |
393 // If there is a visible browser window, ask the user before killing it. | 321 // If there is a visible browser window, ask the user before killing it. |
394 if (visible_window && chrome::ShowMessageBox(NULL, | 322 if (visible_window && chrome::ShowMessageBox(NULL, |
395 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), | 323 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), |
396 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE), | 324 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE), |
397 chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) { | 325 chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) { |
gab
2013/05/21 19:48:40
optional: While you're here would you mind fixing
koz (OOO until 15th September)
2013/05/23 06:11:49
Done.
| |
398 // The user denied. Quit silently. | 326 // The user denied. Quit silently. |
399 return PROCESS_NOTIFIED; | 327 return PROCESS_NOTIFIED; |
400 } | 328 } |
401 | 329 |
402 // Time to take action. Kill the browser process. | 330 // Time to take action. Kill the browser process. |
403 base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true); | 331 base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true); |
404 remote_window_ = NULL; | 332 remote_window_ = NULL; |
405 return PROCESS_NONE; | 333 return PROCESS_NONE; |
406 } | 334 } |
407 | 335 |
(...skipping 11 matching lines...) Expand all Loading... | |
419 } | 347 } |
420 | 348 |
421 // Look for a Chrome instance that uses the same profile directory. If there | 349 // Look for a Chrome instance that uses the same profile directory. If there |
422 // isn't one, create a message window with its title set to the profile | 350 // isn't one, create a message window with its title set to the profile |
423 // directory path. | 351 // directory path. |
424 bool ProcessSingleton::Create() { | 352 bool ProcessSingleton::Create() { |
425 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; | 353 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; |
426 static const wchar_t kMetroActivationEventName[] = | 354 static const wchar_t kMetroActivationEventName[] = |
427 L"Local\\ChromeProcessSingletonStartupMetroActivation!"; | 355 L"Local\\ChromeProcessSingletonStartupMetroActivation!"; |
428 | 356 |
429 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, | 357 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); |
430 chrome::kMessageWindowClass, | |
431 user_data_dir_.value().c_str()); | |
432 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) { | 358 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) { |
433 // Make sure we will be the one and only process creating the window. | 359 // Make sure we will be the one and only process creating the window. |
434 // We use a named Mutex since we are protecting against multi-process | 360 // We use a named Mutex since we are protecting against multi-process |
435 // access. As documented, it's clearer to NOT request ownership on creation | 361 // access. As documented, it's clearer to NOT request ownership on creation |
436 // since it isn't guaranteed we will get it. It is better to create it | 362 // since it isn't guaranteed we will get it. It is better to create it |
437 // without ownership and explicitly get the ownership afterward. | 363 // without ownership and explicitly get the ownership afterward. |
438 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); | 364 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); |
439 DPCHECK(only_me.IsValid()); | 365 DPCHECK(only_me.IsValid()); |
440 | 366 |
441 AutoLockMutex auto_lock_only_me(only_me); | 367 AutoLockMutex auto_lock_only_me(only_me); |
442 | 368 |
443 // We now own the mutex so we are the only process that can create the | 369 // We now own the mutex so we are the only process that can create the |
444 // window at this time, but we must still check if someone created it | 370 // window at this time, but we must still check if someone created it |
445 // between the time where we looked for it above and the time the mutex | 371 // between the time where we looked for it above and the time the mutex |
446 // was given to us. | 372 // was given to us. |
447 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, | 373 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); |
448 chrome::kMessageWindowClass, | |
449 user_data_dir_.value().c_str()); | |
450 | 374 |
451 | 375 |
452 // In Win8+, a new Chrome process launched in Desktop mode may need to be | 376 // In Win8+, a new Chrome process launched in Desktop mode may need to be |
453 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for | 377 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for |
454 // heuristics). To accomplish this, the current Chrome activates Metro | 378 // heuristics). To accomplish this, the current Chrome activates Metro |
455 // Chrome, releases the startup mutex, and waits for metro Chrome to take | 379 // Chrome, releases the startup mutex, and waits for metro Chrome to take |
456 // the singleton. From that point onward, the command line for this Chrome | 380 // the singleton. From that point onward, the command line for this Chrome |
457 // process will be sent to Metro Chrome by the usual channels. | 381 // process will be sent to Metro Chrome by the usual channels. |
458 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 && | 382 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 && |
459 !base::win::IsMetroProcess()) { | 383 !base::win::IsMetroProcess()) { |
(...skipping 28 matching lines...) Expand all Loading... | |
488 | 412 |
489 DWORD result = ::WaitForSingleObject(metro_activation_event, | 413 DWORD result = ::WaitForSingleObject(metro_activation_event, |
490 kMetroChromeActivationTimeoutMs); | 414 kMetroChromeActivationTimeoutMs); |
491 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) | 415 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) |
492 << "Result = " << result; | 416 << "Result = " << result; |
493 } | 417 } |
494 | 418 |
495 // Check if this singleton was successfully grabbed by another process | 419 // Check if this singleton was successfully grabbed by another process |
496 // (hopefully Metro Chrome). Failing to do so, this process will grab | 420 // (hopefully Metro Chrome). Failing to do so, this process will grab |
497 // the singleton and launch in Desktop mode. | 421 // the singleton and launch in Desktop mode. |
498 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, | 422 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); |
499 chrome::kMessageWindowClass, | |
500 user_data_dir_.value().c_str()); | |
501 } | 423 } |
502 } | 424 } |
503 | 425 |
504 if (!remote_window_) { | 426 if (!remote_window_) { |
505 // We have to make sure there is no Chrome instance running on another | 427 // We have to make sure there is no Chrome instance running on another |
506 // machine that uses the same profile. | 428 // machine that uses the same profile. |
507 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); | 429 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); |
508 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), | 430 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), |
509 GENERIC_WRITE, | 431 GENERIC_WRITE, |
510 FILE_SHARE_READ, | 432 FILE_SHARE_READ, |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
569 switch (message) { | 491 switch (message) { |
570 case WM_COPYDATA: | 492 case WM_COPYDATA: |
571 return OnCopyData(reinterpret_cast<HWND>(wparam), | 493 return OnCopyData(reinterpret_cast<HWND>(wparam), |
572 reinterpret_cast<COPYDATASTRUCT*>(lparam)); | 494 reinterpret_cast<COPYDATASTRUCT*>(lparam)); |
573 default: | 495 default: |
574 break; | 496 break; |
575 } | 497 } |
576 | 498 |
577 return ::DefWindowProc(hwnd, message, wparam, lparam); | 499 return ::DefWindowProc(hwnd, message, wparam, lparam); |
578 } | 500 } |
OLD | NEW |