// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome_frame/test_utils.h"
#include <atlbase.h>
#include <atlwin.h>
#include <shellapi.h>
#include <algorithm>
#include "base/command_line.h"
#include "base/file_util.h"
#include "base/files/file_path.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/process/kill.h"
#include "base/process/launch.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_handle.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_switches.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
const wchar_t kChromeFrameDllName[] = L"npchrome_frame.dll";
const wchar_t kChromeLauncherExeName[] = L"chrome_launcher.exe";
// How long to wait for DLLs to register or unregister themselves before killing
// the registrar.
const int64 kDllRegistrationTimeoutMs = 30 * 1000;
base::FilePath GetChromeFrameBuildPath() {
base::FilePath build_path;
PathService::Get(chrome::DIR_APP, &build_path);
base::FilePath dll_path = build_path.Append(kChromeFrameDllName);
if (!base::PathExists(dll_path)) {
// Well, dang.. try looking in the current directory.
dll_path = build_path.Append(kChromeFrameDllName);
}
if (!base::PathExists(dll_path)) {
// No luck, return something empty.
dll_path = base::FilePath();
}
return dll_path;
}
const wchar_t ScopedChromeFrameRegistrar::kCallRegistrationEntrypointSwitch[] =
L"--call-registration-entrypoint";
bool ScopedChromeFrameRegistrar::register_chrome_path_provider_ = false;
// static
void ScopedChromeFrameRegistrar::RegisterDefaults() {
if (!register_chrome_path_provider_) {
chrome::RegisterPathProvider();
register_chrome_path_provider_ = true;
}
}
// Registers or unregisters the DLL at |path| by calling out to the current
// executable with --call-registration-entrypoint. Loading the DLL into the
// test process is problematic for component=shared_library builds since
// singletons in base.dll aren't designed to handle multiple initialization.
// Use of rundll32.exe is problematic since it does not return useful error
// information.
// static
void ScopedChromeFrameRegistrar::DoRegistration(
const string16& path,
RegistrationType registration_type,
RegistrationOperation registration_operation) {
static const char* const kEntrypoints[] = {
"DllRegisterServer",
"DllUnregisterServer",
"DllRegisterUserServer",
"DllUnregisterUserServer",
};
DCHECK(!path.empty());
DCHECK(registration_type == PER_USER || registration_type == SYSTEM_LEVEL);
DCHECK(registration_operation == REGISTER ||
registration_operation == UNREGISTER);
int entrypoint_index = 0;
base::LaunchOptions launch_options;
base::win::ScopedHandle process_handle;
int exit_code = -1;
if (registration_type == PER_USER)
entrypoint_index += 2;
if (registration_operation == UNREGISTER)
entrypoint_index += 1;
string16 registration_command(ASCIIToUTF16("\""));
registration_command +=
CommandLine::ForCurrentProcess()->GetProgram().value();
registration_command += ASCIIToUTF16("\" ");
registration_command += kCallRegistrationEntrypointSwitch;
registration_command += ASCIIToUTF16(" \"");
registration_command += path;
registration_command += ASCIIToUTF16("\" ");
registration_command += ASCIIToUTF16(kEntrypoints[entrypoint_index]);
launch_options.wait = true;
if (!base::LaunchProcess(registration_command, launch_options,
&process_handle)) {
PLOG(FATAL)
<< "Failed to register or unregister DLL with command: "
<< registration_command;
} else {
if (!base::WaitForExitCodeWithTimeout(
process_handle.Get(), &exit_code,
base::TimeDelta::FromMilliseconds(kDllRegistrationTimeoutMs))) {
LOG(ERROR) << "Timeout waiting to register or unregister DLL with "
"command: " << registration_command;
base::KillProcess(process_handle.Get(), 0, false);
NOTREACHED() << "Aborting test due to registration failure.";
}
}
if (exit_code != 0) {
if (registration_operation == REGISTER) {
LOG(ERROR)
<< "DLL registration failed (exit code: 0x" << std::hex << exit_code
<< ", command: " << registration_command
<< "). Make sure you are running as Admin.";
::ExitProcess(1);
} else {
LOG(WARNING)
<< "DLL unregistration failed (exit code: 0x" << std::hex << exit_code
<< ", command: " << registration_command << ").";
}
}
}
// static
void ScopedChromeFrameRegistrar::RegisterAtPath(
const std::wstring& path, RegistrationType registration_type) {
DoRegistration(path, registration_type, REGISTER);
}
// static
void ScopedChromeFrameRegistrar::UnregisterAtPath(
const std::wstring& path, RegistrationType registration_type) {
DoRegistration(path, registration_type, UNREGISTER);
}
base::FilePath ScopedChromeFrameRegistrar::GetReferenceChromeFrameDllPath() {
base::FilePath reference_build_dir;
PathService::Get(chrome::DIR_APP, &reference_build_dir);
reference_build_dir = reference_build_dir.DirName();
reference_build_dir = reference_build_dir.DirName();
reference_build_dir = reference_build_dir.AppendASCII("chrome_frame");
reference_build_dir = reference_build_dir.AppendASCII("tools");
reference_build_dir = reference_build_dir.AppendASCII("test");
reference_build_dir = reference_build_dir.AppendASCII("reference_build");
reference_build_dir = reference_build_dir.AppendASCII("chrome");
reference_build_dir = reference_build_dir.AppendASCII("servers");
reference_build_dir = reference_build_dir.Append(kChromeFrameDllName);
return reference_build_dir;
}
// static
void ScopedChromeFrameRegistrar::RegisterAndExitProcessIfDirected() {
// This method is invoked before any Chromium helpers have been initialized.
// Take pains to use only Win32 and CRT functions.
int argc = 0;
const wchar_t* const* argv = ::CommandLineToArgvW(::GetCommandLine(), &argc);
if (argc < 2 || ::lstrcmp(argv[1], kCallRegistrationEntrypointSwitch) != 0)
return;
if (argc != 4) {
printf("Usage: %S %S <path to dll> <entrypoint>\n", argv[0],
kCallRegistrationEntrypointSwitch);
return;
}
// The only way to leave from here on down is ExitProcess.
const wchar_t* dll_path = argv[2];
const wchar_t* wide_entrypoint = argv[3];
char entrypoint[256];
HRESULT exit_code = 0;
int entrypoint_len = lstrlen(wide_entrypoint);
if (entrypoint_len <= 0 || entrypoint_len >= arraysize(entrypoint)) {
exit_code = HRESULT_FROM_WIN32(ERROR_PROC_NOT_FOUND);
} else {
// Convert wide to narrow. Since the entrypoint must be a narrow string
// anyway, it is safe to truncate each character like this.
std::copy(wide_entrypoint, wide_entrypoint + entrypoint_len + 1,
&entrypoint[0]);
HMODULE dll_module = ::LoadLibrary(dll_path);
if (dll_module == NULL) {
exit_code = HRESULT_FROM_WIN32(::GetLastError());
} else {
typedef HRESULT (STDAPICALLTYPE *RegisterFp)();
RegisterFp register_func =
reinterpret_cast<RegisterFp>(::GetProcAddress(dll_module,
entrypoint));
if (register_func == NULL) {
exit_code = HRESULT_FROM_WIN32(::GetLastError());
} else {
exit_code = register_func();
}
::FreeLibrary(dll_module);
}
}
::ExitProcess(exit_code);
}
// Non-statics
ScopedChromeFrameRegistrar::ScopedChromeFrameRegistrar(
const std::wstring& path, RegistrationType registration_type)
: registration_type_(registration_type) {
if (!register_chrome_path_provider_) {
// Register paths needed by the ScopedChromeFrameRegistrar.
chrome::RegisterPathProvider();
register_chrome_path_provider_ = true;
}
original_dll_path_ = path;
RegisterChromeFrameAtPath(original_dll_path_);
}
ScopedChromeFrameRegistrar::ScopedChromeFrameRegistrar(
RegistrationType registration_type)
: registration_type_(registration_type) {
if (!register_chrome_path_provider_) {
// Register paths needed by the ScopedChromeFrameRegistrar.
chrome::RegisterPathProvider();
register_chrome_path_provider_ = true;
}
original_dll_path_ = GetChromeFrameBuildPath().value();
RegisterChromeFrameAtPath(original_dll_path_);
}
ScopedChromeFrameRegistrar::~ScopedChromeFrameRegistrar() {
if (base::FilePath(original_dll_path_) !=
base::FilePath(new_chrome_frame_dll_path_)) {
RegisterChromeFrameAtPath(original_dll_path_);
} else if (registration_type_ == PER_USER) {
UnregisterAtPath(new_chrome_frame_dll_path_, registration_type_);
HWND chrome_frame_helper_window =
FindWindow(L"ChromeFrameHelperWindowClass", NULL);
if (IsWindow(chrome_frame_helper_window)) {
PostMessage(chrome_frame_helper_window, WM_CLOSE, 0, 0);
} else {
base::KillProcesses(L"chrome_frame_helper.exe", 0, NULL);
}
}
}
void ScopedChromeFrameRegistrar::RegisterChromeFrameAtPath(
const std::wstring& path) {
RegisterAtPath(path, registration_type_);
new_chrome_frame_dll_path_ = path;
}
void ScopedChromeFrameRegistrar::RegisterReferenceChromeFrameBuild() {
RegisterChromeFrameAtPath(GetReferenceChromeFrameDllPath().value());
}
std::wstring ScopedChromeFrameRegistrar::GetChromeFrameDllPath() const {
return new_chrome_frame_dll_path_;
}
bool IsWorkstationLocked() {
bool is_locked = true;
HDESK input_desk = ::OpenInputDesktop(0, 0, GENERIC_READ);
if (input_desk) {
wchar_t name[256] = {0};
DWORD needed = 0;
if (::GetUserObjectInformation(input_desk,
UOI_NAME,
name,
sizeof(name),
&needed)) {
is_locked = lstrcmpi(name, L"default") != 0;
}
::CloseDesktop(input_desk);
}
return is_locked;
}