diff options
Diffstat (limited to 'chrome_frame')
-rw-r--r-- | chrome_frame/chrome_frame.gyp | 6 | ||||
-rw-r--r-- | chrome_frame/test/ie_configurator.cc | 483 | ||||
-rw-r--r-- | chrome_frame/test/ie_configurator.h | 46 | ||||
-rw-r--r-- | chrome_frame/test/net/fake_external_tab.cc | 17 | ||||
-rw-r--r-- | chrome_frame/test/net/fake_external_tab.h | 2 | ||||
-rw-r--r-- | chrome_frame/test/run_all_unittests.cc | 3 |
6 files changed, 557 insertions, 0 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp index 60c4b4c..921b0a1 100644 --- a/chrome_frame/chrome_frame.gyp +++ b/chrome_frame/chrome_frame.gyp @@ -223,6 +223,8 @@ 'test/delete_chrome_history_test.cc', 'test/dll_redirector_loading_test.cc', 'test/header_test.cc', + 'test/ie_configurator.cc', + 'test/ie_configurator.h', 'test/ie_event_sink.cc', 'test/ie_event_sink.h', 'test/mock_ie_event_sink_actions.h', @@ -404,6 +406,8 @@ '../net/url_request/url_request_unittest.cc', 'test/chrome_frame_test_utils.cc', 'test/chrome_frame_test_utils.h', + 'test/ie_configurator.cc', + 'test/ie_configurator.h', 'test/simulate_input.cc', 'test/simulate_input.h', 'test/test_server.cc', @@ -553,6 +557,8 @@ 'test/chrome_frame_ui_test_utils.cc', 'test/chrome_frame_ui_test_utils.h', 'test/external_sites_test.cc', + 'test/ie_configurator.cc', + 'test/ie_configurator.h', 'test/ie_event_sink.cc', 'test/ie_event_sink.h', 'test/mock_ie_event_sink_actions.h', diff --git a/chrome_frame/test/ie_configurator.cc b/chrome_frame/test/ie_configurator.cc new file mode 100644 index 0000000..d08a803 --- /dev/null +++ b/chrome_frame/test/ie_configurator.cc @@ -0,0 +1,483 @@ +// 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/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"; + +// A helper class that accumulate a set of registry mutations and corresponding +// undo data via calls to its Add*() methods. The mutations can be applied to +// the registry (repeatedly, if desired) via a call to Apply(). Revert() can be +// called to apply the accumulated undo data. +class RegistrySetter { + public: + RegistrySetter(); + ~RegistrySetter(); + + // Adds a mutation that sets a REG_DWORD registry value, creating any + // intermediate keys as necessary. |key| and |value| must remain valid + // throughout all calls to Apply(). + void AddDWORDValue(HKEY root, const wchar_t* key, const wchar_t* value, + DWORD data); + + // Adds a mutation that assigns a FILETIME to a REG_BINARY registry value, + // creating any intermediate keys as necessary. |key| and |value| must remain + // valid throughout all calls to Apply(). + void AddFILETIMEValue(HKEY root, const wchar_t* key, const wchar_t* value, + FILETIME data); + + // Applies all mutations in the order they were added. Errors encountered + // along the way are logged, but do not stop progress. + void Apply() const; + + // Applies the undo data in the reverse order that their corresponding + // mutations were added. + void Revert() const; + + private: + // The data for an individual registry value. A non-existent value is + // indicated by type = REG_NONE and empty data. + struct RegistryData { + HKEY root; + const wchar_t* key; + const wchar_t* value; + DWORD type; + std::vector<uint8> data; + }; + + typedef std::list<RegistryData> RegistryDataList; + + // Adds a mutation to the end of the apply list with the given data, and a + // mutation to the revert list with the current state of the value. + void AddValue(HKEY root, const wchar_t* key, const wchar_t* value, DWORD type, + const uint8* value_begin, size_t value_len); + + // Add a mutation to the revert list that restores a registry value to its + // current state. This only adds entries to set or remove |value|. If + // portions of the hierarchy identified by |key| do not exist, but are created + // between the invocation of this method and the time the revert list is + // applied, only |value| is deleted upon revert (intermediate keys are not + // deleted). + void SaveCurrentValue(HKEY root, const wchar_t* key, const wchar_t* value); + + // Applies all mutations in |data_list| in order. + static void ApplyList(const RegistryDataList& data_list); + + RegistryDataList apply_list_; + RegistryDataList revert_list_; + + DISALLOW_COPY_AND_ASSIGN(RegistrySetter); +}; + +// A Google Test event listener that delegates to a configurator. +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); +}; + +// A configurator for Internet Explorer 7. +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); +}; + +// A configurator for Internet Explorer 9. +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 implementation. + +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) { + // Add a mutation to delete the value. + 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) { + // Add a mutation to delete the value (but not any keys). + 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)); +} + +// static +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 implementation. + +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 implementation + +IE7Configurator::IE7Configurator() { +} + +IE7Configurator::~IE7Configurator() { +} + +void IE7Configurator::Initialize() { + // Suppress the friendly "Hi! I popped up an info bar for you. Did you see + // the info bar I just showed you? There's a yellow thing in your IE window + // that wasn't there before! I call it an Information Bar. Did you notice + // the Information Bar?" dialog that it likes to show. + setter_.AddDWORDValue(HKEY_CURRENT_USER, kKeyIEInformationBar, + kValueFirstTime, 0); +} + +void IE7Configurator::ApplySettings() { + setter_.Apply(); +} + +void IE7Configurator::RevertSettings() { + setter_.Revert(); +} + +// IE9Configurator implementation + +IE9Configurator::IE9Configurator() { +} + +IE9Configurator::~IE9Configurator() { +} + +// Returns true if the per-user setup is complete. +// static +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; +} + +// Returns the path to the IE9 Approved Extensions key for Chrome Frame. +// static +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; +} + +// Returns true if the add-on enablement prompt is disabled by Group Policy. +// static +bool IE9Configurator::IsAddonPromptDisabledForChromeFrame() { + bool is_disabled = false; + base::win::RegKey key; + + // See if the prompt is disabled for this user of IE. + 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; + } + } + + // Now check if the prompt is disabled for all add-ons via Group Policy. + 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() { + // Check for per-user IE setup. + if (!IsPerUserSetupComplete()) { + const HKEY root = HKEY_CURRENT_USER; + // Suppress the "Set up Internet Explorer 9" dialog. + setter_.AddDWORDValue(root, kKeyIEMain, kValueIE9Completed, 1); + setter_.AddFILETIMEValue(root, kKeyIEMain, kValueIE9CompletionTime, + base::Time::Now().ToFileTime()); + setter_.AddDWORDValue(root, kKeyIEMain, kValueIE9LastShown, 1); + // Don't show a tour of IE 9. + setter_.AddDWORDValue(root, kKeyIEMain, kValueIE9TourNoShow, 1); + // Turn off the phishing filter. + setter_.AddDWORDValue(root, kKeyIEPhishingFilter, kValueEnabledV9, 0); + // Don't download compatibility view lists. + setter_.AddDWORDValue(root, kKeyIEBrowserEmulation, + kValueMSCompatibilityMode, 0); + } + + // Turn off the "'Foo' add-on from 'Bar' is ready for use." prompt via Group + // Policy. + if (!IsAddonPromptDisabledForChromeFrame()) { + setter_.AddDWORDValue(HKEY_LOCAL_MACHINE, kKeyPoliciesExt, + kValueIgnoreFrameApprovalCheck, 1); + } +} + +void IE9Configurator::ApplySettings() { + setter_.Apply(); +} + +void IE9Configurator::RevertSettings() { + setter_.Revert(); +} + +} // namespace + +// Configurator implementation. + +IEConfigurator::IEConfigurator() { +} + +IEConfigurator::~IEConfigurator() { +} + +IEConfigurator* CreateConfigurator() { + IEConfigurator* configurator = NULL; + + switch (GetInstalledIEVersion()) { + case IE_7: + configurator = new IE7Configurator(); + 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)); + } +} + +} // namespace chrome_frame_test diff --git a/chrome_frame/test/ie_configurator.h b/chrome_frame/test/ie_configurator.h new file mode 100644 index 0000000..8ccc540 --- /dev/null +++ b/chrome_frame/test/ie_configurator.h @@ -0,0 +1,46 @@ +// 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. + +// The IE configurator mutates Internet Explorer's configuration to put it in a +// (thought to be) known good state for the Chrome Frame tests. The current +// user's IE configuration may be left in a different state than it was +// initially; see the implementation for details. + +#ifndef CHROME_FRAME_TEST_IE_CONFIGURATOR_H_ +#define CHROME_FRAME_TEST_IE_CONFIGURATOR_H_ +#pragma once + +#include "base/basictypes.h" + +namespace chrome_frame_test { + +// Abstract interface to be implemented for per-version configurators. +class IEConfigurator { + public: + virtual ~IEConfigurator(); + + // Initializes a configurator, causing it to cache existing configuration + // settings that it will modify. + virtual void Initialize() = 0; + + // Applies all configuration settings. + virtual void ApplySettings() = 0; + + // Reverts all configuration settings. + virtual void RevertSettings() = 0; + + protected: + IEConfigurator(); +}; + +// Returns a new configurator for the current configuration, or NULL if none +// applies. +IEConfigurator* CreateConfigurator(); + +// Installs a configurator in the Google Test unit test singleton. +void InstallIEConfigurator(); + +} // namespace chrome_frame_test + +#endif // CHROME_FRAME_TEST_IE_CONFIGURATOR_H_ diff --git a/chrome_frame/test/net/fake_external_tab.cc b/chrome_frame/test/net/fake_external_tab.cc index 322231c..9156409 100644 --- a/chrome_frame/test/net/fake_external_tab.cc +++ b/chrome_frame/test/net/fake_external_tab.cc @@ -514,6 +514,11 @@ void CFUrlRequestUnittestRunner::StartChromeFrameInHostBrowser() { base::win::ScopedCOMInitializer com; chrome_frame_test::CloseAllIEWindows(); + // Tweak IE settings to make it amenable to testing before launching it. + ie_configurator_.reset(chrome_frame_test::CreateConfigurator()); + if (ie_configurator_.get() != NULL) + ie_configurator_->ApplySettings(); + test_http_server_.reset(new test_server::SimpleWebServer(kTestServerPort)); test_http_server_->AddResponse(&chrome_frame_html_); std::wstring url(base::StringPrintf(L"http://localhost:%i/chrome_frame", @@ -541,6 +546,10 @@ void CFUrlRequestUnittestRunner::ShutDownHostBrowser() { void CFUrlRequestUnittestRunner::OnIEShutdownFailure() { LOG(ERROR) << "Failed to shutdown IE and npchrome_frame cleanly after test " "execution."; + + if (ie_configurator_.get() != NULL) + ie_configurator_->RevertSettings(); + StopFileLogger(true); ::ExitProcess(1); } @@ -597,8 +606,13 @@ void CFUrlRequestUnittestRunner::OnInitialTabLoaded() { void CFUrlRequestUnittestRunner::OnProviderDestroyed() { if (tests_ran_) { StopFileLogger(false); + + if (ie_configurator_.get() != NULL) + ie_configurator_->RevertSettings(); + if (crash_service_) base::KillProcess(crash_service_, 0, false); + ::ExitProcess(test_result()); } else { DLOG(ERROR) << "Automation Provider shutting down before test execution " @@ -696,6 +710,9 @@ void CFUrlRequestUnittestRunner::OnInitializationTimeout() { chrome_frame_test::CloseAllIEWindows(); } + if (ie_configurator_.get() != NULL) + ie_configurator_->RevertSettings(); + if (crash_service_) base::KillProcess(crash_service_, 0, false); diff --git a/chrome_frame/test/net/fake_external_tab.h b/chrome_frame/test/net/fake_external_tab.h index 964cb13..cd92dc0 100644 --- a/chrome_frame/test/net/fake_external_tab.h +++ b/chrome_frame/test/net/fake_external_tab.h @@ -14,6 +14,7 @@ #include "base/process.h" #include "base/win/scoped_handle.h" #include "chrome/browser/browser_process_impl.h" +#include "chrome_frame/test/ie_configurator.h" #include "chrome_frame/test/net/process_singleton_subclass.h" #include "chrome_frame/test/net/test_automation_provider.h" #include "chrome_frame/test/test_server.h" @@ -159,6 +160,7 @@ class CFUrlRequestUnittestRunner base::CancelableClosure timeout_closure_; scoped_ptr<logging_win::FileLogger> file_logger_; FilePath log_file_; + scoped_ptr<chrome_frame_test::IEConfigurator> ie_configurator_; DISALLOW_COPY_AND_ASSIGN(CFUrlRequestUnittestRunner); }; diff --git a/chrome_frame/test/run_all_unittests.cc b/chrome_frame/test/run_all_unittests.cc index b5727e5..88e6f5c 100644 --- a/chrome_frame/test/run_all_unittests.cc +++ b/chrome_frame/test/run_all_unittests.cc @@ -15,6 +15,7 @@ #include "chrome_frame/crash_server_init.h" #include "chrome_frame/test/chrome_frame_test_utils.h" #include "chrome_frame/test/chrome_frame_ui_test_utils.h" +#include "chrome_frame/test/ie_configurator.h" #include "chrome_frame/test/test_scrubber.h" #include "chrome_frame/test_utils.h" #include "chrome_frame/utils.h" @@ -76,6 +77,8 @@ int main(int argc, char **argv) { if (!CommandLine::ForCurrentProcess()->HasSwitch(kNoLogCollector)) logging_win::InstallTestLogCollector(testing::UnitTest::GetInstance()); + chrome_frame_test::InstallIEConfigurator(); + chrome_frame_test::InstallTestScrubber(testing::UnitTest::GetInstance()); int ret = -1; |