This source file includes following definitions.
- BrowserWindowEnumeration
- ParseCommandLine
- ProcessLaunchNotification
- ShouldLaunchInWindows8ImmersiveMode
- EscapeVirtualization
- user_data_dir_
- NotifyOtherProcess
- NotifyOtherProcessOrCreate
- Create
- Cleanup
#include "chrome/browser/process_singleton.h"
#include <shellapi.h>
#include "base/base_paths.h"
#include "base/bind.h"
#include "base/command_line.h"
#include "base/files/file_path.h"
#include "base/path_service.h"
#include "base/process/kill.h"
#include "base/process/process_info.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/time/time.h"
#include "base/win/metro.h"
#include "base/win/registry.h"
#include "base/win/scoped_handle.h"
#include "base/win/win_util.h"
#include "base/win/windows_version.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/browser_process_platform_part.h"
#include "chrome/browser/chrome_process_finder_win.h"
#include "chrome/browser/metro_utils/metro_chrome_win.h"
#include "chrome/browser/shell_integration.h"
#include "chrome/browser/ui/simple_message_box.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_paths_internal.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/installer/util/wmi.h"
#include "content/public/common/result_codes.h"
#include "grit/chromium_strings.h"
#include "grit/generated_resources.h"
#include "net/base/escape.h"
#include "ui/base/l10n/l10n_util.h"
#include "ui/gfx/win/hwnd_util.h"
namespace {
const char kLockfile[] = "lockfile";
const int kMetroChromeActivationTimeoutMs = 3000;
class AutoLockMutex {
public:
explicit AutoLockMutex(HANDLE mutex) : mutex_(mutex) {
DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
}
~AutoLockMutex() {
BOOL released = ::ReleaseMutex(mutex_);
DPCHECK(released);
}
private:
HANDLE mutex_;
DISALLOW_COPY_AND_ASSIGN(AutoLockMutex);
};
class AutoUnlockMutex {
public:
explicit AutoUnlockMutex(HANDLE mutex) : mutex_(mutex) {
BOOL released = ::ReleaseMutex(mutex_);
DPCHECK(released);
}
~AutoUnlockMutex() {
DWORD result = ::WaitForSingleObject(mutex_, INFINITE);
DPCHECK(result == WAIT_OBJECT_0) << "Result = " << result;
}
private:
HANDLE mutex_;
DISALLOW_COPY_AND_ASSIGN(AutoUnlockMutex);
};
BOOL CALLBACK BrowserWindowEnumeration(HWND window, LPARAM param) {
bool* result = reinterpret_cast<bool*>(param);
*result = ::IsWindowVisible(window) != 0;
return !*result;
}
bool ParseCommandLine(const COPYDATASTRUCT* cds,
CommandLine* parsed_command_line,
base::FilePath* current_directory) {
static const int min_message_size = 7;
if (cds->cbData < min_message_size * sizeof(wchar_t) ||
cds->cbData % sizeof(wchar_t) != 0) {
LOG(WARNING) << "Invalid WM_COPYDATA, length = " << cds->cbData;
return false;
}
DCHECK(cds->lpData);
const std::wstring msg(static_cast<wchar_t*>(cds->lpData),
cds->cbData / sizeof(wchar_t));
const std::wstring::size_type first_null = msg.find_first_of(L'\0');
if (first_null == 0 || first_null == std::wstring::npos) {
LOG(WARNING) << "Invalid WM_COPYDATA, length = " << msg.length() <<
", first null = " << first_null;
return false;
}
if (msg.substr(0, first_null) == L"START") {
VLOG(1) << "Handling STARTUP request from another process";
const std::wstring::size_type second_null =
msg.find_first_of(L'\0', first_null + 1);
if (second_null == std::wstring::npos ||
first_null == msg.length() - 1 || second_null == msg.length()) {
LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
"parts separated by NULLs";
return false;
}
*current_directory = base::FilePath(msg.substr(first_null + 1,
second_null - first_null));
const std::wstring::size_type third_null =
msg.find_first_of(L'\0', second_null + 1);
if (third_null == std::wstring::npos ||
third_null == msg.length()) {
LOG(WARNING) << "Invalid format for start command, we need a string in 4 "
"parts separated by NULLs";
}
const std::wstring cmd_line =
msg.substr(second_null + 1, third_null - second_null);
*parsed_command_line = CommandLine::FromString(cmd_line);
return true;
}
return false;
}
bool ProcessLaunchNotification(
const ProcessSingleton::NotificationCallback& notification_callback,
UINT message,
WPARAM wparam,
LPARAM lparam,
LRESULT* result) {
if (message != WM_COPYDATA)
return false;
HWND hwnd = reinterpret_cast<HWND>(wparam);
const COPYDATASTRUCT* cds = reinterpret_cast<COPYDATASTRUCT*>(lparam);
CommandLine parsed_command_line(CommandLine::NO_PROGRAM);
base::FilePath current_directory;
if (!ParseCommandLine(cds, &parsed_command_line, ¤t_directory)) {
*result = TRUE;
return true;
}
*result = notification_callback.Run(parsed_command_line, current_directory) ?
TRUE : FALSE;
return true;
}
bool ShouldLaunchInWindows8ImmersiveMode(const base::FilePath& user_data_dir) {
#if defined(USE_AURA)
return false;
#else
if (base::win::GetVersion() < base::win::VERSION_WIN8)
return false;
if (base::win::IsProcessImmersive(base::GetCurrentProcessHandle()))
return false;
if (ShellIntegration::GetDefaultBrowser() != ShellIntegration::IS_DEFAULT)
return false;
base::IntegrityLevel integrity_level = base::INTEGRITY_UNKNOWN;
base::GetProcessIntegrityLevel(base::GetCurrentProcessHandle(),
&integrity_level);
if (integrity_level == base::HIGH_INTEGRITY)
return false;
base::FilePath default_user_data_dir;
if (!chrome::GetDefaultUserDataDirectory(&default_user_data_dir))
return false;
if (default_user_data_dir != user_data_dir)
return false;
base::win::RegKey reg_key;
DWORD reg_value = 0;
if (reg_key.Create(HKEY_CURRENT_USER, chrome::kMetroRegistryPath,
KEY_READ) == ERROR_SUCCESS &&
reg_key.ReadValueDW(chrome::kLaunchModeValue,
®_value) == ERROR_SUCCESS) {
return reg_value == 1;
}
return base::win::IsTouchEnabledDevice();
#endif
}
}
bool ProcessSingleton::EscapeVirtualization(
const base::FilePath& user_data_dir) {
if (::GetModuleHandle(L"sftldr_wow64.dll") ||
::GetModuleHandle(L"sftldr.dll")) {
int process_id;
if (!installer::WMIProcess::Launch(::GetCommandLineW(), &process_id))
return false;
is_virtualized_ = true;
HWND hwnd = 0;
::Sleep(90);
for (int tries = 200; tries; --tries) {
hwnd = chrome::FindRunningChromeWindow(user_data_dir);
if (hwnd) {
::SetForegroundWindow(hwnd);
break;
}
::Sleep(10);
}
return true;
}
return false;
}
ProcessSingleton::ProcessSingleton(
const base::FilePath& user_data_dir,
const NotificationCallback& notification_callback)
: notification_callback_(notification_callback),
is_virtualized_(false), lock_file_(INVALID_HANDLE_VALUE),
user_data_dir_(user_data_dir) {
}
ProcessSingleton::~ProcessSingleton() {
if (lock_file_ != INVALID_HANDLE_VALUE)
::CloseHandle(lock_file_);
}
ProcessSingleton::NotifyResult ProcessSingleton::NotifyOtherProcess() {
if (is_virtualized_)
return PROCESS_NOTIFIED;
if (lock_file_ == INVALID_HANDLE_VALUE && !remote_window_) {
return LOCK_ERROR;
} else if (!remote_window_) {
return PROCESS_NONE;
}
switch (chrome::AttemptToNotifyRunningChrome(remote_window_, false)) {
case chrome::NOTIFY_SUCCESS:
return PROCESS_NOTIFIED;
case chrome::NOTIFY_FAILED:
remote_window_ = NULL;
return PROCESS_NONE;
case chrome::NOTIFY_WINDOW_HUNG:
remote_window_ = NULL;
break;
}
DWORD process_id = 0;
DWORD thread_id = ::GetWindowThreadProcessId(remote_window_, &process_id);
if (!thread_id || !process_id) {
remote_window_ = NULL;
return PROCESS_NONE;
}
bool visible_window = false;
::EnumThreadWindows(thread_id,
&BrowserWindowEnumeration,
reinterpret_cast<LPARAM>(&visible_window));
if (visible_window &&
chrome::ShowMessageBox(
NULL,
l10n_util::GetStringUTF16(IDS_PRODUCT_NAME),
l10n_util::GetStringUTF16(IDS_BROWSER_HUNGBROWSER_MESSAGE),
chrome::MESSAGE_BOX_TYPE_QUESTION) == chrome::MESSAGE_BOX_RESULT_NO) {
return PROCESS_NOTIFIED;
}
base::KillProcessById(process_id, content::RESULT_CODE_HUNG, true);
remote_window_ = NULL;
return PROCESS_NONE;
}
ProcessSingleton::NotifyResult
ProcessSingleton::NotifyOtherProcessOrCreate() {
ProcessSingleton::NotifyResult result = PROCESS_NONE;
if (!Create()) {
result = NotifyOtherProcess();
if (result == PROCESS_NONE)
result = PROFILE_IN_USE;
} else {
g_browser_process->platform_part()->PlatformSpecificCommandLineProcessing(
*CommandLine::ForCurrentProcess());
}
return result;
}
bool ProcessSingleton::Create() {
static const wchar_t kMutexName[] = L"Local\\ChromeProcessSingletonStartup!";
static const wchar_t kMetroActivationEventName[] =
L"Local\\ChromeProcessSingletonStartupMetroActivation!";
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
if (!remote_window_ && !EscapeVirtualization(user_data_dir_)) {
base::win::ScopedHandle only_me(::CreateMutex(NULL, FALSE, kMutexName));
DPCHECK(only_me.IsValid());
AutoLockMutex auto_lock_only_me(only_me);
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
if (!remote_window_ && base::win::GetVersion() >= base::win::VERSION_WIN8 &&
!base::win::IsMetroProcess()) {
base::win::ScopedHandle metro_activation_event(
::OpenEvent(SYNCHRONIZE, FALSE, kMetroActivationEventName));
if (!metro_activation_event.IsValid() &&
ShouldLaunchInWindows8ImmersiveMode(user_data_dir_)) {
metro_activation_event.Set(
::CreateEvent(NULL, TRUE, FALSE, kMetroActivationEventName));
if (!chrome::ActivateMetroChrome()) {
LOG(ERROR) << "Failed to launch immersive chrome";
metro_activation_event.Close();
}
}
if (metro_activation_event.IsValid()) {
{
AutoUnlockMutex auto_unlock_only_me(only_me);
DWORD result = ::WaitForSingleObject(metro_activation_event,
kMetroChromeActivationTimeoutMs);
DPCHECK(result == WAIT_OBJECT_0 || result == WAIT_TIMEOUT)
<< "Result = " << result;
}
remote_window_ = chrome::FindRunningChromeWindow(user_data_dir_);
}
}
if (!remote_window_) {
base::FilePath lock_file_path = user_data_dir_.AppendASCII(kLockfile);
lock_file_ = ::CreateFile(lock_file_path.value().c_str(),
GENERIC_WRITE,
FILE_SHARE_READ,
NULL,
CREATE_ALWAYS,
FILE_ATTRIBUTE_NORMAL |
FILE_FLAG_DELETE_ON_CLOSE,
NULL);
DWORD error = ::GetLastError();
LOG_IF(WARNING, lock_file_ != INVALID_HANDLE_VALUE &&
error == ERROR_ALREADY_EXISTS) << "Lock file exists but is writable.";
LOG_IF(ERROR, lock_file_ == INVALID_HANDLE_VALUE)
<< "Lock file can not be created! Error code: " << error;
if (lock_file_ != INVALID_HANDLE_VALUE) {
bool result = window_.CreateNamed(
base::Bind(&ProcessLaunchNotification, notification_callback_),
user_data_dir_.value());
CHECK(result && window_.hwnd());
}
if (base::win::GetVersion() >= base::win::VERSION_WIN8) {
base::win::ScopedHandle metro_activation_event(
::OpenEvent(EVENT_MODIFY_STATE, FALSE, kMetroActivationEventName));
if (metro_activation_event.IsValid())
::SetEvent(metro_activation_event);
}
}
}
return window_.hwnd() != NULL;
}
void ProcessSingleton::Cleanup() {
}