OLD | NEW |
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 #include <stddef.h> | 8 #include <stddef.h> |
9 | 9 |
10 #include "base/base_paths.h" | 10 #include "base/base_paths.h" |
11 #include "base/bind.h" | 11 #include "base/bind.h" |
12 #include "base/command_line.h" | 12 #include "base/command_line.h" |
13 #include "base/files/file_path.h" | 13 #include "base/files/file_path.h" |
14 #include "base/macros.h" | 14 #include "base/macros.h" |
15 #include "base/process/process.h" | 15 #include "base/process/process.h" |
16 #include "base/process/process_info.h" | 16 #include "base/process/process_info.h" |
17 #include "base/strings/string_number_conversions.h" | 17 #include "base/strings/string_number_conversions.h" |
18 #include "base/strings/stringprintf.h" | 18 #include "base/strings/stringprintf.h" |
19 #include "base/strings/utf_string_conversions.h" | 19 #include "base/strings/utf_string_conversions.h" |
20 #include "base/time/time.h" | 20 #include "base/time/time.h" |
21 #include "base/win/metro.h" | |
22 #include "base/win/registry.h" | 21 #include "base/win/registry.h" |
23 #include "base/win/scoped_handle.h" | 22 #include "base/win/scoped_handle.h" |
24 #include "base/win/windows_version.h" | 23 #include "base/win/windows_version.h" |
25 #include "chrome/browser/browser_process.h" | 24 #include "chrome/browser/browser_process.h" |
26 #include "chrome/browser/browser_process_platform_part.h" | 25 #include "chrome/browser/browser_process_platform_part.h" |
27 #include "chrome/browser/chrome_process_finder_win.h" | 26 #include "chrome/browser/chrome_process_finder_win.h" |
28 #include "chrome/browser/metro_utils/metro_chrome_win.h" | |
29 #include "chrome/browser/shell_integration.h" | 27 #include "chrome/browser/shell_integration.h" |
30 #include "chrome/browser/ui/simple_message_box.h" | 28 #include "chrome/browser/ui/simple_message_box.h" |
31 #include "chrome/common/chrome_constants.h" | 29 #include "chrome/common/chrome_constants.h" |
32 #include "chrome/common/chrome_paths.h" | 30 #include "chrome/common/chrome_paths.h" |
33 #include "chrome/common/chrome_paths_internal.h" | 31 #include "chrome/common/chrome_paths_internal.h" |
34 #include "chrome/common/chrome_switches.h" | 32 #include "chrome/common/chrome_switches.h" |
35 #include "chrome/grit/chromium_strings.h" | 33 #include "chrome/grit/chromium_strings.h" |
36 #include "chrome/installer/util/wmi.h" | 34 #include "chrome/installer/util/wmi.h" |
37 #include "content/public/common/result_codes.h" | 35 #include "content/public/common/result_codes.h" |
38 #include "net/base/escape.h" | 36 #include "net/base/escape.h" |
39 #include "ui/base/l10n/l10n_util.h" | 37 #include "ui/base/l10n/l10n_util.h" |
40 #include "ui/gfx/win/hwnd_util.h" | 38 #include "ui/gfx/win/hwnd_util.h" |
41 | 39 |
42 namespace { | 40 namespace { |
43 | 41 |
44 const char kLockfile[] = "lockfile"; | 42 const char kLockfile[] = "lockfile"; |
45 | 43 |
46 const int kMetroChromeActivationTimeoutMs = 3000; | |
47 | |
48 // A helper class that acquires the given |mutex| while the AutoLockMutex is in | 44 // A helper class that acquires the given |mutex| while the AutoLockMutex is in |
49 // scope. | 45 // scope. |
50 class AutoLockMutex { | 46 class AutoLockMutex { |
51 public: | 47 public: |
52 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) { | 48 explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) { |
53 DWORD result = ::WaitForSingleObject(mutex_, INFINITE); | 49 DWORD result = ::WaitForSingleObject(mutex_, INFINITE); |
54 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result; | 50 DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result; |
55 } | 51 } |
56 | 52 |
57 ~AutoLockMutex() { | 53 ~AutoLockMutex() { |
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
311 *base::CommandLine::ForCurrentProcess()); | 307 *base::CommandLine::ForCurrentProcess()); |
312 } | 308 } |
313 return result; | 309 return result; |
314 } | 310 } |
315 | 311 |
316 // Look for a Chrome instance that uses the same profile directory. If there | 312 // Look for a Chrome instance that uses the same profile directory. If there |
317 // isn't one, create a message window with its title set to the profile | 313 // isn't one, create a message window with its title set to the profile |
318 // directory path. | 314 // directory path. |
319 bool ProcessSingleton::Create() { | 315 bool ProcessSingleton::Create() { |
320 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; | 316 static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!"; |
321 static const wchar_t kMetroActivationEventName[] = | |
322 L"Local\\ChromeProcessSingletonStartupMetroActivation!"; | |
323 | 317 |
324 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); | 318 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); |
325 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) { | 319 if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) { |
326 // Make sure we will be the one and only process creating the window. | 320 // Make sure we will be the one and only process creating the window. |
327 // We use a named Mutex since we are protecting against multi-process | 321 // We use a named Mutex since we are protecting against multi-process |
328 // access. As documented, it's clearer to NOT request ownership on creation | 322 // access. As documented, it's clearer to NOT request ownership on creation |
329 // since it isn't guaranteed we will get it. It is better to create it | 323 // since it isn't guaranteed we will get it. It is better to create it |
330 // without ownership and explicitly get the ownership afterward. | 324 // without ownership and explicitly get the ownership afterward. |
331 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); | 325 base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName)); |
332 if (!only_me.IsValid()) { | 326 if (!only_me.IsValid()) { |
333 DPLOG(FATAL) << "CreateMutex failed"; | 327 DPLOG(FATAL) << "CreateMutex failed"; |
334 return false; | 328 return false; |
335 } | 329 } |
336 | 330 |
337 AutoLockMutex auto_lock_only_me(only_me.Get()); | 331 AutoLockMutex auto_lock_only_me(only_me.Get()); |
338 | 332 |
339 // We now own the mutex so we are the only process that can create the | 333 // We now own the mutex so we are the only process that can create the |
340 // window at this time, but we must still check if someone created it | 334 // window at this time, but we must still check if someone created it |
341 // between the time where we looked for it above and the time the mutex | 335 // between the time where we looked for it above and the time the mutex |
342 // was given to us. | 336 // was given to us. |
343 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); | 337 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); |
344 | 338 |
345 | |
346 // In Win8+, a new Chrome process launched in Desktop mode may need to be | |
347 // transmuted into Metro Chrome (see ShouldLaunchInWindows8ImmersiveMode for | |
348 // heuristics). To accomplish this, the current Chrome activates Metro | |
349 // Chrome, releases the startup mutex, and waits for metro Chrome to take | |
350 // the singleton. From that point onward, the command line for this Chrome | |
351 // process will be sent to Metro Chrome by the usual channels. | |
352 if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 && | |
353 !base::win::IsMetroProcess()) { | |
354 // |metro_activation_event| is created right before activating a Metro | |
355 // Chrome (note that there can only be one Metro Chrome process; by OS | |
356 // design); all following Desktop processes will then wait for this event | |
357 // to be signaled by Metro Chrome which will do so as soon as it grabs | |
358 // this singleton (should any of the waiting processes timeout waiting for | |
359 // the signal they will try to grab the singleton for themselves which | |
360 // will result in a forced Desktop Chrome launch in the worst case). | |
361 base::win::ScopedHandle metro_activation_event( | |
362 ::OpenEvent(SYNCHRONIZE, FALSE, kMetroActivationEventName)); | |
363 if (!metro_activation_event.IsValid() && | |
364 ShouldLaunchInWindows8ImmersiveMode(user_data_dir_)) { | |
365 // No Metro activation is under way, but the desire is to launch in | |
366 // Metro mode: activate and rendez-vous with the activated process. | |
367 metro_activation_event.Set( | |
368 ::CreateEvent(NULL, TRUE, FALSE, kMetroActivationEventName)); | |
369 if (!chrome::ActivateMetroChrome()) { | |
370 // Failed to launch immersive Chrome, default to launching on Desktop. | |
371 LOG(ERROR) << "Failed to launch immersive chrome"; | |
372 metro_activation_event.Close(); | |
373 } | |
374 } | |
375 | |
376 if (metro_activation_event.IsValid()) { | |
377 // Release |only_me| (to let Metro Chrome grab this singleton) and wait | |
378 // until the event is signaled (i.e. Metro Chrome was successfully | |
379 // activated). Ignore timeout waiting for |metro_activation_event|. | |
380 { | |
381 AutoUnlockMutex auto_unlock_only_me(only_me.Get()); | |
382 | |
383 DWORD result = ::WaitForSingleObject(metro_activation_event.Get(), | |
384 kMetroChromeActivationTimeoutMs); | |
385 DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT) | |
386 << "Result = " << result; | |
387 } | |
388 | |
389 // Check if this singleton was successfully grabbed by another process | |
390 // (hopefully Metro Chrome). Failing to do so, this process will grab | |
391 // the singleton and launch in Desktop mode. | |
392 remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_); | |
393 } | |
394 } | |
395 | |
396 if (!remote_window_) { | 339 if (!remote_window_) { |
397 // We have to make sure there is no Chrome instance running on another | 340 // We have to make sure there is no Chrome instance running on another |
398 // machine that uses the same profile. | 341 // machine that uses the same profile. |
399 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); | 342 base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile); |
400 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), | 343 lock_file_ = ::CreateFile(lock_file_path.value().c_str(), |
401 GENERIC_WRITE, | 344 GENERIC_WRITE, |
402 FILE_SHARE_READ, | 345 FILE_SHARE_READ, |
403 NULL, | 346 NULL, |
404 CREATE_ALWAYS, | 347 CREATE_ALWAYS, |
405 FILE_ATTRIBUTE_NORMAL | | 348 FILE_ATTRIBUTE_NORMAL | |
406 FILE_FLAG_DELETE_ON_CLOSE, | 349 FILE_FLAG_DELETE_ON_CLOSE, |
407 NULL); | 350 NULL); |
408 DWORD error = ::GetLastError(); | 351 DWORD error = ::GetLastError(); |
409 LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE && | 352 LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE && |
410 error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable."; | 353 error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable."; |
411 LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE) | 354 LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE) |
412 << "Lock file can not be created! Error code: " << error; | 355 << "Lock file can not be created! Error code: " << error; |
413 | 356 |
414 if (lock_file_ != INVALID_HANDLE_VALUE) { | 357 if (lock_file_ != INVALID_HANDLE_VALUE) { |
415 // Set the window's title to the path of our user data directory so | 358 // Set the window's title to the path of our user data directory so |
416 // other Chrome instances can decide if they should forward to us. | 359 // other Chrome instances can decide if they should forward to us. |
417 bool result = window_.CreateNamed( | 360 bool result = window_.CreateNamed( |
418 base::Bind(&ProcessLaunchNotification, notification_callback_), | 361 base::Bind(&ProcessLaunchNotification, notification_callback_), |
419 user_data_dir_.value()); | 362 user_data_dir_.value()); |
420 CHECK(result && window_.hwnd()); | 363 CHECK(result && window_.hwnd()); |
421 } | 364 } |
422 | |
423 if (base::win::GetVersion() >= base::win::VERSION_WIN8) { | |
424 // Make sure no one is still waiting on Metro activation whether it | |
425 // succeeded (i.e., this is the Metro process) or failed. | |
426 base::win::ScopedHandle metro_activation_event( | |
427 ::OpenEvent(EVENT_MODIFY_STATE, FALSE, kMetroActivationEventName)); | |
428 if (metro_activation_event.IsValid()) | |
429 ::SetEvent(metro_activation_event.Get()); | |
430 } | |
431 } | 365 } |
432 } | 366 } |
433 | 367 |
434 return window_.hwnd() != NULL; | 368 return window_.hwnd() != NULL; |
435 } | 369 } |
436 | 370 |
437 void ProcessSingleton::Cleanup() { | 371 void ProcessSingleton::Cleanup() { |
438 } | 372 } |
439 | 373 |
440 void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting( | 374 void ProcessSingleton::OverrideShouldKillRemoteProcessCallbackForTesting( |
441 const ShouldKillRemoteProcessCallback& display_dialog_callback) { | 375 const ShouldKillRemoteProcessCallback& display_dialog_callback) { |
442 should_kill_remote_process_callback_ = display_dialog_callback; | 376 should_kill_remote_process_callback_ = display_dialog_callback; |
443 } | 377 } |
OLD | NEW |