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