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