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

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: 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_util.h" 13 #include "base/process_util.h"
14 #include "base/stringprintf.h" 14 #include "base/stringprintf.h"
15 #include "base/utf_string_conversions.h" 15 #include "base/utf_string_conversions.h"
16 #include "base/win/metro.h" 16 #include "base/win/metro.h"
17 #include "base/win/registry.h" 17 #include "base/win/registry.h"
18 #include "base/win/scoped_handle.h" 18 #include "base/win/scoped_handle.h"
19 #include "base/win/win_util.h" 19 #include "base/win/win_util.h"
20 #include "base/win/windows_version.h" 20 #include "base/win/windows_version.h"
21 #include "base/win/wrapped_window_proc.h" 21 #include "base/win/wrapped_window_proc.h"
22 #include "chrome/browser/browser_process.h" 22 #include "chrome/browser/browser_process.h"
23 #include "chrome/browser/chrome_process_finder_win.h"
23 #include "chrome/browser/shell_integration.h" 24 #include "chrome/browser/shell_integration.h"
24 #include "chrome/browser/ui/metro_chrome_win.h" 25 #include "chrome/browser/ui/metro_chrome_win.h"
25 #include "chrome/browser/ui/simple_message_box.h" 26 #include "chrome/browser/ui/simple_message_box.h"
26 #include "chrome/common/chrome_constants.h" 27 #include "chrome/common/chrome_constants.h"
27 #include "chrome/common/chrome_paths.h" 28 #include "chrome/common/chrome_paths.h"
28 #include "chrome/common/chrome_paths_internal.h" 29 #include "chrome/common/chrome_paths_internal.h"
29 #include "chrome/common/chrome_switches.h" 30 #include "chrome/common/chrome_switches.h"
30 #include "chrome/installer/util/wmi.h" 31 #include "chrome/installer/util/wmi.h"
31 #include "content/public/common/result_codes.h" 32 #include "content/public/common/result_codes.h"
32 #include "grit/chromium_strings.h" 33 #include "grit/chromium_strings.h"
33 #include "grit/generated_resources.h" 34 #include "grit/generated_resources.h"
34 #include "net/base/escape.h" 35 #include "net/base/escape.h"
35 #include "ui/base/l10n/l10n_util.h" 36 #include "ui/base/l10n/l10n_util.h"
36 #include "ui/base/win/hwnd_util.h" 37 #include "ui/base/win/hwnd_util.h"
37 38
38 namespace { 39 namespace {
39 40
40 const char kLockfile[] = "lockfile"; 41 const char kLockfile[] = "lockfile";
41 42
42 const char kSearchUrl[] =
43 "http://www.google.com/search?q=%s&sourceid=chrome&ie=UTF-8";
44
45 const int kMetroChromeActivationTimeoutMs = 3000; 43 const int kMetroChromeActivationTimeoutMs = 3000;
46 44
47 // A helper class that acquires the given |mutex| while the AutoLockMutex is in 45 // A helper class that acquires the given |mutex| while the AutoLockMutex is in
48 // scope. 46 // scope.
49 class AutoLockMutex { 47 class AutoLockMutex {
50 public: 48 public:
51 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) { 49 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
52 DWORD result = ::WaitForSingleObject(mutex_, INFINITE); 50 DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
53 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result; 51 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
54 } 52 }
(...skipping 177 matching lines...) Expand 10 before | Expand all | Expand 10 after
232 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id)) 230 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id))
233 return false; 231 return false;
234 is_virtualized_ = true; 232 is_virtualized_ = true;
235 // The new window was spawned from WMI, and won't be in the foreground. 233 // The new window was spawned from WMI, and won't be in the foreground.
236 // So, first we sleep while the new chrome.exe instance starts (because 234 // So, first we sleep while the new chrome.exe instance starts (because
237 // WaitForInputIdle doesn't work here). Then we poll for up to two more 235 // WaitForInputIdle doesn't work here). Then we poll for up to two more
238 // seconds and make the window foreground if we find it (or we give up). 236 // seconds and make the window foreground if we find it (or we give up).
239 HWND hwnd = 0; 237 HWND hwnd = 0;
240 ::Sleep(90); 238 ::Sleep(90);
241 for (int tries = 200; tries; --tries) { 239 for (int tries = 200; tries; --tries) {
242 hwnd = ::FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass, 240 hwnd = chrome::FindRunningChromeWindow(user_data_dir);
243 user_data_dir.value().c_str());
244 if (hwnd) { 241 if (hwnd) {
245 ::SetForegroundWindow(hwnd); 242 ::SetForegroundWindow(hwnd);
246 break; 243 break;
247 } 244 }
248 ::Sleep(10); 245 ::Sleep(10);
249 } 246 }
250 return true; 247 return true;
251 } 248 }
252 return false; 249 return false;
253 } 250 }
(...skipping 30 matching lines...) Expand all
284 } 281 }
285 282
286 DWORD process_id = 0; 283 DWORD process_id = 0;
287 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id); 284 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
288 // It is possible that the process owning this window may have died by now. 285 // It is possible that the process owning this window may have died by now.
289 if (!thread_id || !process_id) { 286 if (!thread_id || !process_id) {
290 remote_window_ = NULL; 287 remote_window_ = NULL;
291 return PROCESS_NONE; 288 return PROCESS_NONE;
292 } 289 }
293 290
294 if (base::win::IsMetroProcess()) { 291 if (chrome::AttemptToNotifyRunningChrome(remote_window_))
295 // Interesting corner case. We are launched as a metro process but we
296 // found another chrome running. Since metro enforces single instance then
297 // the other chrome must be desktop chrome and this must be a search charm
298 // activation. This scenario is unique; other cases should be properly
299 // handled by the delegate_execute which will not activate a second chrome.
300 string16 terms;
301 base::win::MetroLaunchType launch = base::win::GetMetroLaunchParams(&terms);
302 if (launch != base::win::METRO_SEARCH) {
303 LOG(WARNING) << "In metro mode, but and launch is " << launch;
304 } else {
305 std::string query = net::EscapeQueryParamValue(UTF16ToUTF8(terms), true);
306 std::string url = base::StringPrintf(kSearchUrl, query.c_str());
307 SHELLEXECUTEINFOA sei = { sizeof(sei) };
308 sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
309 sei.nShow = SW_SHOWNORMAL;
310 sei.lpFile = url.c_str();
311 ::OutputDebugStringA(sei.lpFile);
312 sei.lpDirectory = "";
313 ::ShellExecuteExA(&sei);
314 }
315 return PROCESS_NOTIFIED; 292 return PROCESS_NOTIFIED;
316 }
317
318 // Non-metro mode, send our command line to the other chrome message window.
319 // format is "START\0<<<current directory>>>\0<<<commandline>>>".
320 std::wstring to_send(L"START\0", 6); // want the NULL in the string.
321 base::FilePath cur_dir;
322 if (!PathService::Get(base::DIR_CURRENT, &cur_dir))
323 return PROCESS_NONE;
324 to_send.append(cur_dir.value());
325 to_send.append(L"\0", 1); // Null separator.
326 to_send.append(::GetCommandLineW());
327 to_send.append(L"\0", 1); // Null separator.
328
329 base::win::ScopedHandle process_handle;
330 if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
331 base::OpenProcessHandleWithAccess(
332 process_id, PROCESS_QUERY_INFORMATION,
333 process_handle.Receive()) &&
334 base::win::IsProcessImmersive(process_handle.Get())) {
335 chrome::ActivateMetroChrome();
336 }
337
338 // Allow the current running browser window making itself the foreground
339 // window (otherwise it will just flash in the taskbar).
340 ::AllowSetForegroundWindow(process_id);
341
342 COPYDATASTRUCT cds;
343 cds.dwData = 0;
344 cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
345 cds.lpData = const_cast<wchar_t*>(to_send.c_str());
346 DWORD_PTR result = 0;
347 if (::SendMessageTimeout(remote_window_,
348 WM_COPYDATA,
349 NULL,
350 reinterpret_cast<LPARAM>(&cds),
351 SMTO_ABORTIFHUNG,
352 kTimeoutInSeconds * 1000,
353 &result)) {
354 // It is possible that the process owning this window may have died by now.
355 if (!result) {
356 remote_window_ = NULL;
357 return PROCESS_NONE;
358 }
359 return PROCESS_NOTIFIED;
360 }
361 293
362 // It is possible that the process owning this window may have died by now. 294 // It is possible that the process owning this window may have died by now.
363 if (!::IsWindow(remote_window_)) { 295 if (!::IsWindow(remote_window_)) {
364 remote_window_ = NULL; 296 remote_window_ = NULL;
365 return PROCESS_NONE; 297 return PROCESS_NONE;
366 } 298 }
367 299
368 // The window is hung. Scan for every window to find a visible one. 300 // The window is hung. Scan for every window to find a visible one.
369 bool visible_window = false; 301 bool visible_window = false;
370 ::EnumThreadWindows(thread_id, 302 ::EnumThreadWindows(thread_id,
(...skipping 29 matching lines...) Expand all
400 } 332 }
401 333
402 // Look for a Chrome instance that uses the same profile directory. If there 334 // Look for a Chrome instance that uses the same profile directory. If there
403 // isn't one, create a message window with its title set to the profile 335 // isn't one, create a message window with its title set to the profile
404 // directory path. 336 // directory path.
405 bool ProcessSingleton::Create() { 337 bool ProcessSingleton::Create() {
406 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; 338 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!";
407 static const wchar_t kMetroActivationEventName[] = 339 static const wchar_t kMetroActivationEventName[] =
408 L"Local\\ChromeProcessSingletonStartupMetroActivation!"; 340 L"Local\\ChromeProcessSingletonStartupMetroActivation!";
409 341
410 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 342 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
411 chrome::kMessageWindowClass,
412 user_data_dir_.value().c_str());
413 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) { 343 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
414 // Make sure we will be the one and only process creating the window. 344 // Make sure we will be the one and only process creating the window.
415 // We use a named Mutex since we are protecting against multi-process 345 // We use a named Mutex since we are protecting against multi-process
416 // access. As documented, it's clearer to NOT request ownership on creation 346 // access. As documented, it's clearer to NOT request ownership on creation
417 // since it isn't guaranteed we will get it. It is better to create it 347 // since it isn't guaranteed we will get it. It is better to create it
418 // without ownership and explicitly get the ownership afterward. 348 // without ownership and explicitly get the ownership afterward.
419 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); 349 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
420 DPCHECK(only_me.IsValid()); 350 DPCHECK(only_me.IsValid());
421 351
422 AutoLockMutex auto_lock_only_me(only_me); 352 AutoLockMutex auto_lock_only_me(only_me);
423 353
424 // We now own the mutex so we are the only process that can create the 354 // We now own the mutex so we are the only process that can create the
425 // window at this time, but we must still check if someone created it 355 // window at this time, but we must still check if someone created it
426 // between the time where we looked for it above and the time the mutex 356 // between the time where we looked for it above and the time the mutex
427 // was given to us. 357 // was given to us.
428 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 358 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
429 chrome::kMessageWindowClass,
430 user_data_dir_.value().c_str());
431 359
432 360
433 // In Win8+, a new Chrome process launched in Desktop mode may need to be 361 // In Win8+, a new Chrome process launched in Desktop mode may need to be
434 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for 362 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for
435 // heuristics). To accomplish this, the current Chrome activates Metro 363 // heuristics). To accomplish this, the current Chrome activates Metro
436 // Chrome, releases the startup mutex, and waits for metro Chrome to take 364 // Chrome, releases the startup mutex, and waits for metro Chrome to take
437 // the singleton. From that point onward, the command line for this Chrome 365 // the singleton. From that point onward, the command line for this Chrome
438 // process will be sent to Metro Chrome by the usual channels. 366 // process will be sent to Metro Chrome by the usual channels.
439 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 && 367 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 &&
440 !base::win::IsMetroProcess()) { 368 !base::win::IsMetroProcess()) {
(...skipping 28 matching lines...) Expand all
469 397
470 DWORD result = ::WaitForSingleObject(metro_activation_event, 398 DWORD result = ::WaitForSingleObject(metro_activation_event,
471 kMetroChromeActivationTimeoutMs); 399 kMetroChromeActivationTimeoutMs);
472 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) 400 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
473 << "Result = " << result; 401 << "Result = " << result;
474 } 402 }
475 403
476 // Check if this singleton was successfully grabbed by another process 404 // Check if this singleton was successfully grabbed by another process
477 // (hopefully Metro Chrome). Failing to do so, this process will grab 405 // (hopefully Metro Chrome). Failing to do so, this process will grab
478 // the singleton and launch in Desktop mode. 406 // the singleton and launch in Desktop mode.
479 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 407 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
480 chrome::kMessageWindowClass,
481 user_data_dir_.value().c_str());
482 } 408 }
483 } 409 }
484 410
485 if (!remote_window_) { 411 if (!remote_window_) {
486 // We have to make sure there is no Chrome instance running on another 412 // We have to make sure there is no Chrome instance running on another
487 // machine that uses the same profile. 413 // machine that uses the same profile.
488 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); 414 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
489 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), 415 lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
490 GENERIC_WRITE, 416 GENERIC_WRITE,
491 FILE_SHARE_READ, 417 FILE_SHARE_READ,
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
550 switch (message) { 476 switch (message) {
551 case WM_COPYDATA: 477 case WM_COPYDATA:
552 return OnCopyData(reinterpret_cast<HWND>(wparam), 478 return OnCopyData(reinterpret_cast<HWND>(wparam),
553 reinterpret_cast<COPYDATASTRUCT*>(lparam)); 479 reinterpret_cast<COPYDATASTRUCT*>(lparam));
554 default: 480 default:
555 break; 481 break;
556 } 482 }
557 483
558 return ::DefWindowProc(hwnd, message, wparam, lparam); 484 return ::DefWindowProc(hwnd, message, wparam, lparam);
559 } 485 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698