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

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: rebase Created 7 years, 6 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;
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
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
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 }
OLDNEW
« no previous file with comments | « chrome/browser/metro_utils/metro_chrome_win.cc ('k') | chrome/browser/ui/extensions/app_metro_infobar_delegate_win.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698