Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(97)

Side by Side Diff: chrome/browser/process_singleton_win.cc

Issue 14617003: Make chrome.exe rendezvous with existing chrome process earlier. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: respond to comments Created 7 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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
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
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;
gab 2013/05/24 12:22:22 Please add a comment here explaining why you're sk
koz (OOO until 15th September) 2013/05/27 00:01:46 Done.
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) {
298 remote_window_ = NULL;
299 return PROCESS_NONE;
300 }
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 // Non-metro mode, send our command line to the other chrome message window.
327 // format is "START\0<<<current directory>>>\0<<<commandline>>>".
328 std::wstring to_send(L"START\0", 6); // want the NULL in the string.
329 base::FilePath cur_dir;
330 if (!PathService::Get(base::DIR_CURRENT, &cur_dir))
331 return PROCESS_NONE;
332 to_send.append(cur_dir.value());
333 to_send.append(L"\0", 1); // Null separator.
334 to_send.append(::GetCommandLineW());
335 // Add the process start time as a flag.
336 to_send.append(L" --");
337 to_send.append(ASCIIToWide(switches::kOriginalProcessStartTime));
338 to_send.append(L"=");
339 to_send.append(base::Int64ToString16(
340 base::CurrentProcessInfo::CreationTime()->ToInternalValue()));
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 304
382 // The window is hung. Scan for every window to find a visible one. 305 // The window is hung. Scan for every window to find a visible one.
383 bool visible_window = false; 306 bool visible_window = false;
384 ::EnumThreadWindows(thread_id, 307 ::EnumThreadWindows(thread_id,
385 &BrowserWindowEnumeration, 308 &BrowserWindowEnumeration,
386 reinterpret_cast<LPARAM>(&visible_window)); 309 reinterpret_cast<LPARAM>(&visible_window));
387 310
388 // If there is a visible browser window, ask the user before killing it. 311 // If there is a visible browser window, ask the user before killing it.
389 if (visible_window && chrome::ShowMessageBox(NULL, 312 if (visible_window &&
390 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME), 313 chrome::ShowMessageBox(
391 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE), 314 NULL,
392 chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) { 315 l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
316 l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE),
317 chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) {
393 // The user denied. Quit silently. 318 // The user denied. Quit silently.
394 return PROCESS_NOTIFIED; 319 return PROCESS_NOTIFIED;
395 } 320 }
396 321
397 // Time to take action. Kill the browser process. 322 // Time to take action. Kill the browser process.
398 base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true); 323 base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true);
399 remote_window_ = NULL; 324 remote_window_ = NULL;
400 return PROCESS_NONE; 325 return PROCESS_NONE;
401 } 326 }
402 327
(...skipping 11 matching lines...) Expand all
414 } 339 }
415 340
416 // 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
417 // 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
418 // directory path. 343 // directory path.
419 bool ProcessSingleton::Create() { 344 bool ProcessSingleton::Create() {
420 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; 345 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!";
421 static const wchar_t kMetroActivationEventName[] = 346 static const wchar_t kMetroActivationEventName[] =
422 L"Local\\ChromeProcessSingletonStartupMetroActivation!"; 347 L"Local\\ChromeProcessSingletonStartupMetroActivation!";
423 348
424 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 349 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_)) { 350 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
428 // 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.
429 // 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
430 // 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
431 // 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
432 // without ownership and explicitly get the ownership afterward. 355 // without ownership and explicitly get the ownership afterward.
433 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); 356 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
434 DPCHECK(only_me.IsValid()); 357 DPCHECK(only_me.IsValid());
435 358
436 AutoLockMutex auto_lock_only_me(only_me); 359 AutoLockMutex auto_lock_only_me(only_me);
437 360
438 // 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
439 // 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
440 // 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
441 // was given to us. 364 // was given to us.
442 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 365 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
443 chrome::kMessageWindowClass,
444 user_data_dir_.value().c_str());
445 366
446 367
447 // 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
448 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for 369 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for
449 // heuristics). To accomplish this, the current Chrome activates Metro 370 // heuristics). To accomplish this, the current Chrome activates Metro
450 // 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
451 // 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
452 // process will be sent to Metro Chrome by the usual channels. 373 // process will be sent to Metro Chrome by the usual channels.
453 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 && 374 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 &&
454 !base::win::IsMetroProcess()) { 375 !base::win::IsMetroProcess()) {
(...skipping 28 matching lines...) Expand all
483 404
484 DWORD result = ::WaitForSingleObject(metro_activation_event, 405 DWORD result = ::WaitForSingleObject(metro_activation_event,
485 kMetroChromeActivationTimeoutMs); 406 kMetroChromeActivationTimeoutMs);
486 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) 407 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
487 << "Result = " << result; 408 << "Result = " << result;
488 } 409 }
489 410
490 // Check if this singleton was successfully grabbed by another process 411 // Check if this singleton was successfully grabbed by another process
491 // (hopefully Metro Chrome). Failing to do so, this process will grab 412 // (hopefully Metro Chrome). Failing to do so, this process will grab
492 // the singleton and launch in Desktop mode. 413 // the singleton and launch in Desktop mode.
493 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 414 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
494 chrome::kMessageWindowClass,
495 user_data_dir_.value().c_str());
496 } 415 }
497 } 416 }
498 417
499 if (!remote_window_) { 418 if (!remote_window_) {
500 // 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
501 // machine that uses the same profile. 420 // machine that uses the same profile.
502 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); 421 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
503 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), 422 lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
504 GENERIC_WRITE, 423 GENERIC_WRITE,
505 FILE_SHARE_READ, 424 FILE_SHARE_READ,
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
564 switch (message) { 483 switch (message) {
565 case WM_COPYDATA: 484 case WM_COPYDATA:
566 return OnCopyData(reinterpret_cast<HWND>(wparam), 485 return OnCopyData(reinterpret_cast<HWND>(wparam),
567 reinterpret_cast<COPYDATASTRUCT*>(lparam)); 486 reinterpret_cast<COPYDATASTRUCT*>(lparam));
568 default: 487 default:
569 break; 488 break;
570 } 489 }
571 490
572 return ::DefWindowProc(hwnd, message, wparam, lparam); 491 return ::DefWindowProc(hwnd, message, wparam, lparam);
573 } 492 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698