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

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: add profile dir to whitelist 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 181 matching lines...) Expand 10 before | Expand all | Expand 10 after
236 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id)) 234 if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id))
237 return false; 235 return false;
238 is_virtualized_ = true; 236 is_virtualized_ = true;
239 // The new window was spawned from WMI, and won't be in the foreground. 237 // The new window was spawned from WMI, and won't be in the foreground.
240 // So, first we sleep while the new chrome.exe instance starts (because 238 // So, first we sleep while the new chrome.exe instance starts (because
241 // WaitForInputIdle doesn't work here). Then we poll for up to two more 239 // WaitForInputIdle doesn't work here). Then we poll for up to two more
242 // seconds and make the window foreground if we find it (or we give up). 240 // seconds and make the window foreground if we find it (or we give up).
243 HWND hwnd = 0; 241 HWND hwnd = 0;
244 ::Sleep(90); 242 ::Sleep(90);
245 for (int tries = 200; tries; --tries) { 243 for (int tries = 200; tries; --tries) {
246 hwnd = ::FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass, 244 hwnd = chrome::FindRunningChromeWindow(user_data_dir);
247 user_data_dir.value().c_str());
248 if (hwnd) { 245 if (hwnd) {
249 ::SetForegroundWindow(hwnd); 246 ::SetForegroundWindow(hwnd);
250 break; 247 break;
251 } 248 }
252 ::Sleep(10); 249 ::Sleep(10);
253 } 250 }
254 return true; 251 return true;
255 } 252 }
256 return false; 253 return false;
257 } 254 }
(...skipping 30 matching lines...) Expand all
288 } 285 }
289 286
290 DWORD process_id = 0; 287 DWORD process_id = 0;
291 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id); 288 DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
292 // It is possible that the process owning this window may have died by now. 289 // It is possible that the process owning this window may have died by now.
293 if (!thread_id || !process_id) { 290 if (!thread_id || !process_id) {
294 remote_window_ = NULL; 291 remote_window_ = NULL;
295 return PROCESS_NONE; 292 return PROCESS_NONE;
296 } 293 }
297 294
298 if (base::win::IsMetroProcess()) { 295 if (chrome::AttemptToNotifyRunningChrome(remote_window_))
299 // Interesting corner case. We are launched as a metro process but we
300 // found another chrome running. Since metro enforces single instance then
301 // the other chrome must be desktop chrome and this must be a search charm
302 // activation. This scenario is unique; other cases should be properly
303 // handled by the delegate_execute which will not activate a second chrome.
304 string16 terms;
305 base::win::MetroLaunchType launch = base::win::GetMetroLaunchParams(&terms);
306 if (launch != base::win::METRO_SEARCH) {
307 LOG(WARNING) << "In metro mode, but and launch is " << launch;
308 } else {
309 std::string query = net::EscapeQueryParamValue(UTF16ToUTF8(terms), true);
310 std::string url = base::StringPrintf(kSearchUrl, query.c_str());
311 SHELLEXECUTEINFOA sei = { sizeof(sei) };
312 sei.fMask = SEE_MASK_FLAG_LOG_USAGE;
313 sei.nShow = SW_SHOWNORMAL;
314 sei.lpFile = url.c_str();
315 ::OutputDebugStringA(sei.lpFile);
316 sei.lpDirectory = "";
317 ::ShellExecuteExA(&sei);
318 }
319 return PROCESS_NOTIFIED; 296 return PROCESS_NOTIFIED;
320 }
321
322 // Non-metro mode, send our command line to the other chrome message window.
323 // format is "START\0<<<current directory>>>\0<<<commandline>>>".
324 std::wstring to_send(L"START\0", 6); // want the NULL in the string.
325 base::FilePath cur_dir;
326 if (!PathService::Get(base::DIR_CURRENT, &cur_dir))
327 return PROCESS_NONE;
328 to_send.append(cur_dir.value());
329 to_send.append(L"\0", 1); // Null separator.
330 to_send.append(::GetCommandLineW());
331 to_send.append(L"\0", 1); // Null separator.
332
333 base::win::ScopedHandle process_handle;
334 if (base::win::GetVersion() >= base::win::VERSION_WIN8 &&
335 base::OpenProcessHandleWithAccess(
336 process_id, PROCESS_QUERY_INFORMATION,
337 process_handle.Receive()) &&
338 base::win::IsProcessImmersive(process_handle.Get())) {
339 chrome::ActivateMetroChrome();
340 }
341
342 // Allow the current running browser window making itself the foreground
343 // window (otherwise it will just flash in the taskbar).
344 ::AllowSetForegroundWindow(process_id);
345
346 COPYDATASTRUCT cds;
347 cds.dwData = 0;
348 cds.cbData = static_cast<DWORD>((to_send.length() + 1) * sizeof(wchar_t));
349 cds.lpData = const_cast<wchar_t*>(to_send.c_str());
350 DWORD_PTR result = 0;
351 if (::SendMessageTimeout(remote_window_,
352 WM_COPYDATA,
353 NULL,
354 reinterpret_cast<LPARAM>(&cds),
355 SMTO_ABORTIFHUNG,
356 kTimeoutInSeconds * 1000,
357 &result)) {
358 // It is possible that the process owning this window may have died by now.
359 if (!result) {
360 remote_window_ = NULL;
361 return PROCESS_NONE;
362 }
363 return PROCESS_NOTIFIED;
364 }
365 297
366 // 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.
367 if (!::IsWindow(remote_window_)) { 299 if (!::IsWindow(remote_window_)) {
368 remote_window_ = NULL; 300 remote_window_ = NULL;
369 return PROCESS_NONE; 301 return PROCESS_NONE;
370 } 302 }
371 303
372 // The window is hung. Scan for every window to find a visible one. 304 // The window is hung. Scan for every window to find a visible one.
373 bool visible_window = false; 305 bool visible_window = false;
374 ::EnumThreadWindows(thread_id, 306 ::EnumThreadWindows(thread_id,
(...skipping 29 matching lines...) Expand all
404 } 336 }
405 337
406 // Look for a Chrome instance that uses the same profile directory. If there 338 // Look for a Chrome instance that uses the same profile directory. If there
407 // isn't one, create a message window with its title set to the profile 339 // isn't one, create a message window with its title set to the profile
408 // directory path. 340 // directory path.
409 bool ProcessSingleton::Create() { 341 bool ProcessSingleton::Create() {
410 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; 342 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!";
411 static const wchar_t kMetroActivationEventName[] = 343 static const wchar_t kMetroActivationEventName[] =
412 L"Local\\ChromeProcessSingletonStartupMetroActivation!"; 344 L"Local\\ChromeProcessSingletonStartupMetroActivation!";
413 345
414 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 346 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
415 chrome::kMessageWindowClass,
416 user_data_dir_.value().c_str());
417 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) { 347 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
418 // Make sure we will be the one and only process creating the window. 348 // Make sure we will be the one and only process creating the window.
419 // We use a named Mutex since we are protecting against multi-process 349 // We use a named Mutex since we are protecting against multi-process
420 // access. As documented, it's clearer to NOT request ownership on creation 350 // access. As documented, it's clearer to NOT request ownership on creation
421 // since it isn't guaranteed we will get it. It is better to create it 351 // since it isn't guaranteed we will get it. It is better to create it
422 // without ownership and explicitly get the ownership afterward. 352 // without ownership and explicitly get the ownership afterward.
423 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); 353 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
424 DPCHECK(only_me.IsValid()); 354 DPCHECK(only_me.IsValid());
425 355
426 AutoLockMutex auto_lock_only_me(only_me); 356 AutoLockMutex auto_lock_only_me(only_me);
427 357
428 // We now own the mutex so we are the only process that can create the 358 // We now own the mutex so we are the only process that can create the
429 // window at this time, but we must still check if someone created it 359 // window at this time, but we must still check if someone created it
430 // between the time where we looked for it above and the time the mutex 360 // between the time where we looked for it above and the time the mutex
431 // was given to us. 361 // was given to us.
432 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 362 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
433 chrome::kMessageWindowClass,
434 user_data_dir_.value().c_str());
435 363
436 364
437 // In Win8+, a new Chrome process launched in Desktop mode may need to be 365 // In Win8+, a new Chrome process launched in Desktop mode may need to be
438 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for 366 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for
439 // heuristics). To accomplish this, the current Chrome activates Metro 367 // heuristics). To accomplish this, the current Chrome activates Metro
440 // Chrome, releases the startup mutex, and waits for metro Chrome to take 368 // Chrome, releases the startup mutex, and waits for metro Chrome to take
441 // the singleton. From that point onward, the command line for this Chrome 369 // the singleton. From that point onward, the command line for this Chrome
442 // process will be sent to Metro Chrome by the usual channels. 370 // process will be sent to Metro Chrome by the usual channels.
443 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 && 371 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 &&
444 !base::win::IsMetroProcess()) { 372 !base::win::IsMetroProcess()) {
(...skipping 28 matching lines...) Expand all
473 401
474 DWORD result = ::WaitForSingleObject(metro_activation_event, 402 DWORD result = ::WaitForSingleObject(metro_activation_event,
475 kMetroChromeActivationTimeoutMs); 403 kMetroChromeActivationTimeoutMs);
476 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) 404 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
477 << "Result = " << result; 405 << "Result = " << result;
478 } 406 }
479 407
480 // Check if this singleton was successfully grabbed by another process 408 // Check if this singleton was successfully grabbed by another process
481 // (hopefully Metro Chrome). Failing to do so, this process will grab 409 // (hopefully Metro Chrome). Failing to do so, this process will grab
482 // the singleton and launch in Desktop mode. 410 // the singleton and launch in Desktop mode.
483 remote_window_ = ::FindWindowEx(HWND_MESSAGE, NULL, 411 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
484 chrome::kMessageWindowClass,
485 user_data_dir_.value().c_str());
486 } 412 }
487 } 413 }
488 414
489 if (!remote_window_) { 415 if (!remote_window_) {
490 // We have to make sure there is no Chrome instance running on another 416 // We have to make sure there is no Chrome instance running on another
491 // machine that uses the same profile. 417 // machine that uses the same profile.
492 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); 418 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
493 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), 419 lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
494 GENERIC_WRITE, 420 GENERIC_WRITE,
495 FILE_SHARE_READ, 421 FILE_SHARE_READ,
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after
554 switch (message) { 480 switch (message) {
555 case WM_COPYDATA: 481 case WM_COPYDATA:
556 return OnCopyData(reinterpret_cast<HWND>(wparam), 482 return OnCopyData(reinterpret_cast<HWND>(wparam),
557 reinterpret_cast<COPYDATASTRUCT*>(lparam)); 483 reinterpret_cast<COPYDATASTRUCT*>(lparam));
558 default: 484 default:
559 break; 485 break;
560 } 486 }
561 487
562 return ::DefWindowProc(hwnd, message, wparam, lparam); 488 return ::DefWindowProc(hwnd, message, wparam, lparam);
563 } 489 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698