| 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 |