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

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/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
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 22 matching lines...) Expand all
283 // Code roughly based on Mozilla. 280 // Code roughly based on Mozilla.
284 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() { 281 ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
285 if (is_virtualized_) 282 if (is_virtualized_)
286 return PROCESS_NOTIFIED; // We already spawned the process in this case. 283 return PROCESS_NOTIFIED; // We already spawned the process in this case.
287 if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) { 284 if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
288 return LOCK_ERROR; 285 return LOCK_ERROR;
289 } else if (!remote_window_) { 286 } else if (!remote_window_) {
290 return PROCESS_NONE; 287 return PROCESS_NONE;
291 } 288 }
292 289
293 DWORD process_id = 0; 290 DWORD process_id = 0;
gab 2013/05/15 13:28:03 This code is already in AttemptToNotifyRunningChro
koz (OOO until 15th September) 2013/05/17 00:12:42 These variables are used further down.
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 } 300 else
324 301 remote_window_ = NULL;
gab 2013/05/15 13:28:03 Some of these cases should return PROCESS_NONE her
koz (OOO until 15th September) 2013/05/17 00:12:42 Done. I don't know if it's worth trying to signal
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;
370 return PROCESS_NONE;
371 }
372 return PROCESS_NOTIFIED;
373 }
374 302
375 // It is possible that the process owning this window may have died by now. 303 // It is possible that the process owning this window may have died by now.
376 if (!::IsWindow(remote_window_)) { 304 if (!::IsWindow(remote_window_)) {
gab 2013/05/15 13:28:03 You can also move this block in chrome::AttemptToN
koz (OOO until 15th September) 2013/05/17 00:12:42 Done.
377 remote_window_ = NULL; 305 remote_window_ = NULL;
378 return PROCESS_NONE; 306 return PROCESS_NONE;
379 } 307 }
380 308
381 // 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.
382 bool visible_window = false; 310 bool visible_window = false;
383 ::EnumThreadWindows(thread_id, 311 ::EnumThreadWindows(thread_id,
384 &BrowserWindowEnumeration, 312 &BrowserWindowEnumeration,
385 reinterpret_cast<LPARAM>(&visible_window)); 313 reinterpret_cast<LPARAM>(&visible_window));
386 314
(...skipping 26 matching lines...) Expand all
413 } 341 }
414 342
415 // Look for a Chrome instance that uses the same profile directory. If there 343 // 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 344 // isn't one, create a message window with its title set to the profile
417 // directory path. 345 // directory path.
418 bool ProcessSingleton::Create() { 346 bool ProcessSingleton::Create() {
419 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; 347 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!";
420 static const wchar_t kMetroActivationEventName[] = 348 static const wchar_t kMetroActivationEventName[] =
421 L"Local\\ChromeProcessSingletonStartupMetroActivation!"; 349 L"Local\\ChromeProcessSingletonStartupMetroActivation!";
422 350
423 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 351 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_)) { 352 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
427 // Make sure we will be the one and only process creating the window. 353 // 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 354 // 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 355 // 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 356 // since it isn't guaranteed we will get it. It is better to create it
431 // without ownership and explicitly get the ownership afterward. 357 // without ownership and explicitly get the ownership afterward.
432 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); 358 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
433 DPCHECK(only_me.IsValid()); 359 DPCHECK(only_me.IsValid());
434 360
435 AutoLockMutex auto_lock_only_me(only_me); 361 AutoLockMutex auto_lock_only_me(only_me);
436 362
437 // We now own the mutex so we are the only process that can create the 363 // 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 364 // 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 365 // between the time where we looked for it above and the time the mutex
440 // was given to us. 366 // was given to us.
441 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 367 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
442 chrome::kMessageWindowClass,
443 user_data_dir_.value().c_str());
444 368
445 369
446 // In Win8+, a new Chrome process launched in Desktop mode may need to be 370 // In Win8+, a new Chrome process launched in Desktop mode may need to be
447 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for 371 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for
448 // heuristics). To accomplish this, the current Chrome activates Metro 372 // heuristics). To accomplish this, the current Chrome activates Metro
449 // Chrome, releases the startup mutex, and waits for metro Chrome to take 373 // 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 374 // the singleton. From that point onward, the command line for this Chrome
451 // process will be sent to Metro Chrome by the usual channels. 375 // process will be sent to Metro Chrome by the usual channels.
452 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 && 376 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 &&
453 !base::win::IsMetroProcess()) { 377 !base::win::IsMetroProcess()) {
(...skipping 28 matching lines...) Expand all
482 406
483 DWORD result = ::WaitForSingleObject(metro_activation_event, 407 DWORD result = ::WaitForSingleObject(metro_activation_event,
484 kMetroChromeActivationTimeoutMs); 408 kMetroChromeActivationTimeoutMs);
485 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) 409 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
486 << "Result = " << result; 410 << "Result = " << result;
487 } 411 }
488 412
489 // Check if this singleton was successfully grabbed by another process 413 // Check if this singleton was successfully grabbed by another process
490 // (hopefully Metro Chrome). Failing to do so, this process will grab 414 // (hopefully Metro Chrome). Failing to do so, this process will grab
491 // the singleton and launch in Desktop mode. 415 // the singleton and launch in Desktop mode.
492 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 416 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
493 chrome::kMessageWindowClass,
494 user_data_dir_.value().c_str());
495 } 417 }
496 } 418 }
497 419
498 if (!remote_window_) { 420 if (!remote_window_) {
499 // We have to make sure there is no Chrome instance running on another 421 // We have to make sure there is no Chrome instance running on another
500 // machine that uses the same profile. 422 // machine that uses the same profile.
501 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); 423 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
502 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), 424 lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
503 GENERIC_WRITE, 425 GENERIC_WRITE,
504 FILE_SHARE_READ, 426 FILE_SHARE_READ,
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
563 switch (message) { 485 switch (message) {
564 case WM_COPYDATA: 486 case WM_COPYDATA:
565 return OnCopyData(reinterpret_cast<HWND>(wparam), 487 return OnCopyData(reinterpret_cast<HWND>(wparam),
566 reinterpret_cast<COPYDATASTRUCT*>(lparam)); 488 reinterpret_cast<COPYDATASTRUCT*>(lparam));
567 default: 489 default:
568 break; 490 break;
569 } 491 }
570 492
571 return ::DefWindowProc(hwnd, message, wparam, lparam); 493 return ::DefWindowProc(hwnd, message, wparam, lparam);
572 } 494 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698