#include "chrome_frame/test/ie_configurator.h"
#include <windows.h>
#include <objbase.h>
#include <ios>
#include <list>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "base/string16.h"
#include "base/time.h"
#include "base/win/registry.h"
#include "chrome_frame/chrome_tab.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace chrome_frame_test {
namespace {
const wchar_t kKeyIEApprovedExtensions[] =
L"Software\\Microsoft\\Internet Explorer\\Approved Extensions\\";
const wchar_t kKeyIEInformationBar[] =
L"Software\\Microsoft\\Internet Explorer\\InformationBar";
const wchar_t kKeyIEMain[] =
L"Software\\Microsoft\\Internet Explorer\\Main";
const wchar_t kKeyIEPhishingFilter[] =
L"Software\\Microsoft\\Internet Explorer\\PhishingFilter";
const wchar_t kKeyIEBrowserEmulation[] =
L"Software\\Microsoft\\Internet Explorer\\BrowserEmulation";
const wchar_t kKeyPoliciesExt[] =
L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Policies\\Ext";
const wchar_t kValueEnabledV9[] = L"EnabledV9";
const wchar_t kValueFirstTime[] = L"FirstTime";
const wchar_t kValueIE9Completed[] = L"IE9RunOncePerInstallCompleted";
const wchar_t kValueIE9CompletionTime[] = L"IE9RunOnceCompletionTime";
const wchar_t kValueIE9LastShown[] = L"IE9RunOnceLastShown";
const wchar_t kValueIE9TourNoShow[] = L"IE9TourNoShow";
const wchar_t kValueIgnoreFrameApprovalCheck[] = L"IgnoreFrameApprovalCheck";
const wchar_t kValueMSCompatibilityMode[] = L"MSCompatibilityMode";
class RegistrySetter {
public:
RegistrySetter();
~RegistrySetter();
void AddDWORDValue(HKEY root, const wchar_t* key, const wchar_t* value,
DWORD data);
void AddFILETIMEValue(HKEY root, const wchar_t* key, const wchar_t* value,
FILETIME data);
void Apply() const;
void Revert() const;
private:
struct RegistryData {
HKEY root;
const wchar_t* key;
const wchar_t* value;
DWORD type;
std::vector<uint8> data;
};
typedef std::list<RegistryData> RegistryDataList;
void AddValue(HKEY root, const wchar_t* key, const wchar_t* value, DWORD type,
const uint8* value_begin, size_t value_len);
void SaveCurrentValue(HKEY root, const wchar_t* key, const wchar_t* value);
static void ApplyList(const RegistryDataList& data_list);
RegistryDataList apply_list_;
RegistryDataList revert_list_;
DISALLOW_COPY_AND_ASSIGN(RegistrySetter);
};
class ConfiguratorDriver : public testing::EmptyTestEventListener {
public:
explicit ConfiguratorDriver(IEConfigurator* configurator);
virtual ~ConfiguratorDriver();
virtual void OnTestProgramStart(const testing::UnitTest& unit_test) OVERRIDE;
virtual void OnTestStart(const testing::TestInfo& test_info) OVERRIDE;
virtual void OnTestProgramEnd(const testing::UnitTest& unit_test) OVERRIDE;
private:
scoped_ptr<IEConfigurator> configurator_;
DISALLOW_COPY_AND_ASSIGN(ConfiguratorDriver);
};
class IE7Configurator : public IEConfigurator {
public:
IE7Configurator();
virtual ~IE7Configurator();
virtual void Initialize() OVERRIDE;
virtual void ApplySettings() OVERRIDE;
virtual void RevertSettings() OVERRIDE;
private:
RegistrySetter setter_;
DISALLOW_COPY_AND_ASSIGN(IE7Configurator);
};
class IE9Configurator : public IEConfigurator {
public:
IE9Configurator();
virtual ~IE9Configurator();
virtual void Initialize() OVERRIDE;
virtual void ApplySettings() OVERRIDE;
virtual void RevertSettings() OVERRIDE;
private:
static bool IsPerUserSetupComplete();
static string16 GetChromeFrameBHOCLSID();
static bool IsAddonPromptDisabledForChromeFrame();
RegistrySetter setter_;
DISALLOW_COPY_AND_ASSIGN(IE9Configurator);
};
RegistrySetter::RegistrySetter() {
}
RegistrySetter::~RegistrySetter() {
}
void RegistrySetter::AddValue(HKEY root,
const wchar_t* key,
const wchar_t* value,
DWORD type,
const uint8* value_begin,
size_t value_len) {
RegistryData the_data = {
root,
key,
value,
type,
std::vector<uint8>(value_begin, value_begin + value_len)
};
apply_list_.push_back(the_data);
SaveCurrentValue(root, key, value);
}
void RegistrySetter::SaveCurrentValue(HKEY root,
const wchar_t* key,
const wchar_t* value) {
base::win::RegKey the_key;
RegistryData the_data = { root, key, value, REG_NONE };
LONG result = the_key.Open(root, key, KEY_QUERY_VALUE);
if (result == ERROR_SUCCESS) {
DWORD size = 0;
result = the_key.ReadValue(value, NULL, &size, &the_data.type);
if (result == ERROR_FILE_NOT_FOUND) {
the_data.type = REG_NONE;
revert_list_.push_front(the_data);
} else if (result == ERROR_SUCCESS) {
the_data.data.resize(size);
result = the_key.ReadValue(value, &the_data.data[0], &size,
&the_data.type);
if (result == ERROR_SUCCESS) {
revert_list_.push_front(the_data);
} else {
::SetLastError(result);
PLOG(ERROR) << __FUNCTION__ << " unexpected error reading data for "
<< value << " from key " << key
<< ". The current value will not be restored upon revert.";
}
} else {
::SetLastError(result);
PLOG(ERROR) << __FUNCTION__ << " unexpected error reading " << value
<< " from key " << key
<< ". The current value will not be restored upon revert.";
}
} else if (result == ERROR_FILE_NOT_FOUND) {
revert_list_.push_front(the_data);
} else {
::SetLastError(result);
PLOG(ERROR) << __FUNCTION__ << " unexpected error opening key " << key
<< " to read value " << value
<< ". The current value will not be restored upon revert.";
}
}
void RegistrySetter::AddDWORDValue(HKEY root,
const wchar_t* key,
const wchar_t* value,
DWORD data) {
const uint8* data_ptr = reinterpret_cast<uint8*>(&data);
AddValue(root, key, value, REG_DWORD, data_ptr, sizeof(data));
}
void RegistrySetter::AddFILETIMEValue(HKEY root,
const wchar_t* key,
const wchar_t* value,
FILETIME data) {
const uint8* data_ptr = reinterpret_cast<uint8*>(&data);
AddValue(root, key, value, REG_BINARY, data_ptr, sizeof(data));
}
void RegistrySetter::ApplyList(const RegistryDataList& data_list) {
base::win::RegKey key;
LONG result = ERROR_SUCCESS;
for (RegistryDataList::const_iterator scan(data_list.begin());
scan != data_list.end(); ++scan) {
const RegistryData& data = *scan;
const bool do_delete = (data.type == REG_NONE && data.data.empty());
result = do_delete ?
key.Open(data.root, data.key, KEY_SET_VALUE) :
key.Create(data.root, data.key, KEY_SET_VALUE);
if (result == ERROR_SUCCESS) {
if (do_delete) {
result = key.DeleteValue(data.value);
} else {
result = key.WriteValue(data.value,
data.data.empty() ? NULL : &data.data[0],
data.data.size(), data.type);
}
if (result != ERROR_SUCCESS) {
::SetLastError(result);
PLOG(ERROR) << "Failed to " << (do_delete ? "delete" : "set")
<< " value " << data.value
<< " in registry key " << data.key
<< " in hive " << std::hex <<data.root << std::dec;
}
} else if (!do_delete || result != ERROR_FILE_NOT_FOUND) {
::SetLastError(result);
PLOG(ERROR) << "Failed to create/open registry key " << data.key
<< " in hive " << std::hex << data.root << std::dec;
}
}
}
void RegistrySetter::Apply() const {
ApplyList(apply_list_);
}
void RegistrySetter::Revert() const {
ApplyList(revert_list_);
}
ConfiguratorDriver::ConfiguratorDriver(IEConfigurator* configurator)
: configurator_(configurator) {
DCHECK(configurator);
}
ConfiguratorDriver::~ConfiguratorDriver() {
}
void ConfiguratorDriver::OnTestProgramStart(
const testing::UnitTest& unit_test) {
configurator_->Initialize();
}
void ConfiguratorDriver::OnTestStart(const testing::TestInfo& test_info) {
configurator_->ApplySettings();
}
void ConfiguratorDriver::OnTestProgramEnd(const testing::UnitTest& unit_test) {
configurator_->RevertSettings();
}
IE7Configurator::IE7Configurator() {
}
IE7Configurator::~IE7Configurator() {
}
void IE7Configurator::Initialize() {
setter_.AddDWORDValue(HKEY_CURRENT_USER, kKeyIEInformationBar,
kValueFirstTime, 0);
}
void IE7Configurator::ApplySettings() {
setter_.Apply();
}
void IE7Configurator::RevertSettings() {
setter_.Revert();
}
IE9Configurator::IE9Configurator() {
}
IE9Configurator::~IE9Configurator() {
}
bool IE9Configurator::IsPerUserSetupComplete() {
bool is_complete = false;
base::win::RegKey key_main;
if (key_main.Open(HKEY_CURRENT_USER, kKeyIEMain,
KEY_QUERY_VALUE) == ERROR_SUCCESS) {
DWORD completed = 0;
FILETIME completion_time = {};
DWORD size = sizeof(completion_time);
if (key_main.ReadValueDW(kValueIE9Completed,
&completed) == ERROR_SUCCESS &&
completed != 0 &&
key_main.ReadValue(kValueIE9CompletionTime, &completion_time,
&size, NULL) == ERROR_SUCCESS &&
size == sizeof(completion_time)) {
is_complete = true;
}
}
return is_complete;
}
string16 IE9Configurator::GetChromeFrameBHOCLSID() {
string16 bho_guid(39, L'\0');
int guid_len = StringFromGUID2(CLSID_ChromeFrameBHO, &bho_guid[0],
bho_guid.size());
DCHECK_EQ(guid_len, static_cast<int>(bho_guid.size()));
bho_guid.resize(guid_len - 1);
return bho_guid;
}
bool IE9Configurator::IsAddonPromptDisabledForChromeFrame() {
bool is_disabled = false;
base::win::RegKey key;
if (key.Open(HKEY_CURRENT_USER, kKeyIEApprovedExtensions,
KEY_QUERY_VALUE) == ERROR_SUCCESS) {
DWORD type = REG_NONE;
DWORD size = 0;
if (key.ReadValue(GetChromeFrameBHOCLSID().c_str(), NULL, &size,
&type) == ERROR_SUCCESS &&
type == REG_BINARY) {
is_disabled = true;
}
}
if (!is_disabled &&
key.Open(HKEY_LOCAL_MACHINE, kKeyPoliciesExt,
KEY_QUERY_VALUE) == ERROR_SUCCESS) {
DWORD ignore = 0;
if (key.ReadValueDW(kValueIgnoreFrameApprovalCheck,
&ignore) == ERROR_SUCCESS &&
ignore != 0) {
is_disabled = true;
}
}
return is_disabled;
}
void IE9Configurator::Initialize() {
if (!IsPerUserSetupComplete()) {
const HKEY root = HKEY_CURRENT_USER;
setter_.AddDWORDValue(root, kKeyIEMain, kValueIE9Completed, 1);
setter_.AddFILETIMEValue(root, kKeyIEMain, kValueIE9CompletionTime,
base::Time::Now().ToFileTime());
setter_.AddDWORDValue(root, kKeyIEMain, kValueIE9LastShown, 1);
setter_.AddDWORDValue(root, kKeyIEMain, kValueIE9TourNoShow, 1);
setter_.AddDWORDValue(root, kKeyIEPhishingFilter, kValueEnabledV9, 0);
setter_.AddDWORDValue(root, kKeyIEBrowserEmulation,
kValueMSCompatibilityMode, 0);
}
if (!IsAddonPromptDisabledForChromeFrame()) {
setter_.AddDWORDValue(HKEY_LOCAL_MACHINE, kKeyPoliciesExt,
kValueIgnoreFrameApprovalCheck, 1);
}
}
void IE9Configurator::ApplySettings() {
setter_.Apply();
}
void IE9Configurator::RevertSettings() {
setter_.Revert();
}
}
IEConfigurator::IEConfigurator() {
}
IEConfigurator::~IEConfigurator() {
}
IEConfigurator* CreateConfigurator() {
IEConfigurator* configurator = NULL;
switch (GetInstalledIEVersion()) {
case IE_7:
configurator = new IE7Configurator();
break;
case IE_9:
configurator = new IE9Configurator();
break;
default:
break;
}
return configurator;
}
void InstallIEConfigurator() {
IEConfigurator* configurator = CreateConfigurator();
if (configurator != NULL) {
testing::UnitTest::GetInstance()->listeners().Append(
new ConfiguratorDriver(configurator));
}
}
}