blob: 3922e96ff6746f01ab6cf88770d3eb2efd6c3831 [file] [log] [blame]
// 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/net/fake_external_tab.h"
#include <atlbase.h>
#include <atlcom.h>
#include <exdisp.h>
#include <Winsock2.h>
#include "base/bind.h"
#include "base/command_line.h"
#include "base/debug/debugger.h"
#include "base/file_util.h"
#include "base/file_version_info.h"
#include "base/files/scoped_temp_dir.h"
#include "base/i18n/icu_util.h"
#include "base/lazy_instance.h"
#include "base/memory/scoped_ptr.h"
#include "base/path_service.h"
#include "base/prefs/json_pref_store.h"
#include "base/prefs/pref_registry_simple.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_piece.h"
#include "base/strings/string_util.h"
#include "base/strings/stringprintf.h"
#include "base/system_monitor/system_monitor.h"
#include "base/test/test_timeouts.h"
#include "base/threading/platform_thread.h"
#include "base/threading/thread.h"
#include "base/win/scoped_comptr.h"
#include "base/win/scoped_handle.h"
#include "chrome/app/chrome_main_delegate.h"
#include "chrome/browser/automation/automation_provider_list.h"
#include "chrome/browser/chrome_content_browser_client.h"
#include "chrome/browser/prefs/browser_prefs.h"
#include "chrome/browser/prefs/proxy_config_dictionary.h"
#include "chrome/browser/process_singleton.h"
#include "chrome/browser/profiles/chrome_browser_main_extra_parts_profiles.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/profiles/profiles_state.h"
#include "chrome/browser/renderer_host/web_cache_manager.h"
#include "chrome/common/chrome_constants.h"
#include "chrome/common/chrome_content_client.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/chrome_paths_internal.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/renderer/chrome_content_renderer_client.h"
#include "chrome/test/base/ui_test_utils.h"
#include "chrome/test/logging/win/file_logger.h"
#include "chrome/test/logging/win/log_file_printer.h"
#include "chrome/test/logging/win/test_log_collector.h"
#include "chrome_frame/crash_server_init.h"
#include "chrome_frame/test/chrome_frame_test_utils.h"
#include "chrome_frame/test/ie_configurator.h"
#include "chrome_frame/test/net/test_automation_resource_message_filter.h"
#include "chrome_frame/test/simulate_input.h"
#include "chrome_frame/test/win_event_receiver.h"
#include "chrome_frame/utils.h"
#include "content/public/app/content_main.h"
#include "content/public/app/startup_helper_win.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/notification_service.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/common/content_client.h"
#include "content/public/common/content_paths.h"
#include "net/base/net_util.h"
#include "net/url_request/url_request_test_util.h"
#include "sandbox/win/src/sandbox_types.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/base/resource/resource_bundle_win.h"
#include "ui/base/ui_base_paths.h"
#if defined(USE_AURA)
#include "ui/gfx/screen.h"
#include "ui/views/widget/desktop_aura/desktop_screen.h"
#endif
using content::BrowserThread;
namespace {
// We must store this globally so that our main delegate can set it.
static CFUrlRequestUnittestRunner* g_test_suite = NULL;
// Copied here for access by CreateBrowserMainParts and InitGoogleTest.
static int g_argc = 0;
static char** g_argv = NULL;
// A special command line switch to allow developers to manually launch the
// browser and debug CF inside the browser.
const char kManualBrowserLaunch[] = "manual-browser";
// Pops up a message box after the test environment has been set up
// and before tearing it down. Useful for when debugging tests and not
// the test environment that's been set up.
const char kPromptAfterSetup[] = "prompt-after-setup";
const int kTestServerPort = 4666;
// The test HTML we use to initialize Chrome Frame.
// Note that there's a little trick in there to avoid an extra URL request
// that the browser will otherwise make for the site's favicon.
// If we don't do this the browser will create a new URL request after
// the CF page has been initialized and that URL request will confuse the
// global URL instance counter in the unit tests and subsequently trip
// some DCHECKs.
const char kChromeFrameHtml[] = "<html><head>"
"<meta http-equiv=\"X-UA-Compatible\" content=\"chrome=1\" />"
"<link rel=\"shortcut icon\" href=\"file://c:\\favicon.ico\"/>"
"</head><body>Chrome Frame should now be loaded</body></html>";
// Uses the IAccessible interface for the window to set the focus.
// This can be useful when you don't have control over the thread that
// owns the window.
// NOTE: this depends on oleacc.lib which the net tests already depend on
// but other unit tests don't depend on oleacc so we can't just add the method
// directly into chrome_frame_test_utils.cc (without adding a
// #pragma comment(lib, "oleacc.lib")).
bool SetFocusToAccessibleWindow(HWND hwnd) {
bool ret = false;
base::win::ScopedComPtr<IAccessible> acc;
AccessibleObjectFromWindow(hwnd, OBJID_WINDOW, IID_IAccessible,
reinterpret_cast<void**>(acc.Receive()));
if (acc) {
VARIANT self = { VT_I4 };
self.lVal = CHILDID_SELF;
ret = SUCCEEDED(acc->accSelect(SELFLAG_TAKEFOCUS, self));
}
return ret;
}
class FakeContentBrowserClient : public chrome::ChromeContentBrowserClient {
public:
virtual ~FakeContentBrowserClient() {}
virtual content::BrowserMainParts* CreateBrowserMainParts(
const content::MainFunctionParams& parameters) OVERRIDE;
};
base::LazyInstance<chrome::ChromeContentClient>
g_chrome_content_client = LAZY_INSTANCE_INITIALIZER;
// Override the default ContentBrowserClient to let Chrome participate in
// content logic. Must be done before any tabs are created.
base::LazyInstance<FakeContentBrowserClient>
g_browser_client = LAZY_INSTANCE_INITIALIZER;
base::LazyInstance<chrome::ChromeContentRendererClient>
g_renderer_client = LAZY_INSTANCE_INITIALIZER;
class FakeMainDelegate : public content::ContentMainDelegate {
public:
virtual ~FakeMainDelegate() {}
virtual bool BasicStartupComplete(int* exit_code) OVERRIDE {
logging_win::InstallTestLogCollector(
testing::UnitTest::GetInstance());
content::SetContentClient(&g_chrome_content_client.Get());
content::SetRendererClientForTesting(&g_renderer_client.Get());
return false;
}
// Override the default ContentBrowserClient to let Chrome participate in
// content logic. We use a subclass of Chrome's implementation,
// FakeContentBrowserClient, to override CreateBrowserMainParts. Must
// be done before any tabs are created.
virtual content::ContentBrowserClient* CreateContentBrowserClient() OVERRIDE {
return &g_browser_client.Get();
};
};
void FilterDisabledTests() {
if (::testing::FLAGS_gtest_filter.length() &&
::testing::FLAGS_gtest_filter != "*") {
// Don't override user specified filters.
return;
}
const char* disabled_tests[] = {
// Tests disabled since they're testing the same functionality used
// by the TestAutomationProvider.
"URLRequestTest.Intercept",
"URLRequestTest.InterceptNetworkError",
"URLRequestTest.InterceptRestartRequired",
"URLRequestTest.InterceptRespectsCancelMain",
"URLRequestTest.InterceptRespectsCancelRedirect",
"URLRequestTest.InterceptRespectsCancelFinal",
"URLRequestTest.InterceptRespectsCancelInRestart",
"URLRequestTest.InterceptRedirect",
"URLRequestTest.InterceptServerError",
"URLRequestTestFTP.*",
// Tests that are currently not working:
// Temporarily disabled because they needs user input (login dialog).
"URLRequestTestHTTP.BasicAuth",
"URLRequestTestHTTP.BasicAuthWithCookies",
// ChromeFrame does not support load timing.
"URLRequestTestHTTP.BasicAuthLoadTiming",
"URLRequestTestHTTP.GetTestLoadTiming",
"URLRequestTestHTTP.RedirectLoadTiming",
// HTTPS tests temporarily disabled due to the certificate error dialog.
// TODO(tommi): The tests currently fail though, so need to fix.
"HTTPSRequestTest.HTTPSMismatchedTest",
"HTTPSRequestTest.HTTPSExpiredTest",
"HTTPSRequestTest.ClientAuthTest",
// More HTTPS tests failing due to certificate dialogs.
// http://crbug.com/102991
"URLRequestTestHTTP.HTTPSToHTTPRedirectNoRefererTest",
"HTTPSRequestTest.HTTPSGetTest",
// Tests chrome's network stack's cache (might not apply to CF).
"URLRequestTestHTTP.VaryHeader",
"URLRequestTestHTTP.GetZippedTest",
// Tests that requests can be blocked asynchronously in states
// OnBeforeURLRequest, OnBeforeSendHeaders and OnHeadersReceived. At least
// the second state is not supported by CF.
"URLRequestTestHTTP.NetworkDelegateBlockAsynchronously",
// Tests for cancelling requests in states OnBeforeSendHeaders and
// OnHeadersReceived, which do not seem supported by CF.
"URLRequestTestHTTP.NetworkDelegateCancelRequestSynchronously2",
"URLRequestTestHTTP.NetworkDelegateCancelRequestSynchronously3",
"URLRequestTestHTTP.NetworkDelegateCancelRequestAsynchronously2",
"URLRequestTestHTTP.NetworkDelegateCancelRequestAsynchronously3",
// Tests that requests can be cancelled while blocking in
// OnBeforeSendHeaders state. But this state is not supported by CF.
"URLRequestTestHTTP.NetworkDelegateCancelWhileWaiting2",
// Tests that requests can be cancelled while blocking in
// OnHeadersRecevied state. At first glance, this state does not appear to
// be supported by CF.
"URLRequestTestHTTP.NetworkDelegateCancelWhileWaiting3",
// Tests that requests can be cancelled while blocking in OnAuthRequired
// state. At first glance, this state does not appear to be supported by CF.
// IE displays a credentials prompt during this test - I (erikwright)
// believe that, from Chrome's point of view this is not a state change. In
// any case, I also believe that we do not have support for handling the
// credentials dialog during tests.
"URLRequestTestHTTP.NetworkDelegateCancelWhileWaiting4",
// I suspect we can only get this one to work (if at all) on IE8 and
// later by using the new INTERNET_OPTION_SUPPRESS_BEHAVIOR flags
// See http://msdn.microsoft.com/en-us/library/aa385328(VS.85).aspx
"URLRequestTest.DoNotSaveCookies",
"URLRequestTest.DelayedCookieCallback",
// TODO(ananta): This test has been consistently failing. Disabling it for
// now.
"URLRequestTestHTTP.GetTest_NoCache",
// These tests use HTTPS, and IE's trust store does not have the test
// certs. So these tests time out waiting for user input. The
// functionality they test (HTTP Strict Transport Security and
// HTTP-based Public Key Pinning) does not work in Chrome Frame anyway.
"URLRequestTestHTTP.ProcessPKP",
"URLRequestTestHTTP.ProcessSTS",
"URLRequestTestHTTP.ProcessSTSOnce",
"URLRequestTestHTTP.ProcessSTSAndPKP",
"URLRequestTestHTTP.ProcessSTSAndPKP2",
// These tests have been disabled as the Chrome cookie policies don't make
// sense or have not been implemented for the host network stack.
"URLRequestTest.DoNotSaveCookies_ViaPolicy",
"URLRequestTest.DoNotSendCookies_ViaPolicy",
"URLRequestTest.DoNotSaveCookies_ViaPolicy_Async",
"URLRequestTest.CookiePolicy_ForceSession",
"URLRequestTest.DoNotSendCookies",
"URLRequestTest.DoNotSendCookies_ViaPolicy_Async",
"URLRequestTest.CancelTest_During_OnGetCookies",
"URLRequestTest.CancelTest_During_OnSetCookie",
// These tests are disabled as they rely on functionality provided by
// Chrome's HTTP stack like the ability to set the proxy for a URL, etc.
"URLRequestTestHTTP.ProxyTunnelRedirectTest",
"URLRequestTestHTTP.NetworkDelegateTunnelConnectionFailed",
"URLRequestTestHTTP.UnexpectedServerAuthTest",
// These tests are disabled as they expect an empty UA to be echoed back
// from the server which is not the case in ChromeFrame.
"URLRequestTestHTTP.DefaultUserAgent",
"URLRequestTestHTTP.EmptyHttpUserAgentSettings",
// This test modifies the UploadData object after it has been marshaled to
// ChromeFrame. We don't support this.
"URLRequestTestHTTP.TestPostChunkedDataAfterStart",
// Do not work in CF, it may well be that IE is unconditionally
// adding Accept-Encoding header by default to outgoing requests.
"URLRequestTestHTTP.DefaultAcceptEncoding",
"URLRequestTestHTTP.OverrideAcceptEncoding",
// Not supported in ChromeFrame as we use IE's network stack.
"URLRequestTest.NetworkDelegateProxyError",
"URLRequestTest.AcceptClockSkewCookieWithWrongDateTimezone",
// URLRequestAutomationJob needs to support NeedsAuth.
// http://crbug.com/98446
"URLRequestTestHTTP.NetworkDelegateOnAuthRequiredSyncNoAction",
"URLRequestTestHTTP.NetworkDelegateOnAuthRequiredSyncSetAuth",
"URLRequestTestHTTP.NetworkDelegateOnAuthRequiredSyncCancel",
"URLRequestTestHTTP.NetworkDelegateOnAuthRequiredAsyncNoAction",
"URLRequestTestHTTP.NetworkDelegateOnAuthRequiredAsyncSetAuth",
"URLRequestTestHTTP.NetworkDelegateOnAuthRequiredAsyncCancel",
// Flaky on the tryservers, http://crbug.com/103097
"URLRequestTestHTTP.MultipleRedirectTest",
"URLRequestTestHTTP.NetworkDelegateRedirectRequest",
// These tests are unsupported in CF.
"HTTPSRequestTest.HTTPSPreloadedHSTSTest",
"HTTPSRequestTest.HTTPSErrorsNoClobberTSSTest",
"HTTPSRequestTest.HSTSPreservesPosts",
"HTTPSRequestTest.ResumeTest",
"HTTPSRequestTest.SSLSessionCacheShardTest",
"HTTPSRequestTest.SSLSessionCacheShardTest",
"HTTPSRequestTest.SSLv3Fallback",
"HTTPSRequestTest.TLSv1Fallback",
"HTTPSHardFailTest.*",
"HTTPSOCSPTest.*",
"HTTPSEVCRLSetTest.*",
"HTTPSCRLSetTest.*",
// Chrome Frame doesn't support GetFullRequestHeaders.
"URLRequestTest*.*_GetFullRequestHeaders",
// IE redirects to data: URLs differently.
"URLRequestTestHTTP.RestrictDataRedirects"
};
const char* ie9_disabled_tests[] = {
// These always hang on Joi's box with IE9, http://crbug.com/105435.
// Several other tests, e.g. URLRequestTestHTTP.CancelTest2, 3 and
// 5, often hang but not always.
"URLRequestTestHTTP.NetworkDelegateRedirectRequestPost",
"URLRequestTestHTTP.GetTest",
"HTTPSRequestTest.HTTPSPreloadedHSTSTest",
// This always hangs on erikwright's box with IE9.
"URLRequestTestHTTP.Redirect302Tests"
};
std::string filter("-"); // All following filters will be negative.
for (int i = 0; i < arraysize(disabled_tests); ++i) {
if (i > 0)
filter += ":";
// If the rule has the form TestSuite.TestCase, also filter out
// TestSuite.FLAKY_TestCase . This way the exclusion rules above
// don't need to be updated when a test is marked flaky.
base::StringPiece test_name(disabled_tests[i]);
size_t dot_index = test_name.find('.');
if (dot_index != base::StringPiece::npos &&
dot_index + 1 < test_name.size()) {
test_name.substr(0, dot_index).AppendToString(&filter);
filter += ".FLAKY_";
test_name.substr(dot_index + 1).AppendToString(&filter);
filter += ":";
}
filter += disabled_tests[i];
}
if (chrome_frame_test::GetInstalledIEVersion() >= IE_9) {
for (int i = 0; i < arraysize(ie9_disabled_tests); ++i) {
filter += ":";
filter += ie9_disabled_tests[i];
}
}
::testing::FLAGS_gtest_filter = filter;
}
} // namespace
// Same as BrowserProcessImpl, but uses custom profile manager.
class FakeBrowserProcessImpl : public BrowserProcessImpl {
public:
FakeBrowserProcessImpl(base::SequencedTaskRunner* local_state_task_runner,
const CommandLine& command_line)
: BrowserProcessImpl(local_state_task_runner, command_line) {
profiles_dir_.CreateUniqueTempDir();
}
virtual ~FakeBrowserProcessImpl() {}
virtual ProfileManager* profile_manager() OVERRIDE {
if (!profile_manager_.get()) {
profile_manager_.reset(
new ProfileManagerWithoutInit(profiles_dir_.path()));
}
return profile_manager_.get();
}
virtual MetricsService* metrics_service() OVERRIDE {
return NULL;
}
void DestroyProfileManager() {
profile_manager_.reset();
}
private:
base::ScopedTempDir profiles_dir_;
scoped_ptr<ProfileManager> profile_manager_;
};
class SupplyProxyCredentials : public WindowObserver {
public:
SupplyProxyCredentials(const char* username, const char* password);
protected:
struct DialogProps {
HWND username_;
HWND password_;
};
virtual void OnWindowOpen(HWND hwnd);
virtual void OnWindowClose(HWND hwnd);
static BOOL CALLBACK EnumChildren(HWND hwnd, LPARAM param);
protected:
std::string username_;
std::string password_;
};
SupplyProxyCredentials::SupplyProxyCredentials(const char* username,
const char* password)
: username_(username), password_(password) {
}
void SupplyProxyCredentials::OnWindowClose(HWND hwnd) { }
void SupplyProxyCredentials::OnWindowOpen(HWND hwnd) {
DialogProps props = {0};
::EnumChildWindows(hwnd, EnumChildren, reinterpret_cast<LPARAM>(&props));
DCHECK(::IsWindow(props.username_));
DCHECK(::IsWindow(props.password_));
// We can't use SetWindowText to set the username/password, so simulate
// keyboard input instead.
simulate_input::ForceSetForegroundWindow(hwnd);
CHECK(SetFocusToAccessibleWindow(props.username_));
simulate_input::SendStringA(username_.c_str());
Sleep(100);
simulate_input::SendCharA(VK_TAB, simulate_input::NONE);
Sleep(100);
simulate_input::SendStringA(password_.c_str());
Sleep(100);
simulate_input::SendCharA(VK_RETURN, simulate_input::NONE);
}
// static
BOOL SupplyProxyCredentials::EnumChildren(HWND hwnd, LPARAM param) {
if (!::IsWindowVisible(hwnd))
return TRUE; // Ignore but continue to enumerate.
DialogProps* props = reinterpret_cast<DialogProps*>(param);
char class_name[MAX_PATH] = {0};
::GetClassNameA(hwnd, class_name, arraysize(class_name));
if (lstrcmpiA(class_name, "Edit") == 0) {
if (props->username_ == NULL || props->username_ == hwnd) {
props->username_ = hwnd;
} else if (props->password_ == NULL) {
props->password_ = hwnd;
}
} else {
EnumChildWindows(hwnd, EnumChildren, param);
}
return TRUE;
}
FakeExternalTab::FakeExternalTab() {
user_data_dir_ = chrome_frame_test::GetProfilePathForIE();
if (base::PathExists(user_data_dir_)) {
VLOG(1) << __FUNCTION__ << " deleting IE Profile user data directory "
<< user_data_dir_.value();
bool deleted = base::DeleteFile(user_data_dir_, true);
LOG_IF(ERROR, !deleted) << "Failed to delete user data directory directory "
<< user_data_dir_.value();
}
PathService::Get(chrome::DIR_USER_DATA, &overridden_user_dir_);
PathService::Override(chrome::DIR_USER_DATA, user_data_dir_);
}
FakeExternalTab::~FakeExternalTab() {
if (!overridden_user_dir_.empty()) {
PathService::Override(chrome::DIR_USER_DATA, overridden_user_dir_);
}
}
void FakeExternalTab::Initialize() {
DCHECK(g_browser_process == NULL);
TestTimeouts::Initialize();
// Load Chrome.dll as our resource dll.
base::FilePath dll;
PathService::Get(base::DIR_MODULE, &dll);
dll = dll.Append(chrome::kBrowserResourcesDll);
HMODULE res_mod = ::LoadLibraryExW(dll.value().c_str(),
NULL, LOAD_LIBRARY_AS_DATAFILE | LOAD_LIBRARY_AS_IMAGE_RESOURCE);
DCHECK(res_mod);
_AtlBaseModule.SetResourceInstance(res_mod);
// Point the ResourceBundle at chrome.dll.
ui::SetResourcesDataDLL(_AtlBaseModule.GetResourceInstance());
ResourceBundle::InitSharedInstanceWithLocale("en-US", NULL);
CommandLine* cmd = CommandLine::ForCurrentProcess();
cmd->AppendSwitch(switches::kDisableWebResources);
cmd->AppendSwitch(switches::kSingleProcess);
base::FilePath local_state_path;
CHECK(PathService::Get(chrome::FILE_LOCAL_STATE, &local_state_path));
scoped_refptr<base::SequencedTaskRunner> local_state_task_runner =
JsonPrefStore::GetTaskRunnerForFile(local_state_path,
BrowserThread::GetBlockingPool());
browser_process_.reset(new FakeBrowserProcessImpl(local_state_task_runner,
*cmd));
// BrowserProcessImpl's constructor should set g_browser_process.
DCHECK(g_browser_process);
g_browser_process->SetApplicationLocale("en-US");
content::RenderProcessHost::SetRunRendererInProcess(true);
// TODO(joi): Registration should be done up front via browser_prefs.cc
scoped_refptr<PrefRegistrySimple> registry = static_cast<PrefRegistrySimple*>(
browser_process_->local_state()->DeprecatedGetPrefRegistry());
if (!browser_process_->local_state()->FindPreference(
prefs::kMetricsReportingEnabled)) {
registry->RegisterBooleanPref(prefs::kMetricsReportingEnabled, false);
}
}
void FakeExternalTab::InitializePostThreadsCreated() {
#if defined(USE_AURA)
gfx::Screen::SetScreenInstance(gfx::SCREEN_TYPE_NATIVE,
views::CreateDesktopScreen());
#endif
base::FilePath profile_path(
profiles::GetDefaultProfileDir(user_data()));
Profile* profile =
g_browser_process->profile_manager()->GetProfile(profile_path);
}
void FakeExternalTab::Shutdown() {
browser_process_.reset();
g_browser_process = NULL;
ResourceBundle::CleanupSharedInstance();
}
FakeBrowserProcessImpl* FakeExternalTab::browser_process() const {
return browser_process_.get();
}
CFUrlRequestUnittestRunner::CFUrlRequestUnittestRunner(int argc, char** argv)
: NetTestSuite(argc, argv, false),
chrome_frame_html_("/chrome_frame", kChromeFrameHtml),
registrar_(chrome_frame_test::GetTestBedType()),
test_result_(0),
launch_browser_(
!CommandLine::ForCurrentProcess()->HasSwitch(kManualBrowserLaunch)),
prompt_after_setup_(
CommandLine::ForCurrentProcess()->HasSwitch(kPromptAfterSetup)),
tests_ran_(false) {
}
CFUrlRequestUnittestRunner::~CFUrlRequestUnittestRunner() {
}
void CFUrlRequestUnittestRunner::StartChromeFrameInHostBrowser() {
if (!launch_browser_)
return;
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_->Initialize();
ie_configurator_->ApplySettings();
}
test_http_server_.reset(new test_server::SimpleWebServer("127.0.0.1",
kTestServerPort));
test_http_server_->AddResponse(&chrome_frame_html_);
std::wstring url(base::StringPrintf(L"http://localhost:%i/chrome_frame",
kTestServerPort));
// Launch IE. This launches IE correctly on Vista too.
base::win::ScopedHandle ie_process(chrome_frame_test::LaunchIE(url));
EXPECT_TRUE(ie_process.IsValid());
// NOTE: If you're running IE8 and CF is not being loaded, you need to
// disable IE8's prebinding until CF properly handles that situation.
//
// HKCU\Software\Microsoft\Internet Explorer\Main
// Value name: EnablePreBinding (REG_DWORD)
// Value: 0
}
void CFUrlRequestUnittestRunner::ShutDownHostBrowser() {
if (launch_browser_)
chrome_frame_test::CloseAllIEWindows();
}
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(0);
}
// Override virtual void Initialize to not call icu initialize.
void CFUrlRequestUnittestRunner::Initialize() {
DCHECK(::GetCurrentThreadId() == test_thread_id_);
// Start by replicating some of the steps that would otherwise be
// done by TestSuite::Initialize. We can't call the base class
// directly because it will attempt to initialize some things such as
// ICU that have already been initialized for this process.
CFUrlRequestUnittestRunner::InitializeLogging();
SuppressErrorDialogs();
base::debug::SetSuppressDebugUI(true);
logging::SetLogAssertHandler(UnitTestAssertHandler);
// Next, do some initialization for NetTestSuite.
NetTestSuite::InitializeTestThreadNoNetworkChangeNotifier();
// Finally, override the host used by the HTTP tests. See
// http://crbug.com/114369 .
OverrideHttpHost();
}
void CFUrlRequestUnittestRunner::Shutdown() {
DCHECK(::GetCurrentThreadId() == test_thread_id_);
NetTestSuite::Shutdown();
OleUninitialize();
}
void CFUrlRequestUnittestRunner::OnInitialTabLoaded() {
test_http_server_.reset();
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&CFUrlRequestUnittestRunner::CancelInitializationTimeout,
base::Unretained(this)));
StartTests();
}
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 "
"has completed.";
}
}
void CFUrlRequestUnittestRunner::StartTests() {
if (prompt_after_setup_)
MessageBoxA(NULL, "click ok to run", "", MB_OK);
DCHECK_EQ(test_thread_.IsValid(), false);
StopFileLogger(false);
test_thread_.Set(::CreateThread(NULL, 0, RunAllUnittests, this, 0,
&test_thread_id_));
DCHECK(test_thread_.IsValid());
}
// static
DWORD CFUrlRequestUnittestRunner::RunAllUnittests(void* param) {
base::PlatformThread::SetName("CFUrlRequestUnittestRunner");
CFUrlRequestUnittestRunner* me =
reinterpret_cast<CFUrlRequestUnittestRunner*>(param);
me->test_result_ = me->Run();
me->tests_ran_ = true;
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&CFUrlRequestUnittestRunner::TakeDownBrowser,
base::Unretained(me)));
return 0;
}
void CFUrlRequestUnittestRunner::TakeDownBrowser() {
if (prompt_after_setup_)
MessageBoxA(NULL, "click ok to exit", "", MB_OK);
// Start capturing logs from npchrome_frame and the in-process Chrome to help
// diagnose failures in IE shutdown. This will be Stopped in either
// OnIEShutdownFailure or OnProviderDestroyed.
StartFileLogger();
// AddRef to ensure that IE going away does not trigger the Chrome shutdown
// process.
// IE shutting down will, however, trigger the automation channel to shut
// down, at which time we will exit the process (see OnProviderDestroyed).
g_browser_process->AddRefModule();
ShutDownHostBrowser();
// In case IE is somehow hung, make sure we don't sit around until a try-bot
// kills us. OnIEShutdownFailure will log and exit with an error.
BrowserThread::PostDelayedTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&CFUrlRequestUnittestRunner::OnIEShutdownFailure,
base::Unretained(this)),
TestTimeouts::action_max_timeout());
}
void CFUrlRequestUnittestRunner::InitializeLogging() {
base::FilePath exe;
PathService::Get(base::FILE_EXE, &exe);
base::FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
logging::LoggingSettings settings;
settings.logging_dest = logging::LOG_TO_ALL;
settings.log_file = log_filename.value().c_str();
settings.delete_old = logging::DELETE_OLD_LOG_FILE;
logging::InitLogging(settings);
// We want process and thread IDs because we may have multiple processes.
// Note: temporarily enabled timestamps in an effort to catch bug 6361.
logging::SetLogItems(true, true, true, true);
}
void CFUrlRequestUnittestRunner::CancelInitializationTimeout() {
timeout_closure_.Cancel();
}
void CFUrlRequestUnittestRunner::StartInitializationTimeout() {
timeout_closure_.Reset(
base::Bind(&CFUrlRequestUnittestRunner::OnInitializationTimeout,
base::Unretained(this)));
base::MessageLoop::current()->PostDelayedTask(
FROM_HERE,
timeout_closure_.callback(),
TestTimeouts::action_max_timeout());
}
void CFUrlRequestUnittestRunner::OnInitializationTimeout() {
LOG(ERROR) << "Failed to start Chrome Frame in the host browser.";
base::FilePath snapshot;
if (ui_test_utils::SaveScreenSnapshotToDesktop(&snapshot))
LOG(ERROR) << "Screen snapshot saved to " << snapshot.value();
StopFileLogger(true);
if (launch_browser_)
chrome_frame_test::CloseAllIEWindows();
if (ie_configurator_.get() != NULL)
ie_configurator_->RevertSettings();
if (crash_service_)
base::KillProcess(crash_service_, 0, false);
::ExitProcess(1);
}
void CFUrlRequestUnittestRunner::OverrideHttpHost() {
override_http_host_.reset(
new net::ScopedCustomUrlRequestTestHttpHost(
chrome_frame_test::GetLocalIPv4Address()));
}
void CFUrlRequestUnittestRunner::PreEarlyInitialization() {
testing::InitGoogleTest(&g_argc, g_argv);
FilterDisabledTests();
StartFileLogger();
}
int CFUrlRequestUnittestRunner::PreCreateThreads() {
fake_chrome_.reset(new FakeExternalTab());
fake_chrome_->Initialize();
fake_chrome_->browser_process()->PreCreateThreads();
ProcessSingleton::NotificationCallback callback(
base::Bind(
&CFUrlRequestUnittestRunner::ProcessSingletonNotificationCallback,
base::Unretained(this)));
process_singleton_.reset(new ProcessSingleton(fake_chrome_->user_data(),
callback));
return 0;
}
bool CFUrlRequestUnittestRunner::ProcessSingletonNotificationCallback(
const CommandLine& command_line, const base::FilePath& current_directory) {
std::string channel_id = command_line.GetSwitchValueASCII(
switches::kAutomationClientChannelID);
EXPECT_FALSE(channel_id.empty());
Profile* profile = g_browser_process->profile_manager()->GetLastUsedProfile(
fake_chrome_->user_data());
AutomationProviderList* list = g_browser_process->GetAutomationProviderList();
DCHECK(list);
list->AddProvider(
TestAutomationProvider::NewAutomationProvider(profile, channel_id, this));
return true;
}
void CFUrlRequestUnittestRunner::PreMainMessageLoopRun() {
fake_chrome_->InitializePostThreadsCreated();
// Call Create directly instead of NotifyOtherProcessOrCreate as failure is
// prefered to notifying another process here.
if (!process_singleton_->Create()) {
LOG(FATAL) << "Failed to start up ProcessSingleton. Is another test "
<< "executable or Chrome Frame running?";
if (crash_service_)
base::KillProcess(crash_service_, 0, false);
::ExitProcess(1);
}
}
bool CFUrlRequestUnittestRunner::MainMessageLoopRun(int* result_code) {
DCHECK(base::MessageLoop::current());
DCHECK(base::MessageLoop::current()->type() == base::MessageLoop::TYPE_UI);
// We need to allow IO on the main thread for these tests.
base::ThreadRestrictions::SetIOAllowed(true);
StartChromeFrameInHostBrowser();
StartInitializationTimeout();
return false;
}
void CFUrlRequestUnittestRunner::PostMainMessageLoopRun() {
process_singleton_->Cleanup();
fake_chrome_->browser_process()->StartTearDown();
// Must do this separately as the mock profile_manager_ is not the
// same member as BrowserProcessImpl::StartTearDown resets.
fake_chrome_->browser_process()->DestroyProfileManager();
if (crash_service_)
base::KillProcess(crash_service_, 0, false);
base::KillProcesses(chrome_frame_test::kIEImageName, 0, NULL);
base::KillProcesses(chrome_frame_test::kIEBrokerImageName, 0, NULL);
}
void CFUrlRequestUnittestRunner::PostDestroyThreads() {
process_singleton_.reset();
fake_chrome_->browser_process()->PostDestroyThreads();
fake_chrome_->Shutdown();
fake_chrome_.reset();
#ifndef NDEBUG
// Avoid CRT cleanup in debug test runs to ensure that webkit ASSERTs which
// check if globals are created and destroyed on the same thread don't fire.
// Webkit global objects are created on the inproc renderer thread.
::ExitProcess(test_result());
#endif
}
void CFUrlRequestUnittestRunner::StartFileLogger() {
if (file_util::CreateTemporaryFile(&log_file_)) {
file_logger_.reset(new logging_win::FileLogger());
file_logger_->Initialize();
file_logger_->StartLogging(log_file_);
} else {
LOG(ERROR) << "Failed to create an ETW log file";
}
}
void CFUrlRequestUnittestRunner::StopFileLogger(bool print) {
if (file_logger_.get() != NULL && file_logger_->is_logging()) {
file_logger_->StopLogging();
if (print) {
// Flushing stdout should prevent unrelated output from being interleaved
// with the log file output.
std::cout.flush();
// Dump the log to stderr.
logging_win::PrintLogFile(log_file_, &std::cerr);
std::cerr.flush();
}
}
if (!log_file_.empty() && !base::DeleteFile(log_file_, false))
LOG(ERROR) << "Failed to delete log file " << log_file_.value();
log_file_.clear();
file_logger_.reset();
}
const char* IEVersionToString(IEVersion version) {
switch (version) {
case IE_6:
return "IE6";
case IE_7:
return "IE7";
case IE_8:
return "IE8";
case IE_9:
return "IE9";
case IE_10:
return "IE10";
case IE_UNSUPPORTED:
return "Unknown IE Version";
case NON_IE:
return "Could not find IE";
default:
return "Error.";
}
}
content::BrowserMainParts* FakeContentBrowserClient::CreateBrowserMainParts(
const content::MainFunctionParams& parameters) {
// Normally this would happen during browser startup, but for tests
// we need to trigger creation of Profile-related services.
ChromeBrowserMainExtraPartsProfiles::
EnsureBrowserContextKeyedServiceFactoriesBuilt();
// We never delete this, as the content module takes ownership.
//
// We must not construct this earlier, or we will have out-of-order
// AtExitManager creation/destruction.
g_test_suite = new CFUrlRequestUnittestRunner(g_argc, g_argv);
g_test_suite->set_crash_service(chrome_frame_test::StartCrashService());
return g_test_suite;
}
// We must provide a few functions content/ expects to link with. They
// are never called for this executable.
int PluginMain(const content::MainFunctionParams& parameters) {
NOTREACHED();
return 0;
}
int PpapiBrokerMain(const content::MainFunctionParams& parameters) {
return PluginMain(parameters);
}
int PpapiPluginMain(const content::MainFunctionParams& parameters) {
return PluginMain(parameters);
}
int WorkerMain(const content::MainFunctionParams& parameters) {
return PluginMain(parameters);
}
int main(int argc, char** argv) {
ScopedChromeFrameRegistrar::RegisterAndExitProcessIfDirected();
g_argc = argc;
g_argv = argv;
google_breakpad::scoped_ptr<google_breakpad::ExceptionHandler> breakpad(
InitializeCrashReporting(HEADLESS));
// Display the IE version we run with. This must be done after
// CFUrlRequestUnittestRunner is constructed since that initializes logging.
IEVersion ie_version = chrome_frame_test::GetInstalledIEVersion();
LOG(INFO) << "Running CF net tests with IE version: "
<< IEVersionToString(ie_version);
// See url_request_unittest.cc for these credentials.
SupplyProxyCredentials credentials("user", "secret");
WindowWatchdog watchdog;
watchdog.AddObserver(&credentials, "Windows Security", "");
sandbox::SandboxInterfaceInfo sandbox_info = {0};
content::InitializeSandboxInfo(&sandbox_info);
FakeMainDelegate delegate;
content::ContentMain(
reinterpret_cast<HINSTANCE>(GetModuleHandle(NULL)),
&sandbox_info,
&delegate);
// Note: In debug builds, we ExitProcess during PostDestroyThreads.
return g_test_suite->test_result();
}