diff options
Diffstat (limited to 'chrome_frame/test/chrome_frame_unittests.cc')
-rw-r--r-- | chrome_frame/test/chrome_frame_unittests.cc | 1510 |
1 files changed, 1510 insertions, 0 deletions
diff --git a/chrome_frame/test/chrome_frame_unittests.cc b/chrome_frame/test/chrome_frame_unittests.cc new file mode 100644 index 0000000..20826b1 --- /dev/null +++ b/chrome_frame/test/chrome_frame_unittests.cc @@ -0,0 +1,1510 @@ +// Copyright (c) 2006-2008 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 <windows.h> +#include <stdarg.h> + +// IShellWindows includes. Unfortunately we can't keep these in +// alphabetic order since exdisp will bark if some interfaces aren't fully +// defined. +#include <mshtml.h> +#include <exdisp.h> + +#include "base/basictypes.h" +#include "base/file_version_info.h" +#include "base/file_util.h" +#include "base/scoped_bstr_win.h" +#include "base/scoped_comptr_win.h" +#include "base/scoped_variant_win.h" +#include "base/sys_info.h" +#include "gmock/gmock.h" +#include "net/url_request/url_request_unittest.h" +#include "chrome_frame/test/chrome_frame_unittests.h" +#include "chrome_frame/chrome_frame_automation.h" +#include "chrome_frame/chrome_frame_delegate.h" +#include "chrome_frame/test/chrome_frame_test_utils.h" +#include "chrome_frame/test/helper_gmock.h" +#include "chrome_frame/test_utils.h" +#include "chrome_frame/utils.h" +#include "chrome_frame/vectored_handler-impl.h" +#include "chrome/installer/util/install_util.h" +#include "chrome/installer/util/helper.h" + +const wchar_t kDocRoot[] = L"chrome_frame\\test\\data"; +const int kLongWaitTimeout = 60 * 1000; +const int kShortWaitTimeout = 25 * 1000; + +_ATL_FUNC_INFO WebBrowserEventSink::kNavigateErrorInfo = {
+ CC_STDCALL, VT_EMPTY, 5, { + VT_DISPATCH, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_BOOL | VT_BYREF, + } +}; + +_ATL_FUNC_INFO WebBrowserEventSink::kNavigateComplete2Info = { + CC_STDCALL, VT_EMPTY, 2, { + VT_DISPATCH, + VT_VARIANT | VT_BYREF + } +}; + +_ATL_FUNC_INFO WebBrowserEventSink::kBeforeNavigate2Info = {
+ CC_STDCALL, VT_EMPTY, 7, { + VT_DISPATCH, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_VARIANT | VT_BYREF, + VT_BOOL | VT_BYREF + } +}; + + + +void ChromeFrameTestWithWebServer::SetUp() { + server_.SetUp(); + results_dir_ = server_.GetDataDir(); + file_util::AppendToPath(&results_dir_, L"dump"); +} + +void ChromeFrameTestWithWebServer::TearDown() { + CloseBrowser(); + + // Web browsers tend to relaunch themselves in other processes, meaning the + // KillProcess stuff above might not have actually cleaned up all our browser + // instances, so make really sure browsers are dead. + base::KillProcesses(chrome_frame_test::kIEImageName, 0, NULL); + base::KillProcesses(chrome_frame_test::kIEBrokerImageName, 0, NULL); + base::KillProcesses(chrome_frame_test::kFirefoxImageName, 0, NULL); + base::KillProcesses(chrome_frame_test::kSafariImageName, 0, NULL); + base::KillProcesses(chrome_frame_test::kChromeImageName, 0, NULL); + + server_.TearDown(); +} + +bool ChromeFrameTestWithWebServer::LaunchBrowser(BrowserKind browser, + const wchar_t* page) { + std::wstring url = UTF8ToWide(server_.Resolve(page).spec()); + browser_ = browser; + if (browser == IE) { + browser_handle_.Set(chrome_frame_test::LaunchIE(url)); + } else if (browser == FIREFOX) { + browser_handle_.Set(chrome_frame_test::LaunchFirefox(url)); + } else if (browser == OPERA) { + browser_handle_.Set(chrome_frame_test::LaunchOpera(url)); + } else if (browser == SAFARI) { + browser_handle_.Set(chrome_frame_test::LaunchSafari(url)); + } else if (browser == CHROME) { + browser_handle_.Set(chrome_frame_test::LaunchChrome(url)); + } else { + NOTREACHED(); + } + + return browser_handle_.IsValid(); +} + +void ChromeFrameTestWithWebServer::CloseBrowser() { + if (!browser_handle_.IsValid()) + return; + + int attempts = 0; + if (browser_ == IE) { + attempts = chrome_frame_test::CloseAllIEWindows(); + } else { + attempts = chrome_frame_test::CloseVisibleWindowsOnAllThreads( + browser_handle_); + } + + if (attempts > 0) { + DWORD wait = ::WaitForSingleObject(browser_handle_, 20000); + if (wait == WAIT_OBJECT_0) { + browser_handle_.Close(); + } else { + DLOG(ERROR) << "WaitForSingleObject returned " << wait; + } + } else { + DLOG(ERROR) << "No attempts to close browser windows"; + } + + if (browser_handle_.IsValid()) { + DWORD exit_code = 0; + if (!::GetExitCodeProcess(browser_handle_, &exit_code) || + exit_code == STILL_ACTIVE) { + DLOG(ERROR) << L"Forcefully killing browser process. Exit:" << exit_code; + base::KillProcess(browser_handle_, 0, true); + } + browser_handle_.Close(); + } +} + +bool ChromeFrameTestWithWebServer::BringBrowserToTop() { + return chrome_frame_test::EnsureProcessInForeground(GetProcessId( + browser_handle_)); +} + +bool ChromeFrameTestWithWebServer::WaitForTestToComplete(int milliseconds) { + return server_.WaitToFinish(milliseconds); +} + +bool ChromeFrameTestWithWebServer::WaitForOnLoad(int milliseconds) { + DWORD start = ::GetTickCount(); + std::string data; + while (!ReadResultFile(L"OnLoadEvent", &data) || data.length() == 0) { + DWORD now = ::GetTickCount(); + if (start > now) { + // Very simple check for overflow. In that case we just restart the + // wait. + start = now; + } else if (static_cast<int>(now - start) > milliseconds) { + break; + } + Sleep(100); + } + + return data.compare("loaded") == 0; +} + +bool ChromeFrameTestWithWebServer::ReadResultFile(const std::wstring& file_name, + std::string* data) { + std::wstring full_path = results_dir_; + file_util::AppendToPath(&full_path, file_name); + return file_util::ReadFileToString(full_path, data); +} + +bool ChromeFrameTestWithWebServer::CheckResultFile( + const std::wstring& file_name, const std::string& expected_result) { + std::string data; + bool ret = ReadResultFile(file_name, &data); + if (ret) + ret = (data == expected_result); + + if (!ret) { + DLOG(ERROR) << "Error text: " << (data.empty() ? "<empty>" : data.c_str()); + } + + return ret; +} + +void ChromeFrameTestWithWebServer::SimpleBrowserTest(BrowserKind browser, + const wchar_t* page, const wchar_t* result_file_to_check) { + EXPECT_TRUE(LaunchBrowser(browser, page)); + ASSERT_TRUE(WaitForTestToComplete(kLongWaitTimeout)); + ASSERT_TRUE(CheckResultFile(result_file_to_check, "OK")); +} + +void ChromeFrameTestWithWebServer::OptionalBrowserTest(BrowserKind browser, + const wchar_t* page, const wchar_t* result_file_to_check) { + if (!LaunchBrowser(browser, page)) { + DLOG(ERROR) << "Failed to launch browser " << ToString(browser); + } else { + ASSERT_TRUE(WaitForTestToComplete(kLongWaitTimeout)); + ASSERT_TRUE(CheckResultFile(result_file_to_check, "OK")); + } +} + +void ChromeFrameTestWithWebServer::VersionTest(BrowserKind browser, + const wchar_t* page, const wchar_t* result_file_to_check) { + std::wstring plugin_path; + PathService::Get(base::DIR_MODULE, &plugin_path); + file_util::AppendToPath(&plugin_path, L"servers/npchrome_tab.dll"); + + static FileVersionInfo* version_info = + FileVersionInfo::CreateFileVersionInfo(plugin_path); + + std::wstring version; + if (version_info) + version = version_info->product_version(); + + // If we can't find the npchrome_tab.dll in the src tree, we turn to + // the directory where chrome is installed. + if (!version_info) { + installer::Version* ver_system = InstallUtil::GetChromeVersion(true); + installer::Version* ver_user = InstallUtil::GetChromeVersion(false); + ASSERT_TRUE(ver_system || ver_user); + + bool system_install = ver_system ? true : false; + std::wstring npchrome_path(installer::GetChromeInstallPath(system_install)); + file_util::AppendToPath(&npchrome_path, + ver_system ? ver_system->GetString() : ver_user->GetString()); + file_util::AppendToPath(&npchrome_path, L"npchrome_tab.dll"); + version_info = FileVersionInfo::CreateFileVersionInfo(npchrome_path); + if (version_info) + version = version_info->product_version(); + } + + EXPECT_TRUE(version_info); + EXPECT_FALSE(version.empty()); + EXPECT_TRUE(LaunchBrowser(browser, page)); + ASSERT_TRUE(WaitForTestToComplete(kLongWaitTimeout)); + ASSERT_TRUE(CheckResultFile(result_file_to_check, WideToUTF8(version))); +} + +const wchar_t kPostMessageBasicPage[] = L"files/postmessage_basic_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_PostMessageBasic) { + SimpleBrowserTest(IE, kPostMessageBasicPage, L"PostMessage"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_PostMessageBasic) { + SimpleBrowserTest(FIREFOX, kPostMessageBasicPage, L"PostMessage"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_PostMessageBasic) { + OptionalBrowserTest(OPERA, kPostMessageBasicPage, L"PostMessage"); +} + +TEST_F(ChromeFrameTestWithWebServer, FullTabIE_MIMEFilterBasic) { + const wchar_t kMIMEFilterBasicPage[] = + L"files/chrome_frame_mime_filter_test.html"; + + SimpleBrowserTest(IE, kMIMEFilterBasicPage, L"MIMEFilter"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_Resize) { + SimpleBrowserTest(IE, L"files/chrome_frame_resize.html", L"Resize"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_Resize) { + SimpleBrowserTest(FIREFOX, L"files/chrome_frame_resize.html", L"Resize"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_Resize) { + OptionalBrowserTest(OPERA, L"files/chrome_frame_resize.html", L"Resize"); +} + +const wchar_t kNavigateURLAbsolutePage[] = + L"files/navigateurl_absolute_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_NavigateURLAbsolute) { + SimpleBrowserTest(IE, kNavigateURLAbsolutePage, L"NavigateURL"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_NavigateURLAbsolute) { + SimpleBrowserTest(FIREFOX, kNavigateURLAbsolutePage, L"NavigateURL"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_NavigateURLAbsolute) { + OptionalBrowserTest(OPERA, kNavigateURLAbsolutePage, L"NavigateURL"); +} + +const wchar_t kNavigateURLRelativePage[] = + L"files/navigateurl_relative_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_NavigateURLRelative) { + SimpleBrowserTest(IE, kNavigateURLRelativePage, L"NavigateURL"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_NavigateURLRelative) { + SimpleBrowserTest(FIREFOX, kNavigateURLRelativePage, L"NavigateURL"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_NavigateURLRelative) { + OptionalBrowserTest(OPERA, kNavigateURLRelativePage, L"NavigateURL"); +} + +const wchar_t kNavigateSimpleObjectFocus[] = L"files/simple_object_focus.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_ObjectFocus) { + SimpleBrowserTest(FIREFOX, kNavigateSimpleObjectFocus, L"ObjectFocus"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_ObjectFocus) { + SimpleBrowserTest(IE, kNavigateSimpleObjectFocus, L"ObjectFocus"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_ObjectFocus) { + if (!LaunchBrowser(OPERA, kNavigateSimpleObjectFocus)) { + DLOG(ERROR) << "Failed to launch browser " << ToString(OPERA); + } else { + ASSERT_TRUE(WaitForOnLoad(kLongWaitTimeout)); + BringBrowserToTop(); + // Tab through a couple of times. Once should be enough in theory + // but in practice activating the browser can take a few milliseconds more. + bool ok; + for (int i = 0; + i < 5 && (ok = CheckResultFile(L"ObjectFocus", "OK")) == false; + ++i) { + Sleep(300); + chrome_frame_test::SendMnemonic(VK_TAB, false, false, false); + } + ASSERT_TRUE(ok); + } +} + +const wchar_t kiframeBasicPage[] = L"files/iframe_basic_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_iframeBasic) { + SimpleBrowserTest(IE, kiframeBasicPage, L"PostMessage"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_iframeBasic) { + SimpleBrowserTest(FIREFOX, kiframeBasicPage, L"PostMessage"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_iframeBasic) { + OptionalBrowserTest(OPERA, kiframeBasicPage, L"PostMessage"); +} + +const wchar_t kSrcPropertyTestPage[] = L"files/src_property_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_SrcProperty) { + SimpleBrowserTest(IE, kSrcPropertyTestPage, L"SrcProperty"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_SrcProperty) { + SimpleBrowserTest(FIREFOX, kSrcPropertyTestPage, L"SrcProperty"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_SrcProperty) { + OptionalBrowserTest(OPERA, kSrcPropertyTestPage, L"SrcProperty"); +} + +const wchar_t kCFInstanceBasicTestPage[] = L"files/CFInstance_basic_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstanceBasic) { + SimpleBrowserTest(IE, kCFInstanceBasicTestPage, L"CFInstanceBasic"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceBasic) { + SimpleBrowserTest(FIREFOX, kCFInstanceBasicTestPage, L"CFInstanceBasic"); +} + +const wchar_t kCFISingletonPage[] = L"files/CFInstance_singleton_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstanceSingleton) { + SimpleBrowserTest(IE, kCFISingletonPage, L"CFInstanceSingleton"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceSingleton) { + SimpleBrowserTest(FIREFOX, kCFISingletonPage, L"CFInstanceSingleton"); +} + +const wchar_t kCFIDelayPage[] = L"files/CFInstance_delay_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, DISABLED_WidgetModeIE_CFInstanceDelay) { + SimpleBrowserTest(IE, kCFIDelayPage, L"CFInstanceDelay"); +} + +TEST_F(ChromeFrameTestWithWebServer, DISABLED_WidgetModeFF_CFInstanceDelay) { + SimpleBrowserTest(FIREFOX, kCFIDelayPage, L"CFInstanceDelay"); +} + +TEST_F(ChromeFrameTestWithWebServer, DISABLED_WidgetModeOpera_CFInstanceDelay) { + OptionalBrowserTest(OPERA, kCFIDelayPage, L"CFInstanceDelay"); +} + +const wchar_t kCFIFallbackPage[] = L"files/CFInstance_fallback_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstanceFallback) { + SimpleBrowserTest(IE, kCFIFallbackPage, L"CFInstanceFallback"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceFallback) { + SimpleBrowserTest(FIREFOX, kCFIFallbackPage, L"CFInstanceFallback"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_CFInstanceFallback) { + OptionalBrowserTest(OPERA, kCFIFallbackPage, L"CFInstanceFallback"); +} + +const wchar_t kCFINoSrcPage[] = L"files/CFInstance_no_src_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstanceNoSrc) { + SimpleBrowserTest(IE, kCFINoSrcPage, L"CFInstanceNoSrc"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceNoSrc) { + SimpleBrowserTest(FIREFOX, kCFINoSrcPage, L"CFInstanceNoSrc"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_CFInstanceNoSrc) { + OptionalBrowserTest(OPERA, kCFINoSrcPage, L"CFInstanceNoSrc"); +} + +const wchar_t kCFIIfrOnLoadPage[] = L"files/CFInstance_iframe_onload_host.html"; + +// disabled since it's unlikely that we care about this case +TEST_F(ChromeFrameTestWithWebServer, DISABLED_WidgetModeIE_CFInstanceIfrOnLoad) { + SimpleBrowserTest(IE, kCFIIfrOnLoadPage, L"CFInstanceIfrOnLoad"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceIfrOnLoad) { + SimpleBrowserTest(FIREFOX, kCFIIfrOnLoadPage, L"CFInstanceIfrOnLoad"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_CFInstanceIfrOnLoad) { + OptionalBrowserTest(OPERA, kCFIIfrOnLoadPage, L"CFInstanceIfrOnLoad"); +} + +const wchar_t kCFIZeroSizePage[] = L"files/CFInstance_zero_size_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstanceZeroSize) { + SimpleBrowserTest(IE, kCFIZeroSizePage, L"CFInstanceZeroSize"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceZeroSize) { + SimpleBrowserTest(FIREFOX, kCFIZeroSizePage, L"CFInstanceZeroSize"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_CFInstanceZeroSize) { + OptionalBrowserTest(OPERA, kCFIZeroSizePage, L"CFInstanceZeroSize"); +} + +const wchar_t kCFIIfrPostPage[] = L"files/CFInstance_iframe_post_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstanceIfrPost) { + SimpleBrowserTest(IE, kCFIIfrPostPage, L"CFInstanceIfrPost"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceIfrPost) { + SimpleBrowserTest(FIREFOX, kCFIIfrPostPage, L"CFInstanceIfrPost"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeChrome_CFInstanceIfrPost) { + OptionalBrowserTest(CHROME, kCFIIfrPostPage, L"CFInstanceIfrPost"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeSafari_CFInstanceIfrPost) { + OptionalBrowserTest(SAFARI, kCFIIfrPostPage, L"CFInstanceIfrPost"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_CFInstanceIfrPost) { + OptionalBrowserTest(OPERA, kCFIIfrPostPage, L"CFInstanceIfrPost"); +} + +const wchar_t kCFIPostPage[] = L"files/CFInstance_post_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstancePost) { + SimpleBrowserTest(IE, kCFIPostPage, L"CFInstancePost"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstancePost) { + SimpleBrowserTest(FIREFOX, kCFIPostPage, L"CFInstancePost"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeChrome_CFInstancePost) { + OptionalBrowserTest(CHROME, kCFIPostPage, L"CFInstancePost"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeSafari_CFInstancePost) { + OptionalBrowserTest(SAFARI, kCFIPostPage, L"CFInstancePost"); +} + +TEST_F(ChromeFrameTestWithWebServer, DISABLED_WidgetModeOpera_CFInstancePost) { + OptionalBrowserTest(OPERA, kCFIPostPage, L"CFInstancePost"); +} + +const wchar_t kCFIRPCPage[] = L"files/CFInstance_rpc_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstanceRPC) { + SimpleBrowserTest(IE, kCFIRPCPage, L"CFInstanceRPC"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceRPC) { + SimpleBrowserTest(FIREFOX, kCFIRPCPage, L"CFInstanceRPC"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeChrome_CFInstanceRPC) { + OptionalBrowserTest(CHROME, kCFIRPCPage, L"CFInstanceRPC"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeSafari_CFInstanceRPC) { + OptionalBrowserTest(SAFARI, kCFIRPCPage, L"CFInstanceRPC"); +} + +TEST_F(ChromeFrameTestWithWebServer, DISABLED_WidgetModeOpera_CFInstanceRPC) { + OptionalBrowserTest(OPERA, kCFIRPCPage, L"CFInstanceRPC"); +} + +const wchar_t kCFIRPCInternalPage[] = + L"files/CFInstance_rpc_internal_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstanceRPCInternal) { + SimpleBrowserTest(IE, kCFIRPCInternalPage, L"CFInstanceRPCInternal"); +} + +// Disabled: http://b/issue?id=2050201 +TEST_F(ChromeFrameTestWithWebServer, DISABLED_WidgetModeFF_CFInstanceRPCInternal) { + SimpleBrowserTest(FIREFOX, kCFIRPCInternalPage, L"CFInstanceRPCInternal"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeChrome_CFInstanceRPCInternal) { + OptionalBrowserTest(CHROME, kCFIRPCInternalPage, L"CFInstanceRPCInternal"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeSafari_CFInstanceRPCInternal) { + OptionalBrowserTest(SAFARI, kCFIRPCInternalPage, L"CFInstanceRPCInternal"); +} + +const wchar_t kCFIDefaultCtorPage[] = + L"files/CFInstance_default_ctor_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_CFInstanceDefaultCtor) { + SimpleBrowserTest(IE, kCFIDefaultCtorPage, L"CFInstanceDefaultCtor"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_CFInstanceDefaultCtor) { + SimpleBrowserTest(FIREFOX, kCFIDefaultCtorPage, L"CFInstanceDefaultCtor"); +} + +// Class that mocks external call from VectoredHandlerT for testing purposes. +class EMock : public VEHTraitsBase { + public: + static inline bool WriteDump(EXCEPTION_POINTERS* p) { + g_dump_made = true; + return true; + } + + static inline void* Register(PVECTORED_EXCEPTION_HANDLER func, + const void* module_start, + const void* module_end) { + VEHTraitsBase::SetModule(module_start, module_end); + // Return some arbitrary number, expecting to get the same on Unregister() + return reinterpret_cast<void*>(4); + } + + static inline ULONG Unregister(void* handle) { + EXPECT_EQ(handle, reinterpret_cast<void*>(4)); + return 1; + } + + static inline WORD RtlCaptureStackBackTrace(DWORD FramesToSkip, + DWORD FramesToCapture, void** BackTrace, DWORD* BackTraceHash) { + EXPECT_EQ(2, FramesToSkip); + EXPECT_LE(FramesToSkip + FramesToCapture, + VectoredHandlerBase::max_back_trace); + memcpy(BackTrace, g_stack, g_stack_entries * sizeof(BackTrace[0])); + return g_stack_entries; + } + + static inline EXCEPTION_REGISTRATION_RECORD* RtlpGetExceptionList() { + return g_seh_chain; + } + + // Test helpers + + // Create fake SEH chain of random filters - with and without our module. + static void SetHaveSEHFilter() { + SetSEHChain(reinterpret_cast<const char*>(g_module_start) - 0x1000, + reinterpret_cast<const char*>(g_module_start) + 0x1000, + reinterpret_cast<const char*>(g_module_end) + 0x7127, + NULL); + } + + static void SetNoSEHFilter() { + SetSEHChain(reinterpret_cast<const char*>(g_module_start) - 0x1000, + reinterpret_cast<const char*>(g_module_end) + 0x7127, + NULL); + } + + // Create fake stack - with and without our module. + static void SetOnStack() { + SetStack(reinterpret_cast<const char*>(g_module_start) - 0x11283, + reinterpret_cast<const char*>(g_module_start) - 0x278361, + reinterpret_cast<const char*>(g_module_start) + 0x9171, + reinterpret_cast<const char*>(g_module_end) + 1231, + NULL); + } + + static void SetNotOnStack() { + SetStack(reinterpret_cast<const char*>(g_module_start) - 0x11283, + reinterpret_cast<const char*>(g_module_start) - 0x278361, + reinterpret_cast<const char*>(g_module_end) + 1231, + NULL); + } + + // Populate stack array + static void SetStack(const void* p, ...) { + va_list vl; + va_start(vl, p); + g_stack_entries = 0; + for (; p; ++g_stack_entries) { + CHECK(g_stack_entries < arraysize(g_stack)); + g_stack[g_stack_entries] = p; + p = va_arg(vl, const void*); + } + } + + static void SetSEHChain(const void* p, ...) { + va_list vl; + va_start(vl, p); + int i = 0; + for (; p; ++i) { + CHECK(i + 1 < arraysize(g_seh_chain)); + g_seh_chain[i].Handler = const_cast<void*>(p); + g_seh_chain[i].Next = &g_seh_chain[i + 1]; + p = va_arg(vl, const void*); + } + + g_seh_chain[i].Next = EXCEPTION_CHAIN_END; + } + + static EXCEPTION_REGISTRATION_RECORD g_seh_chain[25]; + static const void* g_stack[VectoredHandlerBase::max_back_trace]; + static WORD g_stack_entries; + static bool g_dump_made; +}; + +EXCEPTION_REGISTRATION_RECORD EMock::g_seh_chain[25]; +const void* EMock::g_stack[VectoredHandlerBase::max_back_trace]; +WORD EMock::g_stack_entries; +bool EMock::g_dump_made; + +typedef VectoredHandlerT<EMock> VectoredHandlerMock; + +class ExPtrsHelper : public _EXCEPTION_POINTERS { + public: + ExPtrsHelper() { + ExceptionRecord = &er_; + ContextRecord = &ctx_; + ZeroMemory(&er_, sizeof(er_)); + ZeroMemory(&ctx_, sizeof(ctx_)); + } + + void Set(DWORD code, void* address, DWORD flags) { + er_.ExceptionCode = code; + er_.ExceptionAddress = address; + er_.ExceptionFlags = flags; + } + + EXCEPTION_RECORD er_; + CONTEXT ctx_; +}; + + +TEST(ChromeFrame, ExceptionReport) { + char* s = reinterpret_cast<char*>(0x30000000); + char* e = s + 0x10000; + void* handler = VectoredHandlerMock::Register(s, e); + char* our_code = s + 0x1111; + char* not_our_code = s - 0x5555; + char* not_our_code2 = e + 0x5555; + + ExPtrsHelper ex; + // Exception in our code, but we have SEH filter + ex.Set(STATUS_ACCESS_VIOLATION, our_code, 0); + EMock::SetHaveSEHFilter(); + EMock::SetOnStack(); + EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); + EXPECT_EQ(1, VectoredHandlerMock::g_exceptions_seen); + EXPECT_FALSE(EMock::g_dump_made); + + // RPC_E_DISCONNECTED (0x80010108) is "The object invoked has disconnected + // from its clients", shall not be caught since it's a warning only. + ex.Set(RPC_E_DISCONNECTED, our_code, 0); + EMock::SetHaveSEHFilter(); + EMock::SetOnStack(); + EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); + EXPECT_EQ(1, VectoredHandlerMock::g_exceptions_seen); + EXPECT_FALSE(EMock::g_dump_made); + + + // Exception, not in our code, we do not have SEH and we are not in stack. + ex.Set(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code, 0); + EMock::SetNoSEHFilter(); + EMock::SetNotOnStack(); + EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); + EXPECT_EQ(2, VectoredHandlerMock::g_exceptions_seen); + EXPECT_FALSE(EMock::g_dump_made); + + // Exception, not in our code, no SEH, but we are on the stack. + ex.Set(STATUS_INTEGER_DIVIDE_BY_ZERO, not_our_code2, 0); + EMock::SetNoSEHFilter(); + EMock::SetOnStack(); + EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); + EXPECT_EQ(3, VectoredHandlerMock::g_exceptions_seen); + EXPECT_TRUE(EMock::g_dump_made); + EMock::g_dump_made = false; + + + // Exception, in our code, no SEH, not on stack (assume FPO screwed us) + ex.Set(STATUS_INTEGER_DIVIDE_BY_ZERO, our_code, 0); + EMock::SetNoSEHFilter(); + EMock::SetNotOnStack(); + EXPECT_EQ(ExceptionContinueSearch, VectoredHandlerMock::VectoredHandler(&ex)); + EXPECT_EQ(4, VectoredHandlerMock::g_exceptions_seen); + EXPECT_TRUE(EMock::g_dump_made); + EMock::g_dump_made = false; + + VectoredHandlerMock::Unregister(); +} + +const wchar_t kInitializeHiddenPage[] = L"files/initialize_hidden.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_InitializeHidden) { + SimpleBrowserTest(IE, kInitializeHiddenPage, L"InitializeHidden"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_InitializeHidden) { + SimpleBrowserTest(FIREFOX, kInitializeHiddenPage, L"InitializeHidden"); +} + +// Disabled due to a problem with Opera. +// http://b/issue?id=1708275 +TEST_F(ChromeFrameTestWithWebServer, DISABLED_WidgetModeOpera_InitializeHidden) { + OptionalBrowserTest(OPERA, kInitializeHiddenPage, L"InitializeHidden"); +} + +const wchar_t kInHeadPage[] = L"files/in_head.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_InHead) { + SimpleBrowserTest(FIREFOX, kInHeadPage, L"InHead"); +} + +const wchar_t kVersionPage[] = L"files/version.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_Version) { + VersionTest(IE, kVersionPage, L"version"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_Version) { + VersionTest(FIREFOX, kVersionPage, L"version"); +} + +const wchar_t kEventListenerPage[] = L"files/event_listener.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_EventListener) { + SimpleBrowserTest(IE, kEventListenerPage, L"EventListener"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_EventListener) { + SimpleBrowserTest(FIREFOX, kEventListenerPage, L"EventListener"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_EventListener) { + OptionalBrowserTest(OPERA, kEventListenerPage, L"EventListener"); +} + +const wchar_t kPrivilegedApisPage[] = L"files/privileged_apis_host.html"; + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeIE_PrivilegedApis) { + SimpleBrowserTest(IE, kPrivilegedApisPage, L"PrivilegedApis"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeFF_PrivilegedApis) { + SimpleBrowserTest(FIREFOX, kPrivilegedApisPage, L"PrivilegedApis"); +} + +TEST_F(ChromeFrameTestWithWebServer, WidgetModeOpera_PrivilegedApis) { + OptionalBrowserTest(OPERA, kPrivilegedApisPage, L"PrivilegedApis"); +} + +class ChromeFrameTestEnvironment: public testing::Environment { + public: + ~ChromeFrameTestEnvironment() { + } + + void SetUp() { + ScopedChromeFrameRegistrar::RegisterDefaults(); + } + + void TearDown() { + } +}; + +::testing::Environment* const chrome_frame_env =
+ ::testing::AddGlobalTestEnvironment(new ChromeFrameTestEnvironment); + +// TODO(stoyan): - Move everything below in separate file(s). +struct LaunchDelegateMock : public ProxyFactory::LaunchDelegate { + MOCK_METHOD2(LaunchComplete, void(ChromeFrameAutomationProxy*, + AutomationLaunchResult)); +}; + +TEST(ProxyFactoryTest, CreateDestroy) { + ProxyFactory f; + LaunchDelegateMock d; + EXPECT_CALL(d, LaunchComplete(testing::NotNull(), testing::_)).Times(1); + void* id = f.GetAutomationServer(0, L"Adam.N.Epilinter", L"", false, &d); + f.ReleaseAutomationServer(id); +} + +TEST(ProxyFactoryTest, CreateSameProfile) { + ProxyFactory f; + LaunchDelegateMock d; + EXPECT_CALL(d, LaunchComplete(testing::NotNull(), testing::_)).Times(2); + void* i1 = f.GetAutomationServer(0, L"Dr. Gratiano Forbeson", L"", false, &d); + void* i2 = f.GetAutomationServer(0, L"Dr. Gratiano Forbeson", L"", false, &d); + EXPECT_EQ(i1, i2); + f.ReleaseAutomationServer(i2); + f.ReleaseAutomationServer(i1); +} + +TEST(ProxyFactoryTest, CreateDifferentProfiles) { + ProxyFactory f; + LaunchDelegateMock d; + EXPECT_CALL(d, LaunchComplete(testing::NotNull(), testing::_)).Times(2); + void* i1 = f.GetAutomationServer(0, L"Adam.N.Epilinter", L"", false, &d); + void* i2 = f.GetAutomationServer(0, L"Dr. Gratiano Forbeson", L"", false, &d); + EXPECT_NE(i1, i2); + f.ReleaseAutomationServer(i2); + f.ReleaseAutomationServer(i1); +} + +// ChromeFrameAutomationClient [CFAC] tests. +struct MockCFDelegate : public ChromeFrameDelegateImpl { + MOCK_CONST_METHOD0(GetWindow, WindowType()); + MOCK_METHOD1(GetBounds, void(RECT* bounds)); + MOCK_METHOD0(GetDocumentUrl, std::string()); + MOCK_METHOD2(ExecuteScript, bool(const std::string& script, + std::string* result)); + MOCK_METHOD0(OnAutomationServerReady, void()); + MOCK_METHOD2(OnAutomationServerLaunchFailed, void( + AutomationLaunchResult reason, const std::string& server_version)); + // This remains in interface since we call it if Navigate() + // returns immediate error. + MOCK_METHOD2(OnLoadFailed, void(int error_code, const std::string& url)); + + // Do not mock this method. :) Use it as message demuxer and dispatcher + // to the following methods (which we mock) + // MOCK_METHOD1(OnMessageReceived, void(const IPC::Message&)); + + + MOCK_METHOD2(OnNavigationStateChanged, void(int tab_handle, int flags)); + MOCK_METHOD2(OnUpdateTargetUrl, void(int tab_handle, + const std::wstring& new_target_url)); + MOCK_METHOD2(OnAcceleratorPressed, void(int tab_handle, + const MSG& accel_message)); + MOCK_METHOD2(OnTabbedOut, void(int tab_handle, bool reverse)); + MOCK_METHOD3(OnOpenURL, void(int tab_handle, const GURL& url, + int open_disposition)); + MOCK_METHOD2(OnDidNavigate, void(int tab_handle, + const IPC::NavigationInfo& navigation_info)); + MOCK_METHOD3(OnNavigationFailed, void(int tab_handle, int error_code, + const GURL& gurl)); + MOCK_METHOD2(OnLoad, void(int tab_handle, const GURL& url)); + MOCK_METHOD4(OnMessageFromChromeFrame, void(int tab_handle, + const std::string& message, + const std::string& origin, + const std::string& target)); + MOCK_METHOD5(OnHandleContextMenu, void(int tab_handle, HANDLE menu_handle, + int x_pos, int y_pos, int align_flags)); + MOCK_METHOD3(OnRequestStart, void(int tab_handle, int request_id, + const IPC::AutomationURLRequest& request)); + MOCK_METHOD3(OnRequestRead, void(int tab_handle, int request_id, + int bytes_to_read)); + MOCK_METHOD3(OnRequestEnd, void(int tab_handle, int request_id, + const URLRequestStatus& status)); + MOCK_METHOD3(OnSetCookieAsync, void(int tab_handle, const GURL& url, + const std::string& cookie)); + + // Use for sending network responses + void SetAutomationSender(IPC::Message::Sender* automation) { + automation_ = automation; + } + + // Set-expectation helpers + void SetOnNavigationStateChanged(int tab_handle) { + EXPECT_CALL(*this, + OnNavigationStateChanged(testing::Eq(tab_handle), testing::_)) + .Times(testing::AnyNumber()); + } + + // Response sender helpers + void ReplyStarted(const IPC::AutomationURLResponse* response, + int tab_handle, int request_id, + const IPC::AutomationURLRequest& request) { + automation_->Send(new AutomationMsg_RequestStarted(0, tab_handle, + request_id, *response)); + } + + void ReplyData(const std::string* data, int tab_handle, int request_id, + int bytes_to_read) { + automation_->Send(new AutomationMsg_RequestData(0, tab_handle, + request_id, *data)); + } + + void ReplyEOF(int tab_handle, int request_id) { + automation_->Send(new AutomationMsg_RequestEnd(0, tab_handle, + request_id, URLRequestStatus())); + } + + void Reply404(int tab_handle, int request_id, + const IPC::AutomationURLRequest& request) { + const IPC::AutomationURLResponse notfound = {"", "HTTP/1.1 404\r\n\r\n"}; + automation_->Send(new AutomationMsg_RequestStarted(0, tab_handle, + request_id, notfound)); + automation_->Send(new AutomationMsg_RequestEnd(0, tab_handle, + request_id, URLRequestStatus())); + } + + IPC::Message::Sender* automation_; +}; + +class MockProxyFactory : public ProxyFactory { + public: + MOCK_METHOD5(GetAutomationServer, void*(int, const std::wstring&, + const std::wstring& extra_argument, bool, ProxyFactory::LaunchDelegate*)); + MOCK_METHOD1(ReleaseAutomationServer, bool(void* id)); + + MockProxyFactory() : thread_("mock factory worker") { + thread_.Start(); + loop_ = thread_.message_loop(); + } + + // Fake implementation + void GetServerImpl(ChromeFrameAutomationProxy* pxy, + AutomationLaunchResult result, + int timeout, + ProxyFactory::LaunchDelegate* d) { + Task* task = NewRunnableMethod(d, + &ProxyFactory::LaunchDelegate::LaunchComplete, pxy, result); + loop_->PostDelayedTask(FROM_HERE, task, timeout/2); + } + + base::Thread thread_; + MessageLoop* loop_; +}; + +class MockAutomationProxy : public ChromeFrameAutomationProxy { + public: + MOCK_METHOD1(Send, bool(IPC::Message*)); + MOCK_METHOD3(SendAsAsync, void(IPC::SyncMessage* msg, void* callback, + void* key)); + MOCK_METHOD1(CancelAsync, void(void* key)); + MOCK_METHOD1(CreateTabProxy, scoped_refptr<TabProxy>(int handle)); + MOCK_METHOD0(server_version, std::string(void)); + MOCK_METHOD1(SendProxyConfig, void(const std::string&)); + MOCK_METHOD1(SetEnableExtensionAutomation, void(bool enable)); + + ~MockAutomationProxy() {} +}; + +struct MockAutomationMessageSender : public AutomationMessageSender { + MOCK_METHOD1(Send, bool(IPC::Message*)); + MOCK_METHOD3(SendWithTimeout, bool(IPC::Message* , int , bool*)); + + void ForwardTo(MockAutomationProxy *p) { + proxy_ = p; + ON_CALL(*this, Send(testing::_)) + .WillByDefault(testing::Invoke(proxy_, &MockAutomationProxy::Send)); + } + + MockAutomationProxy* proxy_; +}; + +template <> struct RunnableMethodTraits<ProxyFactory::LaunchDelegate> { + static void RetainCallee(ProxyFactory::LaunchDelegate* obj) {} + static void ReleaseCallee(ProxyFactory::LaunchDelegate* obj) {} +}; + +template <> struct RunnableMethodTraits<MockProxyFactory> { + static void RetainCallee(MockProxyFactory* obj) {} + static void ReleaseCallee(MockProxyFactory* obj) {} +}; + +template <> struct RunnableMethodTraits<ChromeFrameAutomationClient> { + static void RetainCallee(ChromeFrameAutomationClient* obj) {} + static void ReleaseCallee(ChromeFrameAutomationClient* obj) {} +}; + +// MessageLoopForUI wrapper that runs only for a limited time. +// We need a UI message loop in the main thread. +struct TimedMsgLoop { + public: + void RunFor(int seconds) { + loop_.PostDelayedTask(FROM_HERE, new MessageLoop::QuitTask, 1000 * seconds); + loop_.MessageLoop::Run(); + } + + void Quit() { + loop_.PostTask(FROM_HERE, new MessageLoop::QuitTask); + } + + MessageLoopForUI loop_; +}; + +template <> struct RunnableMethodTraits<TimedMsgLoop> { + static void RetainCallee(TimedMsgLoop* obj) {} + static void ReleaseCallee(TimedMsgLoop* obj) {} +}; + +// Saves typing. It's somewhat hard to create a wrapper around +// testing::InvokeWithoutArgs since it returns a +// non-public (testing::internal) type. +#define QUIT_LOOP(loop) testing::InvokeWithoutArgs(TaskHolder(\ + NewRunnableMethod(&loop, &TimedMsgLoop::Quit))) + +// We mock ChromeFrameDelegate only. The rest is with real AutomationProxy +TEST(CFACWithChrome, CreateTooFast) { + MockCFDelegate cfd; + TimedMsgLoop loop; + int timeout = 0; // Chrome cannot send Hello message so fast. + const std::wstring profile = L"Adam.N.Epilinter"; + + scoped_ptr<ChromeFrameAutomationClient> client; + client.reset(new ChromeFrameAutomationClient()); + + EXPECT_CALL(cfd, OnAutomationServerLaunchFailed(AUTOMATION_TIMEOUT, + testing::_)) + .Times(1) + .WillOnce(QUIT_LOOP(loop)); + + EXPECT_TRUE(client->Initialize(&cfd, timeout, false, profile, L"", false)); + loop.RunFor(10); + client->Uninitialize(); +} + +// This test may fail if Chrome take more that 10 seconds (timeout var) to +// launch. In this case GMock shall print something like "unexpected call to +// OnAutomationServerLaunchFailed". I'm yet to find out how to specify +// that this is an unexpected call, and still to execute and action. +TEST(CFACWithChrome, CreateNotSoFast) { + MockCFDelegate cfd; + TimedMsgLoop loop; + const std::wstring profile = L"Adam.N.Epilinter"; + int timeout = 10000; + + scoped_ptr<ChromeFrameAutomationClient> client; + client.reset(new ChromeFrameAutomationClient); + + EXPECT_CALL(cfd, OnAutomationServerReady()) + .Times(1) + .WillOnce(QUIT_LOOP(loop)); + + EXPECT_CALL(cfd, OnAutomationServerLaunchFailed(testing::_, testing::_)) + .Times(0); + + EXPECT_TRUE(client->Initialize(&cfd, timeout, false, profile, L"", false)); + + loop.RunFor(11); + client->Uninitialize(); + client.reset(NULL); +} + +MATCHER_P(MsgType, msg_type, "IPC::Message::type()") { + const IPC::Message& m = arg; + return (m.type() == msg_type); +} + +MATCHER_P(EqNavigationInfoUrl, url, "IPC::NavigationInfo matcher") { + if (url.is_valid() && url != arg.url) + return false; + // TODO: other members + return true; +} + +TEST(CFACWithChrome, NavigateOk) { + MockCFDelegate cfd; + TimedMsgLoop loop; + const std::wstring profile = L"Adam.N.Epilinter"; + const std::string url = "about:version"; + int timeout = 10000; + + scoped_ptr<ChromeFrameAutomationClient> client; + client.reset(new ChromeFrameAutomationClient); + + EXPECT_CALL(cfd, OnAutomationServerReady()) + .WillOnce(testing::InvokeWithoutArgs(TaskHolder(NewRunnableMethod( + client.get(), &ChromeFrameAutomationClient::InitiateNavigation, + url)))); + +// cfd.SetOnNavigationStateChanged(); + EXPECT_CALL(cfd, + OnNavigationStateChanged(testing::_, testing::_)) + .Times(testing::AnyNumber()); + + { + testing::InSequence s; + + EXPECT_CALL(cfd, OnDidNavigate(testing::_, EqNavigationInfoUrl(GURL()))) + .Times(1); + + EXPECT_CALL(cfd, OnUpdateTargetUrl(testing::_, testing::_)).Times(1); + + EXPECT_CALL(cfd, OnLoad(testing::_, testing::_)) + .Times(1) + .WillOnce(QUIT_LOOP(loop)); + } + + EXPECT_TRUE(client->Initialize(&cfd, timeout, false, profile, L"", false)); + loop.RunFor(10); + client->Uninitialize(); + client.reset(NULL); +} + +// Bug: http://b/issue?id=2033644 +TEST(CFACWithChrome, DISABLED_NavigateFailed) { + MockCFDelegate cfd; + TimedMsgLoop loop; + const std::wstring profile = L"Adam.N.Epilinter"; + const std::string url = "http://127.0.0.3:65412/"; + int timeout = 10000; + + scoped_ptr<ChromeFrameAutomationClient> client; + client.reset(new ChromeFrameAutomationClient); + + EXPECT_CALL(cfd, OnAutomationServerReady()) + .WillOnce(testing::InvokeWithoutArgs(TaskHolder(NewRunnableMethod( + client.get(), &ChromeFrameAutomationClient::InitiateNavigation, + url)))); + + EXPECT_CALL(cfd, + OnNavigationStateChanged(testing::_, testing::_)) + .Times(testing::AnyNumber()); + + EXPECT_CALL(cfd, OnNavigationFailed(testing::_, testing::_, testing::_)) + .Times(1); + + EXPECT_CALL(cfd, OnUpdateTargetUrl(testing::_, testing::_)) + .Times(testing::AnyNumber()); + + EXPECT_CALL(cfd, OnLoad(testing::_, testing::_)) + .Times(0); + + EXPECT_TRUE(client->Initialize(&cfd, timeout, false, profile, L"", false)); + + loop.RunFor(10); + client->Uninitialize(); + client.reset(NULL); +} + +MATCHER_P(EqURLRequest, x, "IPC::AutomationURLRequest matcher") { + if (arg.url != x.url) + return false; + if (arg.method != x.method) + return false; + if (arg.referrer != x.referrer) + return false; + if (arg.extra_request_headers != x.extra_request_headers) + return false; + // TODO: uploaddata member + return true; +} + +MATCHER_P(EqUrlGet, url, "Quick URL matcher for 'HTTP GET' request") { + if (arg.url != url) + return false; + if (arg.method != "GET") + return false; + return true; +} + +TEST(CFACWithChrome, UseHostNetworkStack) { + MockCFDelegate cfd; + TimedMsgLoop loop; + const std::wstring profile = L"Adam.N.Epilinter"; + const std::string url = "http://bongo.com"; + int timeout = 10000; + + scoped_ptr<ChromeFrameAutomationClient> client; + client.reset(new ChromeFrameAutomationClient); + client->set_use_chrome_network(false); + cfd.SetAutomationSender(client.get()); + + EXPECT_CALL(cfd, OnAutomationServerReady()) + .WillOnce(testing::InvokeWithoutArgs(TaskHolder(NewRunnableMethod( + client.get(), &ChromeFrameAutomationClient::InitiateNavigation, + url)))); + + EXPECT_CALL(cfd, OnNavigationStateChanged(testing::_, testing::_)) + .Times(testing::AnyNumber()); + + EXPECT_CALL(cfd, GetBounds(testing::_)) + .Times(testing::AtMost(1)); + + EXPECT_CALL(cfd, OnUpdateTargetUrl(testing::_, testing::_)) + .Times(testing::AnyNumber()); + + // Note slash appending to the url string, because of GURL inside chrome + const IPC::AutomationURLResponse found = {"", "HTTP/0.9 200\r\n\r\n\r\n", }; + + // Hard coded tab and request ids + static const int tab_id = 1; + int request_id = 2; + + EXPECT_CALL(cfd, OnRequestStart(tab_id, request_id, EqUrlGet(url + '/'))) + .Times(1) + .WillOnce(testing::Invoke(CBF(&cfd, &MockCFDelegate::ReplyStarted, + &found))); + + // Return some trivial page, that have a link to a "logo.gif" image + const std::string data = "<!DOCTYPE html><title>Hello</title>" + "<img src=\"logo.gif\">"; + EXPECT_CALL(cfd, OnRequestRead(tab_id, request_id, testing::Ge(0))) + .Times(2) + .WillOnce(testing::Invoke(CBF(&cfd, &MockCFDelegate::ReplyData, &data))) + .WillOnce(testing::WithArgs<0, 1>(testing::Invoke(CBF(&cfd, + &MockCFDelegate::ReplyEOF)))); + + EXPECT_CALL(cfd, OnDidNavigate(tab_id, EqNavigationInfoUrl(GURL(url)))) + .Times(1); + EXPECT_CALL(cfd, OnLoad(tab_id, GURL(url))) + .Times(1); + + // Expect request for logo.gif + request_id++; + EXPECT_CALL(cfd, + OnRequestStart(tab_id, request_id, EqUrlGet(url + "/logo.gif"))) + .Times(1) + .WillOnce(testing::Invoke(CBF(&cfd, &MockCFDelegate::Reply404))); + + EXPECT_CALL(cfd, OnRequestRead(tab_id, request_id, testing::_)) + .Times(testing::AtMost(1)); + + // Chrome makes a brave request for favicon.ico + request_id++; + EXPECT_CALL(cfd, + OnRequestStart(tab_id, request_id, EqUrlGet(url + "/favicon.ico"))) + .Times(1) + .WillOnce(testing::Invoke(CBF(&cfd, &MockCFDelegate::Reply404))); + + EXPECT_CALL(cfd, OnRequestRead(tab_id, request_id, testing::_)) + .Times(testing::AtMost(1)); + + bool incognito = true; + EXPECT_TRUE(client->Initialize(&cfd, timeout, false, profile, L"", + incognito)); + + loop.RunFor(10); + client->Uninitialize(); + client.reset(NULL); +} + + +// [CFAC] -- uses a ProxyFactory for creation of ChromeFrameAutomationProxy +// -- uses ChromeFrameAutomationProxy +// -- uses TabProxy obtained from ChromeFrameAutomationProxy +// -- uses ChromeFrameDelegate as outgoing interface +// +// We mock ProxyFactory to return mock object (MockAutomationProxy) implementing +// ChromeFrameAutomationProxy interface. +// Since CFAC uses TabProxy for few calls and TabProxy is not easy mockable, +// we create 'real' TabProxy but with fake AutomationSender (the one responsible +// for sending messages over channel). +// Additionally we have mock implementation ChromeFrameDelagate interface - +// MockCFDelegate. + +// Test fixture, saves typing all of it's members. +class CFACMockTest : public testing::Test { + public: + MockProxyFactory factory_; + MockCFDelegate cfd_; + TimedMsgLoop loop_; + MockAutomationProxy proxy_; + scoped_ptr<AutomationHandleTracker> tracker_; + MockAutomationMessageSender dummy_sender_; + scoped_refptr<TabProxy> tab_; + scoped_ptr<ChromeFrameAutomationClient> client_; // the victim of all tests + + std::wstring profile_; + int timeout_; + void* id_; // Automation server id we are going to return + int tab_handle_; // Tab handle. Any non-zero value is Ok. + + inline ChromeFrameAutomationProxy* get_proxy() { + return static_cast<ChromeFrameAutomationProxy*>(&proxy_); + } + + inline void CreateTab() { + ASSERT_EQ(NULL, tab_.get()); + tab_ = new TabProxy(&dummy_sender_, tracker_.get(), tab_handle_); + } + + // Easy methods to set expectations. + void SetAutomationServerOk() { + EXPECT_CALL(factory_, GetAutomationServer(testing::Eq(timeout_), + testing::StrEq(profile_), + testing::_, + testing::_, + testing::NotNull())) + .Times(1) + .WillOnce(testing::DoAll( + testing::WithArgs<0, 4>( + testing::Invoke(CBF(&factory_, &MockProxyFactory::GetServerImpl, + get_proxy(), AUTOMATION_SUCCESS))), + testing::Return(id_))); + + EXPECT_CALL(factory_, ReleaseAutomationServer(testing::Eq(id_))).Times(1); + } + + void Set_CFD_LaunchFailed(AutomationLaunchResult result) { + EXPECT_CALL(cfd_, OnAutomationServerLaunchFailed( + testing::Eq(result), testing::_)) + .Times(1) + .WillOnce(QUIT_LOOP(loop_)); + } + + protected: + CFACMockTest() : tracker_(NULL), timeout_(500), + profile_(L"Adam.N.Epilinter") { + id_ = reinterpret_cast<void*>(5); + tab_handle_ = 3; + } + + virtual void SetUp() { + dummy_sender_.ForwardTo(&proxy_); + tracker_.reset(new AutomationHandleTracker(&dummy_sender_)); + + client_.reset(new ChromeFrameAutomationClient); + client_->set_proxy_factory(&factory_); + } +}; + +// Could be implemented as MockAutomationProxy member (we have WithArgs<>!) +ACTION_P3(HandleCreateTab, tab_handle, external_tab_container, tab_wnd) { + // arg0 - message + // arg1 - callback + // arg2 - key + CallbackRunner<Tuple3<HWND, HWND, int> >* c = + reinterpret_cast<CallbackRunner<Tuple3<HWND, HWND, int> >*>(arg1); + c->Run(external_tab_container, tab_wnd, tab_handle); + delete c; + delete arg0; +} + +TEST_F(CFACMockTest, MockedCreateTabOk) { + int timeout = 500; + CreateTab(); + SetAutomationServerOk(); + + EXPECT_CALL(proxy_, server_version()).Times(testing::AnyNumber()) + .WillRepeatedly(testing::Return("")); + + // We need some valid HWNDs, when responding to CreateExternalTab + HWND h1 = ::GetDesktopWindow(); + HWND h2 = ::GetDesktopWindow(); + EXPECT_CALL(proxy_, SendAsAsync(testing::Property(&IPC::SyncMessage::type, + AutomationMsg_CreateExternalTab__ID), + testing::NotNull(), testing::_)) + .Times(1) + .WillOnce(HandleCreateTab(tab_handle_, h1, h2)); + + EXPECT_CALL(proxy_, CreateTabProxy(testing::Eq(tab_handle_))) + .WillOnce(testing::Return(tab_)); + + EXPECT_CALL(cfd_, OnAutomationServerReady()) + .WillOnce(QUIT_LOOP(loop_)); + + // Here we go! + EXPECT_TRUE(client_->Initialize(&cfd_, timeout, false, profile_, L"", false)); + loop_.RunFor(10); + client_->Uninitialize(); +} + +TEST_F(CFACMockTest, MockedCreateTabFailed) { + HWND null_wnd = NULL; + SetAutomationServerOk(); + + EXPECT_CALL(proxy_, server_version()).Times(testing::AnyNumber()) + .WillRepeatedly(testing::Return("")); + + EXPECT_CALL(proxy_, SendAsAsync(testing::Property(&IPC::SyncMessage::type, + AutomationMsg_CreateExternalTab__ID), + testing::NotNull(), testing::_)) + .Times(1) + .WillOnce(HandleCreateTab(tab_handle_, null_wnd, null_wnd)); + + EXPECT_CALL(proxy_, CreateTabProxy(testing::_)).Times(0); + + Set_CFD_LaunchFailed(AUTOMATION_CREATE_TAB_FAILED); + + // Here we go! + EXPECT_TRUE(client_->Initialize(&cfd_, timeout_, false, profile_, L"", + false)); + loop_.RunFor(4); + client_->Uninitialize(); +} + +const wchar_t kMetaTagPage[] = L"files/meta_tag.html"; +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_MetaTag) { + SimpleBrowserTest(IE, kMetaTagPage, L"meta_tag"); +} + +const wchar_t kCFProtocolPage[] = L"files/chrome_frame_protocol.html"; +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_CFProtocol) { + SimpleBrowserTest(IE, kCFProtocolPage, L"chrome_frame_protocol"); +} + +const wchar_t kPersistentCookieTest[] = + L"files/persistent_cookie_test_page.html"; +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_PersistentCookieTest) { + SimpleBrowserTest(IE, kPersistentCookieTest, L"PersistentCookieTest"); +} + +const wchar_t kNavigateOutPage[] = L"files/navigate_out.html"; +TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_NavigateOut) { + SimpleBrowserTest(IE, kNavigateOutPage, L"navigate_out"); +} + +HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser) { + if (!web_browser) + return E_INVALIDARG; + + ScopedComPtr<IWebBrowser2> web_browser2; + HRESULT hr = CoCreateInstance( + CLSID_InternetExplorer, NULL, CLSCTX_LOCAL_SERVER, IID_IWebBrowser2, + reinterpret_cast<void**>(web_browser2.Receive())); + + if (SUCCEEDED(hr)) { + *web_browser = web_browser2.Detach(); + } + + return hr; +} + +TEST(ChromeFrameTest, FullTabModeIE_DisallowedUrls) { + int major_version = 0; + int minor_version = 0; + int bugfix_version = 0; + + base::SysInfo::OperatingSystemVersionNumbers(&major_version, &minor_version, + &bugfix_version); + if (major_version > 5) { + DLOG(INFO) << __FUNCTION__ << " Not running test on Windows version: " + << major_version; + return; + } + + IEVersion ie_version = GetIEVersion(); + if (ie_version == IE_8) { + DLOG(INFO) << __FUNCTION__ << " Not running test on IE8"; + return; + } + + HRESULT hr = CoInitialize(NULL); + bool should_uninit = SUCCEEDED(hr); + + ScopedComPtr<IWebBrowser2> web_browser2; + EXPECT_TRUE(S_OK == LaunchIEAsComServer(web_browser2.Receive())); + web_browser2->put_Visible(VARIANT_TRUE); + + CComObject<WebBrowserEventSink>* web_browser_sink = NULL; + CComObject<WebBrowserEventSink>::CreateInstance(&web_browser_sink); + + // Pass the main thread id to the browser sink so that it can notify + // us about test completion. + web_browser_sink->set_main_thread_id(GetCurrentThreadId()); + + hr = web_browser_sink->DispEventAdvise(web_browser2, + &DIID_DWebBrowserEvents2); + EXPECT_TRUE(hr == S_OK); + + VARIANT empty = ScopedVariant::kEmptyVariant; + ScopedVariant url; + url.Set(L"cf:file:///C:/"); + + TimedMsgLoop loop; + + hr = web_browser2->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty); + EXPECT_TRUE(hr == S_OK); + + loop.RunFor(10); + + EXPECT_TRUE(web_browser_sink->navigation_failed()); + + hr = web_browser_sink->DispEventUnadvise(web_browser2); + EXPECT_TRUE(hr == S_OK); + + web_browser2.Release(); + chrome_frame_test::CloseAllIEWindows(); + + if (should_uninit) { + CoUninitialize(); + } +} + |