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

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: fix if/enum 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/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
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
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
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
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
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 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698