summaryrefslogtreecommitdiffstats
path: root/chrome_frame/test
diff options
context:
space:
mode:
authorslightlyoff@chromium.org <slightlyoff@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-24 05:11:58 +0000
committerslightlyoff@chromium.org <slightlyoff@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-09-24 05:11:58 +0000
commitf781782dd67077478e117c61dca4ea5eefce3544 (patch)
tree4801f724123cfdcbb69c4e7fe40a565b331723ae /chrome_frame/test
parent63cf4759efa2373e33436fb5df6849f930081226 (diff)
downloadchromium_src-f781782dd67077478e117c61dca4ea5eefce3544.zip
chromium_src-f781782dd67077478e117c61dca4ea5eefce3544.tar.gz
chromium_src-f781782dd67077478e117c61dca4ea5eefce3544.tar.bz2
Initial import of the Chrome Frame codebase. Integration in chrome.gyp coming in a separate CL.
BUG=None TEST=None Review URL: http://codereview.chromium.org/218019 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@27042 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/test')
-rw-r--r--chrome_frame/test/ChromeTab_UnitTests.vcproj273
-rw-r--r--chrome_frame/test/chrome_frame_automation_mock.cc47
-rw-r--r--chrome_frame/test/chrome_frame_automation_mock.h208
-rw-r--r--chrome_frame/test/chrome_frame_test_utils.cc416
-rw-r--r--chrome_frame/test/chrome_frame_test_utils.h61
-rw-r--r--chrome_frame/test/chrome_frame_unittests.cc1510
-rw-r--r--chrome_frame/test/chrome_frame_unittests.h164
-rw-r--r--chrome_frame/test/chrometab_unittests.vsprops14
-rw-r--r--chrome_frame/test/com_message_event_unittest.cc325
-rw-r--r--chrome_frame/test/data/CFInstance_basic_frame.html8
-rw-r--r--chrome_frame/test/data/CFInstance_basic_host.html55
-rw-r--r--chrome_frame/test/data/CFInstance_default_ctor_host.html45
-rw-r--r--chrome_frame/test/data/CFInstance_delay_host.html47
-rw-r--r--chrome_frame/test/data/CFInstance_fallback_host.html44
-rw-r--r--chrome_frame/test/data/CFInstance_iframe_onload_host.html41
-rw-r--r--chrome_frame/test/data/CFInstance_iframe_post_host.html50
-rw-r--r--chrome_frame/test/data/CFInstance_no_src_host.html43
-rw-r--r--chrome_frame/test/data/CFInstance_post_frame.html26
-rw-r--r--chrome_frame/test/data/CFInstance_post_host.html47
-rw-r--r--chrome_frame/test/data/CFInstance_rpc_frame.html27
-rw-r--r--chrome_frame/test/data/CFInstance_rpc_host.html51
-rw-r--r--chrome_frame/test/data/CFInstance_rpc_internal_frame.html28
-rw-r--r--chrome_frame/test/data/CFInstance_rpc_internal_host.html45
-rw-r--r--chrome_frame/test/data/CFInstance_singleton_frame.html20
-rw-r--r--chrome_frame/test/data/CFInstance_singleton_host.html44
-rw-r--r--chrome_frame/test/data/CFInstance_zero_size_host.html41
-rw-r--r--chrome_frame/test/data/back_to_ie.html21
-rw-r--r--chrome_frame/test/data/cf_protocol.html20
-rw-r--r--chrome_frame/test/data/chrome_frame_mime_filter_test.html32
-rw-r--r--chrome_frame/test/data/chrome_frame_resize.html138
-rw-r--r--chrome_frame/test/data/chrome_frame_resize_hosted.html48
-rw-r--r--chrome_frame/test/data/chrome_frame_tester_helpers.js142
-rw-r--r--chrome_frame/test/data/event_listener.html42
-rw-r--r--chrome_frame/test/data/iframe_basic_host.html13
-rw-r--r--chrome_frame/test/data/in_head.html62
-rw-r--r--chrome_frame/test/data/initialize_hidden.html120
-rw-r--r--chrome_frame/test/data/meta_tag.html21
-rw-r--r--chrome_frame/test/data/navigate_out.html20
-rw-r--r--chrome_frame/test/data/navigateurl_absolute_host.html64
-rw-r--r--chrome_frame/test/data/navigateurl_basic_frame.html12
-rw-r--r--chrome_frame/test/data/navigateurl_relative_host.html60
-rw-r--r--chrome_frame/test/data/persistent_cookie_test_page.html36
-rw-r--r--chrome_frame/test/data/postmessage_basic_frame.html27
-rw-r--r--chrome_frame/test/data/postmessage_basic_host.html69
-rw-r--r--chrome_frame/test/data/privileged_apis_frame.html33
-rw-r--r--chrome_frame/test/data/privileged_apis_host.html85
-rw-r--r--chrome_frame/test/data/simple_object_focus.html95
-rw-r--r--chrome_frame/test/data/simple_object_focus_cf.html10
-rw-r--r--chrome_frame/test/data/src_property_frame1.html13
-rw-r--r--chrome_frame/test/data/src_property_frame2.html8
-rw-r--r--chrome_frame/test/data/src_property_host.html65
-rw-r--r--chrome_frame/test/data/version.html29
-rw-r--r--chrome_frame/test/function_stub_unittest.cc66
-rw-r--r--chrome_frame/test/helper_gmock.h597
-rw-r--r--chrome_frame/test/html_util_test_data/basic_test.html11
-rw-r--r--chrome_frame/test/html_util_test_data/degenerate_cases_test.html7
-rw-r--r--chrome_frame/test/html_util_test_data/multiple_tags.html17
-rw-r--r--chrome_frame/test/html_util_test_data/quotes_test.html10
-rw-r--r--chrome_frame/test/html_util_unittests.cc215
-rw-r--r--chrome_frame/test/http_server.cc56
-rw-r--r--chrome_frame/test/http_server.h32
-rw-r--r--chrome_frame/test/icu_stubs_unittests.cc73
-rw-r--r--chrome_frame/test/net/dialog_watchdog.cc146
-rw-r--r--chrome_frame/test/net/dialog_watchdog.h64
-rw-r--r--chrome_frame/test/net/fake_external_tab.cc391
-rw-r--r--chrome_frame/test/net/fake_external_tab.h106
-rw-r--r--chrome_frame/test/net/process_singleton_subclass.cc111
-rw-r--r--chrome_frame/test/net/process_singleton_subclass.h36
-rw-r--r--chrome_frame/test/net/test_automation_provider.cc89
-rw-r--r--chrome_frame/test/net/test_automation_provider.h52
-rw-r--r--chrome_frame/test/net/test_automation_resource_message_filter.cc60
-rw-r--r--chrome_frame/test/net/test_automation_resource_message_filter.h50
-rw-r--r--chrome_frame/test/perf/chrome_frame_perftest.cc1137
-rw-r--r--chrome_frame/test/perf/chrome_frame_perftest.h21
-rw-r--r--chrome_frame/test/perf/chrometab_perftests.vcproj247
-rw-r--r--chrome_frame/test/perf/chrometab_perftests.vsprops14
-rw-r--r--chrome_frame/test/perf/run_all.cc17
-rw-r--r--chrome_frame/test/perf/silverlight.cc165
-rw-r--r--chrome_frame/test/run_all_unittests.cc49
-rw-r--r--chrome_frame/test/test_server.cc211
-rw-r--r--chrome_frame/test/test_server.h296
-rw-r--r--chrome_frame/test/test_server_test.cc194
-rw-r--r--chrome_frame/test/util_unittests.cc120
83 files changed, 9625 insertions, 0 deletions
diff --git a/chrome_frame/test/ChromeTab_UnitTests.vcproj b/chrome_frame/test/ChromeTab_UnitTests.vcproj
new file mode 100644
index 0000000..9f4369b
--- /dev/null
+++ b/chrome_frame/test/ChromeTab_UnitTests.vcproj
@@ -0,0 +1,273 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="chrometab_unittests"
+ ProjectGUID="{BA08FE92-567D-4411-B344-17ADAECA2B5A}"
+ RootNamespace="ChromeTab_UnitTests"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="..\..\chrome\$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;$(SolutionDir)..\third_party\libxslt\build\using_libxslt.vsprops;..\..\testing\using_gtest.vsprops;$(SolutionDir)..\chrome\third_party\wtl\using_wtl.vsprops;.\chrometab_unittests.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="..\..\chrome\$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;$(SolutionDir)..\third_party\libxslt\build\using_libxslt.vsprops;..\..\testing\using_gtest.vsprops;$(SolutionDir)..\chrome\third_party\wtl\using_wtl.vsprops;$(SolutionDir)..\build\release.vsprops;.\chrometab_unittests.vsprops"
+ UseOfATL="1"
+ ATLMinimizesCRunTimeLibraryUsage="false"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="2"
+ OmitFramePointers="false"
+ WholeProgramOptimization="false"
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;"
+ StringPooling="true"
+ BasicRuntimeChecks="0"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ AdditionalLibraryDirectories="$(ConfigurationName)\lib;"
+ IgnoreDefaultLibraryNames=""
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ OptimizeForWindows98="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <Filter
+ Name="Common"
+ >
+ <File
+ RelativePath=".\cf_test_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\cf_test_utils.h"
+ >
+ </File>
+ <File
+ RelativePath=".\chrometab_unittests.h"
+ >
+ </File>
+ <File
+ RelativePath=".\run_all_unittests.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\base\test_suite.h"
+ >
+ </File>
+ <File
+ RelativePath="..\test_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\test_utils.h"
+ >
+ </File>
+ </Filter>
+ <Filter
+ Name="Tests"
+ >
+ <File
+ RelativePath="..\chrome_frame_automation.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\chrometab_unittests.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\com_message_event.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\com_message_event_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\function_stub_unittest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\html_util_unittests.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\html_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\icu_stubs_unittests.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\util_unittests.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\utils.cc"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ <Filter
+ Name="Header Files"
+ Filter="h;hpp;hxx;hm;inl;inc;xsd"
+ UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"
+ >
+ </Filter>
+ <Filter
+ Name="Resource Files"
+ Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"
+ UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"
+ >
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chrome_frame/test/chrome_frame_automation_mock.cc b/chrome_frame/test/chrome_frame_automation_mock.cc
new file mode 100644
index 0000000..d900176
--- /dev/null
+++ b/chrome_frame/test/chrome_frame_automation_mock.cc
@@ -0,0 +1,47 @@
+// Copyright (c) 2006-2009 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/chrome_frame_automation_mock.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+const int kLongWaitTimeout = 25 * 1000;
+const int kShortWaitTimeout = 5 * 1000;
+
+TEST(ChromeFrame, Launch) {
+ MessageLoopForUI loop;
+ AutomationMockLaunch mock_launch(&loop, kLongWaitTimeout);
+
+ mock_launch.Navigate("about:blank");
+ loop.Run(NULL);
+ EXPECT_EQ(true, mock_launch.launch_result());
+}
+
+TEST(ChromeFrame, Navigate) {
+ MessageLoopForUI loop;
+ AutomationMockNavigate mock_navigate(&loop, kLongWaitTimeout);
+
+ mock_navigate.NavigateRelativeFile(L"postmessage_basic_frame.html");
+ loop.Run(NULL);
+ EXPECT_EQ(true, mock_navigate.navigation_result());
+}
+
+TEST(ChromeFrame, PostMessage) {
+ MessageLoopForUI loop;
+ AutomationMockPostMessage mock_postmessage(&loop, kLongWaitTimeout);
+
+ mock_postmessage.NavigateRelativeFile(L"postmessage_basic_frame.html");
+ loop.Run(NULL);
+ EXPECT_EQ(true, mock_postmessage.postmessage_result());
+}
+
+TEST(ChromeFrame, RequestStart) {
+ MessageLoopForUI loop;
+ AutomationMockHostNetworkRequestStart mock_request_start(&loop,
+ kLongWaitTimeout);
+
+ mock_request_start.NavigateRelative(L"postmessage_basic_frame.html");
+ loop.Run(NULL);
+ EXPECT_EQ(true, mock_request_start.request_start_result());
+}
+
diff --git a/chrome_frame/test/chrome_frame_automation_mock.h b/chrome_frame/test/chrome_frame_automation_mock.h
new file mode 100644
index 0000000..4c3fe7b
--- /dev/null
+++ b/chrome_frame/test/chrome_frame_automation_mock.h
@@ -0,0 +1,208 @@
+// Copyright (c) 2006-2009 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.
+#ifndef CHROME_FRAME_TEST_CHROME_FRAME_AUTOMATION_MOCK_H_
+#define CHROME_FRAME_TEST_CHROME_FRAME_AUTOMATION_MOCK_H_
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/path_service.h"
+#include "chrome_frame/chrome_frame_automation.h"
+#include "chrome_frame/chrome_frame_plugin.h"
+#include "chrome_frame/test/http_server.h"
+
+template <typename T>
+class AutomationMockDelegate
+ : public CWindowImpl<T>,
+ public ChromeFramePlugin<T> {
+ public:
+ AutomationMockDelegate(MessageLoop* caller_message_loop,
+ int launch_timeout, bool perform_version_check,
+ const std::wstring& profile_name,
+ const std::wstring& extra_chrome_arguments, bool incognito)
+ : caller_message_loop_(caller_message_loop), is_connected_(false) {
+ test_server_.SetUp();
+ automation_client_.reset(new ChromeFrameAutomationClient);
+ automation_client_->Initialize(this, launch_timeout, perform_version_check,
+ profile_name, extra_chrome_arguments, incognito);
+ }
+ ~AutomationMockDelegate() {
+ if (automation_client_.get()) {
+ automation_client_->Uninitialize();
+ automation_client_.reset();
+ }
+ if (IsWindow())
+ DestroyWindow();
+
+ test_server_.TearDown();
+ }
+
+ // Navigate external tab to the specified url through automation
+ bool Navigate(const std::string& url) {
+ url_ = GURL(url);
+ return automation_client_->InitiateNavigation(url);
+ }
+
+ // Navigate the external to a 'file://' url for unit test files
+ bool NavigateRelativeFile(const std::wstring& file) {
+ FilePath cf_source_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &cf_source_path);
+ std::wstring file_url(L"file://");
+ file_url.append(cf_source_path.Append(
+ FILE_PATH_LITERAL("chrome_frame")).Append(
+ FILE_PATH_LITERAL("test")).Append(
+ FILE_PATH_LITERAL("data")).Append(file).value());
+ return Navigate(WideToUTF8(file_url));
+ }
+
+ bool NavigateRelative(const std::wstring& relative_url) {
+ return Navigate(test_server_.Resolve(relative_url.c_str()).spec());
+ }
+
+ virtual void OnAutomationServerReady() {
+ if (automation_client_.get()) {
+ Create(NULL, 0, NULL, WS_OVERLAPPEDWINDOW);
+ DCHECK(IsWindow());
+ is_connected_ = true;
+ } else {
+ NOTREACHED();
+ }
+ }
+
+ virtual void OnAutomationServerLaunchFailed() {
+ QuitMessageLoop();
+ }
+
+ virtual void OnLoad(int tab_handle, const GURL& url) {
+ if (url_ == url) {
+ navigation_result_ = true;
+ } else {
+ QuitMessageLoop();
+ }
+ }
+
+ virtual void OnLoadFailed(int error_code, const std::string& url) {
+ QuitMessageLoop();
+ }
+
+ ChromeFrameAutomationClient* automation() {
+ return automation_client_.get();
+ }
+ const GURL& url() const {
+ return url_;
+ }
+ bool is_connected() const {
+ return is_connected_;
+ }
+ bool navigation_result() const {
+ return navigation_result_;
+ }
+
+ BEGIN_MSG_MAP(AutomationMockDelegate)
+ END_MSG_MAP()
+
+ protected:
+ void QuitMessageLoop() {
+ // Quit on the caller message loop has to be called on the caller
+ // thread.
+ caller_message_loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask);
+ }
+
+ private:
+ ChromeFrameHTTPServer test_server_;
+ MessageLoop* caller_message_loop_;
+ GURL url_;
+ bool is_connected_;
+ bool navigation_result_;
+};
+
+class AutomationMockLaunch
+ : public AutomationMockDelegate<AutomationMockLaunch> {
+ public:
+ typedef AutomationMockDelegate<AutomationMockLaunch> Base;
+ AutomationMockLaunch(MessageLoop* caller_message_loop,
+ int launch_timeout)
+ : Base(caller_message_loop, launch_timeout, true, L"", L"", false) {
+ }
+ virtual void OnAutomationServerReady() {
+ Base::OnAutomationServerReady();
+ QuitMessageLoop();
+ }
+ bool launch_result() const {
+ return is_connected();
+ }
+};
+
+class AutomationMockNavigate
+ : public AutomationMockDelegate<AutomationMockNavigate> {
+ public:
+ typedef AutomationMockDelegate<AutomationMockNavigate> Base;
+ AutomationMockNavigate(MessageLoop* caller_message_loop,
+ int launch_timeout)
+ : Base(caller_message_loop, launch_timeout, true, L"", L"", false) {
+ }
+ virtual void OnLoad(int tab_handle, const GURL& url) {
+ Base::OnLoad(tab_handle, url);
+ QuitMessageLoop();
+ }
+};
+
+class AutomationMockPostMessage
+ : public AutomationMockDelegate<AutomationMockPostMessage> {
+ public:
+ typedef AutomationMockDelegate<AutomationMockPostMessage> Base;
+ AutomationMockPostMessage(MessageLoop* caller_message_loop,
+ int launch_timeout)
+ : Base(caller_message_loop, launch_timeout, true, L"", L"", false),
+ postmessage_result_(false) {}
+ bool postmessage_result() const {
+ return postmessage_result_;
+ }
+ virtual void OnLoad(int tab_handle, const GURL& url) {
+ Base::OnLoad(tab_handle, url);
+ if (navigation_result()) {
+ automation()->ForwardMessageFromExternalHost("Test", "null", "*");
+ }
+ }
+ virtual void OnMessageFromChromeFrame(int tab_handle,
+ const std::string& message,
+ const std::string& origin,
+ const std::string& target) {
+ postmessage_result_ = true;
+ QuitMessageLoop();
+ }
+ private:
+ bool postmessage_result_;
+};
+
+class AutomationMockHostNetworkRequestStart
+ : public AutomationMockDelegate<AutomationMockHostNetworkRequestStart> {
+ public:
+ typedef AutomationMockDelegate<AutomationMockHostNetworkRequestStart> Base;
+ AutomationMockHostNetworkRequestStart(MessageLoop* caller_message_loop,
+ int launch_timeout)
+ : Base(caller_message_loop, launch_timeout, true, L"", L"", false),
+ request_start_result_(false) {
+ if (automation()) {
+ automation()->set_use_chrome_network(false);
+ }
+ }
+ bool request_start_result() const {
+ return request_start_result_;
+ }
+ virtual void OnRequestStart(int tab_handle, int request_id,
+ const IPC::AutomationURLRequest& request) {
+ request_start_result_ = true;
+ QuitMessageLoop();
+ }
+ virtual void OnLoad(int tab_handle, const GURL& url) {
+ Base::OnLoad(tab_handle, url);
+ }
+ private:
+ bool request_start_result_;
+};
+
+
+#endif // CHROME_FRAME_TEST_CHROME_FRAME_AUTOMATION_MOCK_H_
+
diff --git a/chrome_frame/test/chrome_frame_test_utils.cc b/chrome_frame/test/chrome_frame_test_utils.cc
new file mode 100644
index 0000000..a75d791
--- /dev/null
+++ b/chrome_frame/test/chrome_frame_test_utils.cc
@@ -0,0 +1,416 @@
+// 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 "chrome_frame/test/chrome_frame_test_utils.h"
+
+#include <atlbase.h>
+#include <atlwin.h>
+#include <iepmapi.h>
+
+#include "base/registry.h" // to find IE and firefox
+#include "base/scoped_handle.h"
+#include "base/scoped_comptr_win.h"
+#include "base/string_util.h"
+#include "base/win_util.h"
+#include "chrome/common/chrome_switches.h"
+
+namespace chrome_frame_test {
+
+const wchar_t kIEImageName[] = L"iexplore.exe";
+const wchar_t kIEBrokerImageName[] = L"ieuser.exe";
+const wchar_t kFirefoxImageName[] = L"firefox.exe";
+const wchar_t kOperaImageName[] = L"opera.exe";
+const wchar_t kSafariImageName[] = L"safari.exe";
+const wchar_t kChromeImageName[] = L"chrome.exe";
+
+bool IsTopLevelWindow(HWND window) {
+ long style = GetWindowLong(window, GWL_STYLE); // NOLINT
+ if (!(style & WS_CHILD))
+ return true;
+
+ HWND parent = GetParent(window);
+ if (!parent)
+ return true;
+
+ if (parent == GetDesktopWindow())
+ return true;
+
+ return false;
+}
+
+// Callback function for EnumThreadWindows.
+BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) {
+ int& count = *reinterpret_cast<int*>(param);
+ if (IsWindowVisible(hwnd)) {
+ if (IsWindowEnabled(hwnd)) {
+ DWORD results = 0;
+ if (!::SendMessageTimeout(hwnd, WM_SYSCOMMAND, SC_CLOSE, 0, SMTO_BLOCK,
+ 10000, &results)) {
+ DLOG(WARNING) << "Window hung: " << StringPrintf(L"%08X", hwnd);
+ }
+ count++;
+ } else {
+ DLOG(WARNING) << "Skipping disabled window: "
+ << StringPrintf(L"%08X", hwnd);
+ }
+ }
+ return TRUE; // continue enumeration
+}
+
+// Attempts to close all non-child, visible windows on the given thread.
+// The return value is the number of visible windows a close request was
+// sent to.
+int CloseVisibleTopLevelWindowsOnThread(DWORD thread_id) {
+ int window_close_attempts = 0;
+ EnumThreadWindows(thread_id, CloseWindowsThreadCallback,
+ reinterpret_cast<LPARAM>(&window_close_attempts));
+ return window_close_attempts;
+}
+
+// Enumerates the threads of a process and attempts to close visible non-child
+// windows on all threads of the process.
+// The return value is the number of visible windows a close request was
+// sent to.
+int CloseVisibleWindowsOnAllThreads(HANDLE process) {
+ DWORD process_id = ::GetProcessId(process);
+ if (process_id == 0) {
+ NOTREACHED();
+ return 0;
+ }
+
+ ScopedHandle snapshot(CreateToolhelp32Snapshot(TH32CS_SNAPTHREAD, 0));
+ if (!snapshot.IsValid()) {
+ NOTREACHED();
+ return 0;
+ }
+
+ int window_close_attempts = 0;
+ THREADENTRY32 te = { sizeof(THREADENTRY32) };
+ if (Thread32First(snapshot, &te)) {
+ do {
+ if (RTL_CONTAINS_FIELD(&te, te.dwSize, th32OwnerProcessID) &&
+ te.th32OwnerProcessID == process_id) {
+ window_close_attempts +=
+ CloseVisibleTopLevelWindowsOnThread(te.th32ThreadID);
+ }
+ te.dwSize = sizeof(te);
+ } while (Thread32Next(snapshot, &te));
+ }
+
+ return window_close_attempts;
+}
+
+class ForegroundHelperWindow : public CWindowImpl<ForegroundHelperWindow> {
+ public:
+BEGIN_MSG_MAP(ForegroundHelperWindow)
+ MESSAGE_HANDLER(WM_HOTKEY, OnHotKey)
+END_MSG_MAP()
+
+ HRESULT SetForeground(HWND window) {
+ DCHECK(::IsWindow(window));
+ if (NULL == Create(NULL, NULL, NULL, WS_POPUP))
+ return AtlHresultFromLastError();
+
+ static const int hotkey_id = 0x0000baba;
+
+ SetWindowLongPtr(GWLP_USERDATA, reinterpret_cast<ULONG_PTR>(window));
+ RegisterHotKey(m_hWnd, hotkey_id, 0, VK_F22);
+
+ MSG msg = {0};
+ PeekMessage(&msg, NULL, 0, 0, PM_NOREMOVE);
+
+ INPUT hotkey = {0};
+ hotkey.type = INPUT_KEYBOARD;
+ hotkey.ki.wVk = VK_F22;
+ SendInput(1, &hotkey, sizeof(hotkey));
+
+ while (GetMessage(&msg, NULL, 0, 0)) {
+ TranslateMessage(&msg);
+ DispatchMessage(&msg);
+ if (WM_HOTKEY == msg.message)
+ break;
+ }
+
+ UnregisterHotKey(m_hWnd, hotkey_id);
+ DestroyWindow();
+
+ return S_OK;
+ }
+
+ LRESULT OnHotKey(UINT msg, WPARAM wp, LPARAM lp, BOOL& handled) { // NOLINT
+ HWND window = reinterpret_cast<HWND>(GetWindowLongPtr(GWLP_USERDATA));
+ SetForegroundWindow(window);
+ return 1;
+ }
+};
+
+bool ForceSetForegroundWindow(HWND window) {
+ if (GetForegroundWindow() == window)
+ return true;
+ ForegroundHelperWindow foreground_helper_window;
+ HRESULT hr = foreground_helper_window.SetForeground(window);
+ return SUCCEEDED(hr);
+}
+
+struct PidAndWindow {
+ base::ProcessId pid;
+ HWND hwnd;
+};
+
+BOOL CALLBACK FindWindowInProcessCallback(HWND hwnd, LPARAM param) {
+ PidAndWindow* paw = reinterpret_cast<PidAndWindow*>(param);
+ base::ProcessId pid;
+ GetWindowThreadProcessId(hwnd, &pid);
+ if (pid == paw->pid && IsWindowVisible(hwnd)) {
+ paw->hwnd = hwnd;
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+bool EnsureProcessInForeground(base::ProcessId process_id) {
+ HWND hwnd = GetForegroundWindow();
+ base::ProcessId current_foreground_pid = 0;
+ DWORD active_thread_id = GetWindowThreadProcessId(hwnd,
+ &current_foreground_pid);
+ if (current_foreground_pid == process_id)
+ return true;
+
+ PidAndWindow paw = { process_id };
+ EnumWindows(FindWindowInProcessCallback, reinterpret_cast<LPARAM>(&paw));
+ if (!IsWindow(paw.hwnd)) {
+ DLOG(ERROR) << "failed to find process window";
+ return false;
+ }
+
+ bool ret = ForceSetForegroundWindow(paw.hwnd);
+ DLOG_IF(ERROR, !ret) << "ForceSetForegroundWindow: " << ret;
+
+ return ret;
+}
+
+// Iterates through all the characters in the string and simulates
+// keyboard input. The input goes to the currently active application.
+bool SendString(const wchar_t* string) {
+ DCHECK(string != NULL);
+
+ INPUT input[2] = {0};
+ input[0].type = INPUT_KEYBOARD;
+ input[0].ki.dwFlags = KEYEVENTF_UNICODE; // to avoid shift, etc.
+ input[1] = input[0];
+ input[1].ki.dwFlags |= KEYEVENTF_KEYUP;
+
+ for (const wchar_t* p = string; *p; p++) {
+ input[0].ki.wScan = input[1].ki.wScan = *p;
+ SendInput(2, input, sizeof(INPUT));
+ }
+
+ return true;
+}
+
+void SendVirtualKey(int16 key) {
+ INPUT input = { INPUT_KEYBOARD };
+ input.ki.wVk = key;
+ SendInput(1, &input, sizeof(input));
+ input.ki.dwFlags = KEYEVENTF_KEYUP;
+ SendInput(1, &input, sizeof(input));
+}
+
+void SendChar(char c) {
+ SendVirtualKey(VkKeyScanA(c));
+}
+
+void SendString(const char* s) {
+ while (*s) {
+ SendChar(*s);
+ s++;
+ }
+}
+
+// Sends a keystroke to the currently active application with optional
+// modifiers set.
+bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed,
+ bool alt_pressed) {
+ INPUT special_keys[3] = {0};
+ for (int index = 0; index < arraysize(special_keys); ++index) {
+ special_keys[index].type = INPUT_KEYBOARD;
+ special_keys[index].ki.dwFlags = 0;
+ }
+
+ int num_special_keys = 0;
+ if (shift_pressed) {
+ special_keys[num_special_keys].ki.wVk = VK_SHIFT;
+ num_special_keys++;
+ }
+
+ if (control_pressed) {
+ special_keys[num_special_keys].ki.wVk = VK_CONTROL;
+ num_special_keys++;
+ }
+
+ if (alt_pressed) {
+ special_keys[num_special_keys].ki.wVk = VK_MENU;
+ num_special_keys++;
+ }
+
+ // Depress the modifiers.
+ SendInput(num_special_keys, special_keys, sizeof(INPUT));
+
+ Sleep(100);
+
+ INPUT mnemonic = {0};
+ mnemonic.type = INPUT_KEYBOARD;
+ mnemonic.ki.wVk = mnemonic_char;
+
+ // Depress and release the mnemonic.
+ SendInput(1, &mnemonic, sizeof(INPUT));
+ Sleep(100);
+
+ mnemonic.ki.dwFlags |= KEYEVENTF_KEYUP;
+ SendInput(1, &mnemonic, sizeof(INPUT));
+ Sleep(100);
+
+ // Now release the modifiers.
+ for (int index = 0; index < num_special_keys; index++) {
+ special_keys[index].ki.dwFlags |= KEYEVENTF_KEYUP;
+ }
+
+ SendInput(num_special_keys, special_keys, sizeof(INPUT));
+ Sleep(100);
+
+ return true;
+}
+
+std::wstring GetExecutableAppPath(const std::wstring& file) {
+ std::wstring kAppPathsKey =
+ L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\App Paths\\";
+
+ std::wstring app_path;
+ RegKey key(HKEY_LOCAL_MACHINE, (kAppPathsKey + file).c_str());
+ if (key.Handle()) {
+ key.ReadValue(NULL, &app_path);
+ }
+
+ return app_path;
+}
+
+std::wstring FormatCommandForApp(const std::wstring& exe_name,
+ const std::wstring& argument) {
+ std::wstring reg_path(StringPrintf(L"Applications\\%ls\\shell\\open\\command",
+ exe_name.c_str()));
+ RegKey key(HKEY_CLASSES_ROOT, reg_path.c_str());
+
+ std::wstring command;
+ if (key.Handle()) {
+ key.ReadValue(NULL, &command);
+ int found = command.find(L"%1");
+ if (found >= 0) {
+ command.replace(found, 2, argument);
+ }
+ }
+ return command;
+}
+
+base::ProcessHandle LaunchExecutable(const std::wstring& executable,
+ const std::wstring& argument) {
+ base::ProcessHandle process = NULL;
+ std::wstring path = GetExecutableAppPath(executable);
+ if (path.empty()) {
+ path = FormatCommandForApp(executable, argument);
+ if (path.empty()) {
+ DLOG(ERROR) << "Failed to find executable: " << executable;
+ } else {
+ CommandLine cmdline(L"");
+ cmdline.ParseFromString(path);
+ base::LaunchApp(cmdline, false, false, &process);
+ }
+ } else {
+ CommandLine cmdline(path);
+ cmdline.AppendLooseValue(argument);
+ base::LaunchApp(cmdline, false, false, &process);
+ }
+ return process;
+}
+
+base::ProcessHandle LaunchFirefox(const std::wstring& url) {
+ return LaunchExecutable(kFirefoxImageName, url);
+}
+
+base::ProcessHandle LaunchSafari(const std::wstring& url) {
+ return LaunchExecutable(kSafariImageName, url);
+}
+
+base::ProcessHandle LaunchChrome(const std::wstring& url) {
+ return LaunchExecutable(kChromeImageName,
+ StringPrintf(L"--%ls ", switches::kNoFirstRun) + url);
+}
+
+base::ProcessHandle LaunchOpera(const std::wstring& url) {
+ // NOTE: For Opera tests to work it must be configured to start up with
+ // a blank page. There is an command line switch, -nosession, that's supposed
+ // to avoid opening up the previous session, but that switch is not working.
+ // TODO(tommi): Include a special ini file (opera6.ini) for opera and launch
+ // with our required settings. This file is by default stored here:
+ // "%USERPROFILE%\Application Data\Opera\Opera\profile\opera6.ini"
+ return LaunchExecutable(kOperaImageName, url);
+}
+
+base::ProcessHandle LaunchIEOnVista(const std::wstring& url) {
+ typedef HRESULT (WINAPI* IELaunchURLPtr)(
+ const wchar_t* url,
+ PROCESS_INFORMATION *pi,
+ VOID *info);
+
+ IELaunchURLPtr launch;
+ PROCESS_INFORMATION pi = {0};
+ IELAUNCHURLINFO info = {sizeof info, 0};
+ HMODULE h = LoadLibrary(L"ieframe.dll");
+ if (!h)
+ return NULL;
+ launch = reinterpret_cast<IELaunchURLPtr>(GetProcAddress(h, "IELaunchURL"));
+ HRESULT hr = launch(url.c_str(), &pi, &info);
+ FreeLibrary(h);
+ if (SUCCEEDED(hr))
+ CloseHandle(pi.hThread);
+ return pi.hProcess;
+}
+
+base::ProcessHandle LaunchIE(const std::wstring& url) {
+ if (win_util::GetWinVersion() >= win_util::WINVERSION_VISTA) {
+ return LaunchIEOnVista(url);
+ } else {
+ return LaunchExecutable(kIEImageName, url);
+ }
+}
+
+int CloseAllIEWindows() {
+ int ret = 0;
+
+ ScopedComPtr<IShellWindows> windows;
+ HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL,
+ IID_IShellWindows, reinterpret_cast<void**>(windows.Receive()));
+ DCHECK(SUCCEEDED(hr));
+
+ if (SUCCEEDED(hr)) {
+ long count = 0; // NOLINT
+ windows->get_Count(&count);
+ VARIANT i = { VT_I4 };
+ for (i.lVal = 0; i.lVal < count; ++i.lVal) {
+ ScopedComPtr<IDispatch> folder;
+ windows->Item(i, folder.Receive());
+ if (folder != NULL) {
+ ScopedComPtr<IWebBrowser2> browser;
+ if (SUCCEEDED(browser.QueryFrom(folder))) {
+ browser->Quit();
+ ++ret;
+ }
+ }
+ }
+ }
+
+ return ret;
+}
+
+} // namespace chrome_frame_test
diff --git a/chrome_frame/test/chrome_frame_test_utils.h b/chrome_frame/test/chrome_frame_test_utils.h
new file mode 100644
index 0000000..95e0c9b
--- /dev/null
+++ b/chrome_frame/test/chrome_frame_test_utils.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2009 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.
+
+#ifndef CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_
+#define CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_
+
+#include <windows.h>
+
+#include "base/basictypes.h"
+#include "base/process_util.h"
+
+namespace chrome_frame_test {
+
+bool IsTopLevelWindow(HWND window);
+int CloseVisibleWindowsOnAllThreads(HANDLE process);
+bool ForceSetForegroundWindow(HWND window);
+bool EnsureProcessInForeground(base::ProcessId process_id);
+
+// Iterates through all the characters in the string and simulates
+// keyboard input. The input goes to the currently active application.
+bool SendString(const wchar_t* s);
+
+// Sends a virtual key such as VK_TAB, VK_RETURN or a character that has been
+// translated to a virtual key.
+void SendVirtualKey(int16 key);
+
+// Translates a single char to a virtual key and calls SendVirtualKey.
+void SendChar(char c);
+
+// Sends an ascii string, char by char (calls SendChar for each).
+void SendString(const char* s);
+
+// Sends a keystroke to the currently active application with optional
+// modifiers set.
+bool SendMnemonic(WORD mnemonic_char, bool shift_pressed, bool control_pressed,
+ bool alt_pressed);
+
+base::ProcessHandle LaunchFirefox(const std::wstring& url);
+base::ProcessHandle LaunchOpera(const std::wstring& url);
+base::ProcessHandle LaunchIE(const std::wstring& url);
+base::ProcessHandle LaunchSafari(const std::wstring& url);
+base::ProcessHandle LaunchChrome(const std::wstring& url);
+
+// Attempts to close all open IE windows.
+// The return value is the number of windows closed.
+// @note: this function requires COM to be initialized on the calling thread.
+// Since the caller might be running in either MTA or STA, the function does
+// not perform this initialization itself.
+int CloseAllIEWindows();
+
+extern const wchar_t kIEImageName[];
+extern const wchar_t kIEBrokerImageName[];
+extern const wchar_t kFirefoxImageName[];
+extern const wchar_t kOperaImageName[];
+extern const wchar_t kSafariImageName[];
+extern const wchar_t kChromeImageName[];
+
+} // namespace chrome_frame_test
+
+#endif // CHROME_FRAME_CHROMETAB_UNITTESTS_CF_TEST_UTILS_H_
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();
+ }
+}
+
diff --git a/chrome_frame/test/chrome_frame_unittests.h b/chrome_frame/test/chrome_frame_unittests.h
new file mode 100644
index 0000000..98b5985
--- /dev/null
+++ b/chrome_frame/test/chrome_frame_unittests.h
@@ -0,0 +1,164 @@
+// 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.
+#ifndef CHROME_FRAME_TEST_CHROME_FRAME_UNITTESTS_H_
+#define CHROME_FRAME_TEST_CHROME_FRAME_UNITTESTS_H_
+
+#include <atlbase.h>
+#include <atlcom.h>
+#include <string>
+#include <exdisp.h>
+#include <exdispid.h>
+
+#include "base/ref_counted.h"
+#include "base/scoped_handle_win.h"
+#include "googleurl/src/gurl.h"
+#include "chrome_frame/test/http_server.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// Class that:
+// 1) Starts the local webserver,
+// 2) Supports launching browsers - Internet Explorer and Firefox with local url
+// 3) Wait the webserver to finish. It is supposed the test webpage to shutdown
+// the server by navigating to "kill" page
+// 4) Supports read the posted results from the test webpage to the "dump"
+// webserver directory
+class ChromeFrameTestWithWebServer: public testing::Test {
+ protected:
+ enum BrowserKind { INVALID, IE, FIREFOX, OPERA, SAFARI, CHROME };
+
+ bool LaunchBrowser(BrowserKind browser, const wchar_t* url);
+ bool WaitForTestToComplete(int milliseconds);
+ // Waits for the page to notify us of the window.onload event firing.
+ // Note that the milliseconds value is only approximate.
+ bool WaitForOnLoad(int milliseconds);
+ bool ReadResultFile(const std::wstring& file_name, std::string* data);
+
+ // Launches the specified browser and waits for the test to complete
+ // (see WaitForTestToComplete). Then checks that the outcome is OK.
+ // This function uses EXPECT_TRUE and ASSERT_TRUE for all steps performed
+ // hence no return value.
+ void SimpleBrowserTest(BrowserKind browser, const wchar_t* page,
+ const wchar_t* result_file_to_check);
+
+ // Same as SimpleBrowserTest but if the browser isn't installed (LaunchBrowser
+ // fails), the function will print out a warning but not treat the test
+ // as failed.
+ // Currently this is how we run Opera tests.
+ void OptionalBrowserTest(BrowserKind browser, const wchar_t* page,
+ const wchar_t* result_file_to_check);
+
+ // Test if chrome frame correctly reports its version.
+ void VersionTest(BrowserKind browser, const wchar_t* page,
+ const wchar_t* result_file_to_check);
+
+ void CloseBrowser();
+
+ // Ensures (well, at least tries to ensure) that the browser window has focus.
+ bool BringBrowserToTop();
+
+ // Returns true iff the specified result file contains 'expected result'.
+ bool CheckResultFile(const std::wstring& file_name,
+ const std::string& expected_result);
+
+ virtual void SetUp();
+ virtual void TearDown();
+
+ // Important: kind means "sheep" in Icelandic. ?:-o
+ const char* ToString(BrowserKind kind) {
+ switch (kind) {
+ case IE:
+ return "IE";
+ case FIREFOX:
+ return "Firefox";
+ case OPERA:
+ return "Opera";
+ case CHROME:
+ return "Chrome";
+ case SAFARI:
+ return "Safari";
+ default:
+ NOTREACHED();
+ break;
+ }
+ return "";
+ }
+
+ BrowserKind browser_;
+ std::wstring results_dir_;
+ ScopedHandle browser_handle_;
+ ChromeFrameHTTPServer server_;
+};
+
+// This class sets up event sinks to the IWebBrowser interface. Currently it
+// subscribes to the following events:-
+// 1. DISPID_BEFORENAVIGATE2
+// 2. DISPID_NAVIGATEERROR
+// 3. DISPID_NAVIGATECOMPLETE2
+// Other events can be subscribed to on an if needed basis.
+class WebBrowserEventSink
+ : public CComObjectRootEx<CComSingleThreadModel>,
+ public IDispEventSimpleImpl<0, WebBrowserEventSink,
+ &DIID_DWebBrowserEvents2> {
+ public:
+ WebBrowserEventSink()
+ : navigation_failed_(false),
+ main_thread_id_(0) {
+ }
+
+BEGIN_COM_MAP(WebBrowserEventSink)
+END_COM_MAP()
+
+BEGIN_SINK_MAP(WebBrowserEventSink)
+ SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2,
+ OnBeforeNavigate2, &kBeforeNavigate2Info)
+ SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2,
+ OnNavigateComplete2, &kNavigateComplete2Info)
+ SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATEERROR,
+ OnNavigateError, &kNavigateErrorInfo)
+END_SINK_MAP()
+
+ STDMETHOD_(void, OnNavigateError)(IDispatch* dispatch, VARIANT* url,
+ VARIANT* frame_name, VARIANT* status_code,
+ VARIANT* cancel) {
+ navigation_failed_ = true;
+ }
+
+ STDMETHOD(OnBeforeNavigate2)(IDispatch* dispatch, VARIANT* url, VARIANT*
+ flags, VARIANT* target_frame_name,
+ VARIANT* post_data, VARIANT* headers,
+ VARIANT_BOOL* cancel) {
+ DLOG(INFO) << __FUNCTION__;
+ // If a navigation fails then IE issues a navigation to an interstitial
+ // page. Catch this to track navigation errors as the NavigateError
+ // notification does not seem to fire reliably.
+ GURL crack_url(url->bstrVal);
+ if (crack_url.scheme() == "res") {
+ navigation_failed_ = true;
+ }
+ return S_OK;
+ }
+
+ STDMETHOD_(void, OnNavigateComplete2)(IDispatch* dispatch, VARIANT* url) {
+ DLOG(INFO) << __FUNCTION__;
+ }
+
+ bool navigation_failed() const {
+ return navigation_failed_;
+ }
+
+ void set_main_thread_id(DWORD thread_id) {
+ main_thread_id_ = thread_id;
+ }
+
+ protected:
+ bool navigation_failed_;
+
+ static _ATL_FUNC_INFO kBeforeNavigate2Info;
+ static _ATL_FUNC_INFO kNavigateComplete2Info;
+ static _ATL_FUNC_INFO kNavigateErrorInfo;
+ DWORD main_thread_id_;
+};
+
+#endif // CHROME_FRAME_TEST_CHROME_FRAME_UNITTESTS_H_
+
diff --git a/chrome_frame/test/chrometab_unittests.vsprops b/chrome_frame/test/chrometab_unittests.vsprops
new file mode 100644
index 0000000..d5375c9
--- /dev/null
+++ b/chrome_frame/test/chrometab_unittests.vsprops
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="chrometab_unittests"
+ InheritedPropertySheets="$(SolutionDir)..\tools\grit\build\using_generated_resources.vsprops"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="_ATL_APARTMENT_THREADED;_ATL_CSTRING_EXPLICIT_CONSTRUCTORS"
+ AdditionalIncludeDirectories=""
+ />
+
+</VisualStudioPropertySheet>
diff --git a/chrome_frame/test/com_message_event_unittest.cc b/chrome_frame/test/com_message_event_unittest.cc
new file mode 100644
index 0000000..f850c20
--- /dev/null
+++ b/chrome_frame/test/com_message_event_unittest.cc
@@ -0,0 +1,325 @@
+// Copyright (c) 2009 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/com_message_event.h"
+
+#include <atlbase.h>
+#include <atlcom.h>
+
+#include "testing/gtest/include/gtest/gtest.h"
+
+// To allow the unit test read-only access to check protected member variables.
+class FriendlyComMessageEvent : public ComMessageEvent {
+ public:
+ inline IHTMLEventObj* basic_event() { return basic_event_; }
+};
+
+class ATL_NO_VTABLE MockDumbContainer :
+ public CComObjectRoot,
+ public IOleContainer {
+ public:
+ DECLARE_NOT_AGGREGATABLE(MockDumbContainer)
+ BEGIN_COM_MAP(MockDumbContainer)
+ COM_INTERFACE_ENTRY(IParseDisplayName)
+ COM_INTERFACE_ENTRY(IOleContainer)
+ END_COM_MAP()
+
+ STDMETHOD(ParseDisplayName)(IBindCtx*, LPOLESTR, ULONG*, IMoniker**) {
+ NOTREACHED();
+ return E_NOTIMPL;
+ }
+ STDMETHOD(EnumObjects)(DWORD, IEnumUnknown**) {
+ NOTREACHED();
+ return E_NOTIMPL;
+ }
+ STDMETHOD(LockContainer)(BOOL) {
+ NOTREACHED();
+ return E_NOTIMPL;
+ }
+};
+
+TEST(ComMessageEvent, WithDumbContainer) {
+ CComObject<MockDumbContainer>* container_obj = NULL;
+ CComObject<MockDumbContainer>::CreateInstance(&container_obj);
+ ScopedComPtr<IOleContainer> container(container_obj);
+ EXPECT_FALSE(!container);
+
+ CComObject<FriendlyComMessageEvent>* event_obj = NULL;
+ CComObject<FriendlyComMessageEvent>::CreateInstance(&event_obj);
+ ScopedComPtr<IUnknown> event_ref(event_obj);
+
+ bool result = event_obj->Initialize(container, "hi", "http://www.foo.com/",
+ "message");
+ EXPECT_TRUE(result);
+ EXPECT_TRUE(!event_obj->basic_event());
+}
+
+// Mock object to mimic a "smart" container, e.g. IE, that will
+// be able to return an IHTMLDocument2 and 4, and from which you
+// can get an IHTMLEventObj implementation. Doubles as a mock
+// IHTMLEventObj implementation.
+class ATL_NO_VTABLE MockSmartContainer :
+ public CComObjectRoot,
+ public IOleContainer,
+ public IHTMLDocument2,
+ public IHTMLDocument4,
+ public IHTMLEventObj {
+ public:
+ DECLARE_NOT_AGGREGATABLE(MockSmartContainer)
+ BEGIN_COM_MAP(MockSmartContainer)
+ COM_INTERFACE_ENTRY_IID(IID_IDispatch, IHTMLEventObj)
+ COM_INTERFACE_ENTRY(IParseDisplayName)
+ COM_INTERFACE_ENTRY(IOleContainer)
+ COM_INTERFACE_ENTRY(IHTMLDocument)
+ COM_INTERFACE_ENTRY(IHTMLDocument2)
+ COM_INTERFACE_ENTRY(IHTMLDocument4)
+ COM_INTERFACE_ENTRY(IHTMLEventObj)
+ END_COM_MAP()
+
+ static const DISPID kDispId = 424242;
+ static const long kResultValue = 42;
+
+ // Only method we actually implement from IHTMLDocument4, to give
+ // out the mock IHTMLEventObj.
+ STDMETHOD(createEventObject)(VARIANT*, IHTMLEventObj** event_obj) {
+ return GetUnknown()->QueryInterface(event_obj);
+ }
+
+ // Dummy IDispatch implementation for unit testing, to validate
+ // passthrough semantics.
+ STDMETHOD(GetIDsOfNames)(REFIID iid, LPOLESTR* names, UINT num_names,
+ LCID lcid, DISPID* disp_ids) {
+ DCHECK(num_names == 1);
+ disp_ids[0] = kDispId;
+ return S_OK;
+ }
+
+ STDMETHOD(Invoke)(DISPID id, REFIID iid, LCID lcid, WORD flags,
+ DISPPARAMS* disp_params, VARIANT* var_result,
+ EXCEPINFO* excep_info, UINT* arg_error) {
+ var_result->vt = VT_I4;
+ var_result->lVal = kResultValue;
+ return S_OK;
+ }
+
+
+ // Do-nothing implementation of the rest of the interface methods.
+ // To make this less verbose, define a macro here and undefine it
+ // at the end of the list.
+#define STDMETHODNOTIMP(method, parameters) \
+ STDMETHOD(method) parameters { \
+ NOTREACHED(); \
+ return E_NOTIMPL; \
+ }
+
+ // IDispatch
+ STDMETHODNOTIMP(GetTypeInfoCount, (UINT*));
+ STDMETHODNOTIMP(GetTypeInfo, (UINT, LCID, ITypeInfo**));
+
+ // IParseDisplayName
+ STDMETHODNOTIMP(ParseDisplayName, (IBindCtx*, LPOLESTR, ULONG*, IMoniker**));
+ // IOleContainer
+ STDMETHODNOTIMP(EnumObjects, (DWORD, IEnumUnknown**));
+ STDMETHODNOTIMP(LockContainer, (BOOL));
+ // IHTMLDocument
+ STDMETHODNOTIMP(get_Script, (IDispatch**));
+ // IHTMLDocument2
+ STDMETHODNOTIMP(get_all, (IHTMLElementCollection**));
+ STDMETHODNOTIMP(get_body, (IHTMLElement**));
+ STDMETHODNOTIMP(get_activeElement, (IHTMLElement**));
+ STDMETHODNOTIMP(get_images, (IHTMLElementCollection**));
+ STDMETHODNOTIMP(get_applets, (IHTMLElementCollection**));
+ STDMETHODNOTIMP(get_links, (IHTMLElementCollection**));
+ STDMETHODNOTIMP(get_forms, (IHTMLElementCollection**));
+ STDMETHODNOTIMP(get_anchors, (IHTMLElementCollection**));
+ STDMETHODNOTIMP(put_title, (BSTR));
+ STDMETHODNOTIMP(get_title, (BSTR*));
+ STDMETHODNOTIMP(get_scripts, (IHTMLElementCollection**));
+ STDMETHODNOTIMP(put_designMode, (BSTR));
+ STDMETHODNOTIMP(get_designMode, (BSTR*));
+ STDMETHODNOTIMP(get_selection, (IHTMLSelectionObject**));
+ STDMETHODNOTIMP(get_readyState, (BSTR*));
+ STDMETHODNOTIMP(get_frames, (IHTMLFramesCollection2**));
+ STDMETHODNOTIMP(get_embeds, (IHTMLElementCollection**));
+ STDMETHODNOTIMP(get_plugins, (IHTMLElementCollection**));
+ STDMETHODNOTIMP(put_alinkColor, (VARIANT));
+ STDMETHODNOTIMP(get_alinkColor, (VARIANT*));
+ STDMETHODNOTIMP(put_bgColor, (VARIANT));
+ STDMETHODNOTIMP(get_bgColor, (VARIANT*));
+ STDMETHODNOTIMP(put_fgColor, (VARIANT));
+ STDMETHODNOTIMP(get_fgColor, (VARIANT*));
+ STDMETHODNOTIMP(put_linkColor, (VARIANT));
+ STDMETHODNOTIMP(get_linkColor, (VARIANT*));
+ STDMETHODNOTIMP(put_vlinkColor, (VARIANT));
+ STDMETHODNOTIMP(get_vlinkColor, (VARIANT*));
+ STDMETHODNOTIMP(get_referrer, (BSTR*));
+ STDMETHODNOTIMP(get_location, (IHTMLLocation**));
+ STDMETHODNOTIMP(get_lastModified, (BSTR*));
+ STDMETHODNOTIMP(put_URL, (BSTR));
+ STDMETHODNOTIMP(get_URL, (BSTR*));
+ STDMETHODNOTIMP(put_domain, (BSTR));
+ STDMETHODNOTIMP(get_domain, (BSTR*));
+ STDMETHODNOTIMP(put_cookie, (BSTR));
+ STDMETHODNOTIMP(get_cookie, (BSTR*));
+ STDMETHODNOTIMP(put_expando, (VARIANT_BOOL));
+ STDMETHODNOTIMP(get_expando, (VARIANT_BOOL*));
+ STDMETHODNOTIMP(put_charset, (BSTR));
+ STDMETHODNOTIMP(get_charset, (BSTR*));
+ STDMETHODNOTIMP(put_defaultCharset, (BSTR));
+ STDMETHODNOTIMP(get_defaultCharset, (BSTR*));
+ STDMETHODNOTIMP(get_mimeType, (BSTR*));
+ STDMETHODNOTIMP(get_fileSize, (BSTR*));
+ STDMETHODNOTIMP(get_fileCreatedDate, (BSTR*));
+ STDMETHODNOTIMP(get_fileModifiedDate, (BSTR*));
+ STDMETHODNOTIMP(get_fileUpdatedDate, (BSTR*));
+ STDMETHODNOTIMP(get_security, (BSTR*));
+ STDMETHODNOTIMP(get_protocol, (BSTR*));
+ STDMETHODNOTIMP(get_nameProp, (BSTR*));
+ STDMETHODNOTIMP(write, (SAFEARRAY*));
+ STDMETHODNOTIMP(writeln, (SAFEARRAY*));
+ STDMETHODNOTIMP(open, (BSTR, VARIANT, VARIANT, VARIANT, IDispatch**));
+ STDMETHODNOTIMP(close, ());
+ STDMETHODNOTIMP(clear, ());
+ STDMETHODNOTIMP(queryCommandSupported, (BSTR, VARIANT_BOOL*));
+ STDMETHODNOTIMP(queryCommandEnabled, (BSTR, VARIANT_BOOL*));
+ STDMETHODNOTIMP(queryCommandState, (BSTR, VARIANT_BOOL*));
+ STDMETHODNOTIMP(queryCommandIndeterm, (BSTR, VARIANT_BOOL*));
+ STDMETHODNOTIMP(queryCommandText, (BSTR, BSTR*));
+ STDMETHODNOTIMP(queryCommandValue, (BSTR, VARIANT*));
+ STDMETHODNOTIMP(execCommand, (BSTR, VARIANT_BOOL, VARIANT, VARIANT_BOOL*));
+ STDMETHODNOTIMP(execCommandShowHelp, (BSTR, VARIANT_BOOL*));
+ STDMETHODNOTIMP(createElement, (BSTR, IHTMLElement**));
+ STDMETHODNOTIMP(put_onhelp, (VARIANT));
+ STDMETHODNOTIMP(get_onhelp, (VARIANT*));
+ STDMETHODNOTIMP(put_onclick, (VARIANT));
+ STDMETHODNOTIMP(get_onclick, (VARIANT*));
+ STDMETHODNOTIMP(put_ondblclick, (VARIANT));
+ STDMETHODNOTIMP(get_ondblclick, (VARIANT*));
+ STDMETHODNOTIMP(put_onkeyup, (VARIANT));
+ STDMETHODNOTIMP(get_onkeyup, (VARIANT*));
+ STDMETHODNOTIMP(put_onkeydown, (VARIANT));
+ STDMETHODNOTIMP(get_onkeydown, (VARIANT*));
+ STDMETHODNOTIMP(put_onkeypress, (VARIANT));
+ STDMETHODNOTIMP(get_onkeypress, (VARIANT*));
+ STDMETHODNOTIMP(put_onmouseup, (VARIANT));
+ STDMETHODNOTIMP(get_onmouseup, (VARIANT*));
+ STDMETHODNOTIMP(put_onmousedown, (VARIANT));
+ STDMETHODNOTIMP(get_onmousedown, (VARIANT*));
+ STDMETHODNOTIMP(put_onmousemove, (VARIANT));
+ STDMETHODNOTIMP(get_onmousemove, (VARIANT*));
+ STDMETHODNOTIMP(put_onmouseout, (VARIANT));
+ STDMETHODNOTIMP(get_onmouseout, (VARIANT*));
+ STDMETHODNOTIMP(put_onmouseover, (VARIANT));
+ STDMETHODNOTIMP(get_onmouseover, (VARIANT*));
+ STDMETHODNOTIMP(put_onreadystatechange, (VARIANT));
+ STDMETHODNOTIMP(get_onreadystatechange, (VARIANT*));
+ STDMETHODNOTIMP(put_onafterupdate, (VARIANT));
+ STDMETHODNOTIMP(get_onafterupdate, (VARIANT*));
+ STDMETHODNOTIMP(put_onrowexit, (VARIANT));
+ STDMETHODNOTIMP(get_onrowexit, (VARIANT*));
+ STDMETHODNOTIMP(put_onrowenter, (VARIANT));
+ STDMETHODNOTIMP(get_onrowenter, (VARIANT*));
+ STDMETHODNOTIMP(put_ondragstart, (VARIANT));
+ STDMETHODNOTIMP(get_ondragstart, (VARIANT*));
+ STDMETHODNOTIMP(put_onselectstart, (VARIANT));
+ STDMETHODNOTIMP(get_onselectstart, (VARIANT*));
+ STDMETHODNOTIMP(elementFromPoint, (long, long, IHTMLElement**));
+ STDMETHODNOTIMP(get_parentWindow, (IHTMLWindow2**));
+ STDMETHODNOTIMP(get_styleSheets, (IHTMLStyleSheetsCollection**));
+ STDMETHODNOTIMP(put_onbeforeupdate, (VARIANT));
+ STDMETHODNOTIMP(get_onbeforeupdate, (VARIANT*));
+ STDMETHODNOTIMP(put_onerrorupdate, (VARIANT));
+ STDMETHODNOTIMP(get_onerrorupdate, (VARIANT*));
+ STDMETHODNOTIMP(toString, (BSTR*));
+ STDMETHODNOTIMP(createStyleSheet, (BSTR, long, IHTMLStyleSheet**));
+ // IHTMLDocument4
+ STDMETHODNOTIMP(focus, ());
+ STDMETHODNOTIMP(hasFocus, (VARIANT_BOOL*));
+ STDMETHODNOTIMP(put_onselectionchange, (VARIANT));
+ STDMETHODNOTIMP(get_onselectionchange, (VARIANT*));
+ STDMETHODNOTIMP(get_namespaces, (IDispatch**));
+ STDMETHODNOTIMP(createDocumentFromUrl, (BSTR, BSTR, IHTMLDocument2**));
+ STDMETHODNOTIMP(put_media, (BSTR));
+ STDMETHODNOTIMP(get_media, (BSTR*));
+ STDMETHODNOTIMP(fireEvent, (BSTR, VARIANT*, VARIANT_BOOL*));
+ STDMETHODNOTIMP(createRenderStyle, (BSTR, IHTMLRenderStyle**));
+ STDMETHODNOTIMP(put_oncontrolselect, (VARIANT));
+ STDMETHODNOTIMP(get_oncontrolselect, (VARIANT*));
+ STDMETHODNOTIMP(get_URLUnencoded, (BSTR*));
+ // IHTMLEventObj
+ STDMETHODNOTIMP(get_srcElement, (IHTMLElement**))
+ STDMETHODNOTIMP(get_altKey, (VARIANT_BOOL*));
+ STDMETHODNOTIMP(get_ctrlKey, (VARIANT_BOOL*));
+ STDMETHODNOTIMP(get_shiftKey, (VARIANT_BOOL*));
+ STDMETHODNOTIMP(put_returnValue, (VARIANT));
+ STDMETHODNOTIMP(get_returnValue, (VARIANT*));
+ STDMETHODNOTIMP(put_cancelBubble, (VARIANT_BOOL));
+ STDMETHODNOTIMP(get_cancelBubble, (VARIANT_BOOL*));
+ STDMETHODNOTIMP(get_fromElement, (IHTMLElement**));
+ STDMETHODNOTIMP(get_toElement, (IHTMLElement**));
+ STDMETHODNOTIMP(put_keyCode, (long));
+ STDMETHODNOTIMP(get_keyCode, (long*));
+ STDMETHODNOTIMP(get_button, (long*));
+ STDMETHODNOTIMP(get_type, (BSTR*));
+ STDMETHODNOTIMP(get_qualifier, (BSTR*));
+ STDMETHODNOTIMP(get_reason, (long*));
+ STDMETHODNOTIMP(get_x, (long*));
+ STDMETHODNOTIMP(get_y, (long*));
+ STDMETHODNOTIMP(get_clientX, (long*));
+ STDMETHODNOTIMP(get_clientY, (long*));
+ STDMETHODNOTIMP(get_offsetX, (long*));
+ STDMETHODNOTIMP(get_offsetY, (long*));
+ STDMETHODNOTIMP(get_screenX, (long*));
+ STDMETHODNOTIMP(get_screenY, (long*));
+ STDMETHODNOTIMP(get_srcFilter, (IDispatch**));
+#undef STDMETHODNOTIMP
+};
+
+TEST(ComMessageEvent, WithSmartContainer) {
+ CComObject<MockSmartContainer>* container_obj = NULL;
+ CComObject<MockSmartContainer>::CreateInstance(&container_obj);
+ ScopedComPtr<IOleContainer> container(container_obj);
+ EXPECT_FALSE(!container);
+
+ CComObject<FriendlyComMessageEvent>* event_obj = NULL;
+ CComObject<FriendlyComMessageEvent>::CreateInstance(&event_obj);
+ ScopedComPtr<IUnknown> event_ref(event_obj);
+
+ bool succeeded = event_obj->Initialize(container, "hi",
+ "http://www.foo.com/", "message");
+ EXPECT_TRUE(succeeded);
+ EXPECT_FALSE(!event_obj->basic_event());
+
+ // Name handled natively by CF's ComMessageEvent.
+ DISPID dispid = -1;
+ LPOLESTR name = L"data";
+ HRESULT hr = event_obj->GetIDsOfNames(IID_IDispatch, &name, 1,
+ LOCALE_USER_DEFAULT, &dispid);
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ EXPECT_EQ(dispid, ComMessageEvent::DISPID_MESSAGE_EVENT_DATA);
+
+ // Name not handled by CF's ComMessageEvent.
+ dispid = -1;
+ name = L"nothandledatallbyanyone";
+ hr = event_obj->GetIDsOfNames(IID_IDispatch, &name, 1,
+ LOCALE_USER_DEFAULT, &dispid);
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ EXPECT_EQ(dispid, MockSmartContainer::kDispId);
+
+ // Invoke function handled by ComMessageEvent.
+ CComDispatchDriver dispatcher(event_obj);
+ CComVariant result;
+ hr = dispatcher.GetProperty(ComMessageEvent::DISPID_MESSAGE_EVENT_DATA,
+ &result);
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ EXPECT_EQ(result.vt, VT_BSTR);
+ EXPECT_EQ(wcscmp(result.bstrVal, L"hi"), 0);
+
+ // And now check passthrough.
+ result.Clear();
+ hr = dispatcher.GetProperty(MockSmartContainer::kDispId, &result);
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ EXPECT_EQ(result.vt, VT_I4);
+ EXPECT_EQ(result.lVal, MockSmartContainer::kResultValue);
+}
diff --git a/chrome_frame/test/data/CFInstance_basic_frame.html b/chrome_frame/test/data/CFInstance_basic_frame.html
new file mode 100644
index 0000000..11938ee
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_basic_frame.html
@@ -0,0 +1,8 @@
+<html>
+ <head>
+ <title></title>
+ </head>
+ <body>
+ <h1>do nothing</h1>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/CFInstance_basic_host.html b/chrome_frame/test/data/CFInstance_basic_host.html
new file mode 100644
index 0000000..6bad61c
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_basic_host.html
@@ -0,0 +1,55 @@
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceBasic";
+ (function(){
+ try{
+ var cf = new CFInstance({
+ src: "CFInstance_basic_frame.html",
+ node: "toBeReplaced"
+ });
+
+ if (document.getElementById("parent") != cf.parentNode ) {
+ onFailure(testName, 1, "parent node mismatch");
+ return;
+ }
+
+ if (document.getElementById("prev").nextSibling != cf) {
+ onFailure(testName, 1, "sibling node mismatch");
+ return;
+ }
+
+ if (document.getElementById("after").previousSibling != cf) {
+ onFailure(testName, 1, "sibling node mismatch");
+ return;
+ }
+
+ onSuccess(testName, 1);
+
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: "+e);
+ }
+ })();
+ </script>
+ <p>Tests ChromeFrame Navigation</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_default_ctor_host.html b/chrome_frame/test/data/CFInstance_default_ctor_host.html
new file mode 100644
index 0000000..744c6de
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_default_ctor_host.html
@@ -0,0 +1,45 @@
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceDefaultCtor";
+ (function(){
+ try{
+ var cf = new CFInstance();
+ cf.src = "CFInstance_basic_frame.html";
+ var node = document.getElementById("toBeReplaced");
+ node.parentNode.replaceChild(cf, node);
+ var timer = setTimeout(function() {
+ onFailure(testName, 1, "CFInstance navigation timeout");
+ }, 15000);
+ cf.listen("load", function() {
+ clearTimeout(timer);
+ onSuccess(testName, 1);
+ });
+ cf.src = "CFInstance_basic_frame.html";
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: "+e);
+ }
+ })();
+ </script>
+ <p>Tests Chrome Frame constructor without arguments</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_delay_host.html b/chrome_frame/test/data/CFInstance_delay_host.html
new file mode 100644
index 0000000..f6d9e02
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_delay_host.html
@@ -0,0 +1,47 @@
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceDelay";
+ (function(){
+ try{
+ var cf = new CFInstance({
+ onload: function() {
+ onSuccess(testName, 1);
+ },
+ src: "CFInstance_basic_frame.html"
+ });
+
+ setTimeout(function() {
+ var replNode = document.getElementById("toBeReplaced");
+ // impedence matching between new and old CFInstance.js
+ var node = cf["plugin"] ? cf.plugin : cf;
+ replNode.parentNode.replaceChild(node, replNode);
+ }, 100);
+
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: "+e);
+ }
+ })();
+ </script>
+ <p>Tests ChromeFrame Navigation when placed in the document on a delay</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_fallback_host.html b/chrome_frame/test/data/CFInstance_fallback_host.html
new file mode 100644
index 0000000..5939180
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_fallback_host.html
@@ -0,0 +1,44 @@
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceFallback";
+ (function() {
+ try {
+ var cf = new CFInstance({
+ node: "toBeReplaced",
+ src: "CFInstance_basic_frame.html",
+ requirements: []
+ });
+
+ if (cf.tagName.toLowerCase() == "iframe") {
+ onSuccess(testName, 1);
+ } else {
+ onFailure(testName, 1, "expected tagName mismatch");
+ }
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: "+e);
+ }
+ })();
+ </script>
+ <p>Tests ChromeFrame fallback</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_iframe_onload_host.html b/chrome_frame/test/data/CFInstance_iframe_onload_host.html
new file mode 100644
index 0000000..913c462
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_iframe_onload_host.html
@@ -0,0 +1,41 @@
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceIfrOnload";
+ (function() {
+ try {
+ var cf = new CFInstance({
+ node: "toBeReplaced",
+ src: "CFInstance_basic_frame.html",
+ onload: function() {
+ onSuccess(testName, 1);
+ },
+ requirements: [] // always use an iframe
+ });
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: "+e);
+ }
+ })();
+ </script>
+ <p>Tests CFInstance event handling on iframes</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_iframe_post_host.html b/chrome_frame/test/data/CFInstance_iframe_post_host.html
new file mode 100644
index 0000000..f503522
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_iframe_post_host.html
@@ -0,0 +1,50 @@
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceIfrPost";
+ (function() {
+ if (!CFInstance.contentTests.postMessage()) {
+ onSuccess(testName, 1);
+ } else {
+ try {
+ var cf = new CFInstance({
+ node: "toBeReplaced",
+ src: "CFInstance_post_frame.html",
+ onload: function() {
+ cf.postMessage("howdy!");
+ },
+ onmessage: function(evt) {
+ if (evt.data == 'hola!') {
+ onSuccess(testName, 1);
+ }
+ },
+ requirements: [] // always use an iframe
+ });
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: "+e);
+ }
+ }
+ })();
+ </script>
+ <p>Tests CFInstance event handling on iframes</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_no_src_host.html b/chrome_frame/test/data/CFInstance_no_src_host.html
new file mode 100644
index 0000000..379e26d
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_no_src_host.html
@@ -0,0 +1,43 @@
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceNoSrc";
+ (function() {
+ try {
+ var cf = new CFInstance({
+ src: "",
+ node: "toBeReplaced"
+ });
+
+ // check that we got "src" set to "about:blank" by CFInstance
+ if (cf.src == "about:blank") {
+ onSuccess(testName, 1);
+ } else {
+ onFailure(testName, 1, "blank URL mismatch");
+ }
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: "+e);
+ }
+ })();
+ </script>
+ <p>Tests ChromeFrame with blank src</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/CFInstance_post_frame.html b/chrome_frame/test/data/CFInstance_post_frame.html
new file mode 100644
index 0000000..8055cb2
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_post_frame.html
@@ -0,0 +1,26 @@
+<!-- saved from url=(0014)about:internet -->
+<html>
+<!-- This page is meant to load inside ChromeFrame -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ <script>
+ var cf = CFInstance;
+
+ cf.listen("load", function() {
+ cf.postMessage("hola!");
+ })
+ </script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <p>ChromeFrame + CFInstance PostMessage Test
+ <br>Test for PostMessage from the host browser to iframe and back</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/CFInstance_post_host.html b/chrome_frame/test/data/CFInstance_post_host.html
new file mode 100644
index 0000000..09402ac
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_post_host.html
@@ -0,0 +1,47 @@
+<!DOCTYPE html>
+
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstancePost";
+ (function() {
+ try {
+ var cf = new CFInstance({
+ node: "toBeReplaced",
+ src: "CFInstance_post_frame.html",
+ onload: function() {
+ cf.postMessage("howdy!");
+ },
+ onmessage: function(evt) {
+ if (evt.data == "hola!") {
+ onSuccess(testName, 1);
+ }
+ }
+ });
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: " + e);
+ }
+ })();
+ </script>
+ <p>Tests CFInstance event handling on iframes</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_rpc_frame.html b/chrome_frame/test/data/CFInstance_rpc_frame.html
new file mode 100644
index 0000000..a7dbfd7
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_rpc_frame.html
@@ -0,0 +1,27 @@
+<!DOCTYPE html>
+
+<html>
+<!-- This page is meant to load inside ChromeFrame -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ <script>
+ var cf = CFInstance;
+ cf.rpc.expose("rpcHandler", function(arg) {
+ cf.rpc.callRemote("handleCallback", ["hola!"]);
+ });
+ cf.rpc.init();
+ </script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <p>ChromeFrame + CFInstance PostMessage Test
+ <br>Test for PostMessage from the host browser to iframe and back</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/CFInstance_rpc_host.html b/chrome_frame/test/data/CFInstance_rpc_host.html
new file mode 100644
index 0000000..60680cf
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_rpc_host.html
@@ -0,0 +1,51 @@
+<!DOCTYPE html>
+
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceRPC";
+ (function() {
+ try {
+ var cf = new CFInstance({
+ node: "toBeReplaced",
+ src: "CFInstance_rpc_frame.html"
+ });
+
+ var handleCallback = function(arg) {
+ // alert(arg);
+ if (arg == "hola!") {
+ onSuccess(testName, 1);
+ }
+ };
+
+ cf.rpc.expose("handleCallback", handleCallback);
+ cf.rpc.init();
+
+ cf.rpc.callRemote("rpcHandler", ["whatcho talkin 'bout, willis!?"]);
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: " + e);
+ }
+ })();
+ </script>
+ <p>Tests CFInstance event handling on iframes</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_rpc_internal_frame.html b/chrome_frame/test/data/CFInstance_rpc_internal_frame.html
new file mode 100644
index 0000000..8208269
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_rpc_internal_frame.html
@@ -0,0 +1,28 @@
+<!DOCTYPE html>
+
+<html>
+<!-- This page is meant to load inside ChromeFrame -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ <script>
+ setTimeout(rpcCall, 10000);
+ function rpcCall() {
+ var cf = CFInstance;
+ cf.rpc.callRemote("callback");
+ cf.rpc.init();
+ }
+ </script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <p>ChromeFrame + CFInstance PostMessage Test
+ <br>Test for PostMessage from the host browser to iframe and back</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/CFInstance_rpc_internal_host.html b/chrome_frame/test/data/CFInstance_rpc_internal_host.html
new file mode 100644
index 0000000..f350871
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_rpc_internal_host.html
@@ -0,0 +1,45 @@
+<!DOCTYPE html>
+
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceRPCInternal";
+ (function() {
+ try {
+ var cf = new CFInstance({
+ node: "toBeReplaced",
+ src: "CFInstance_rpc_internal_frame.html"
+ });
+
+
+ cf.rpc.expose("callback", function(arg) {
+ onSuccess(testName, 1);
+ });
+ cf.rpc.init();
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: " + e);
+ }
+ })();
+ </script>
+ <p>Tests CFInstance event handling on iframes</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_singleton_frame.html b/chrome_frame/test/data/CFInstance_singleton_frame.html
new file mode 100644
index 0000000..1ab0302
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_singleton_frame.html
@@ -0,0 +1,20 @@
+<html>
+ <head>
+ <title>talk to me...</title>
+ <script type="text/javascript" src="CFInstance.js"></script>
+ <script type="text/javascript">
+ CFInstance.listen("load", function() {
+ document.body.innerHTML = "sending 'foo'";
+ CFInstance.postMessage("foo");
+ document.body.innerHTML = "...waiting...";
+ });
+ CFInstance.listen("message", function(evt) {
+ document.body.innerHTML = "sending 'baz'";
+ CFInstance.postMessage("baz");
+ });
+ </script>
+ </head>
+ <body>
+ <h1>sends a message to the parent</h1>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/CFInstance_singleton_host.html b/chrome_frame/test/data/CFInstance_singleton_host.html
new file mode 100644
index 0000000..3eda108
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_singleton_host.html
@@ -0,0 +1,44 @@
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript" src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="toBeReplaced">
+ fallback content goes here
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceSingleton";
+ (function() {
+ try{
+ var cf = new CFInstance({
+ src: "CFInstance_singleton_frame.html",
+ node: "toBeReplaced"
+ });
+
+ // test a call/response set of actions driven by the CF content
+ cf.listen("message", function(evt) {
+ if (evt.data == "foo") {
+ cf.postMessage("bar");
+ } else if(evt.data == "baz") {
+ onSuccess(testName, 1);
+ }
+ });
+
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: " + e);
+ }
+ })();
+ </script>
+ <p>Tests ChromeFrame Navigation</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/CFInstance_zero_size_host.html b/chrome_frame/test/data/CFInstance_zero_size_host.html
new file mode 100644
index 0000000..94b35c0
--- /dev/null
+++ b/chrome_frame/test/data/CFInstance_zero_size_host.html
@@ -0,0 +1,41 @@
+<html>
+ <!-- This page is meant to loaded inside the host browser (IE, FF, etc.) -->
+ <head>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+ <script type="text/javascript"
+ src="CFInstance.js"></script>
+ </head>
+
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <div id="parent">
+ <div id="prev">before</div><div id="toBeReplaced">
+ fallback content goes here
+ </div><div id="after">after</div>
+ </div>
+ <script type="text/javascript">
+ var testName = "CFInstanceZeroSize";
+ (function() {
+ try {
+ var cf = new CFInstance({
+ node: "toBeReplaced",
+ src: "CFInstance_basic_frame.html",
+ cssText: "width: 0px; height: 0px;",
+ onload: function() {
+ onSuccess(testName, 1);
+ }
+ });
+ } catch (e) {
+ onFailure(testName, 1,
+ "CFInstance constructor failed with error: "+e);
+ }
+ })();
+ </script>
+ <p>Tests CFInstance event handling on iframes</p>
+ </body>
+</html>
+
diff --git a/chrome_frame/test/data/back_to_ie.html b/chrome_frame/test/data/back_to_ie.html
new file mode 100644
index 0000000..ad4b61d
--- /dev/null
+++ b/chrome_frame/test/data/back_to_ie.html
@@ -0,0 +1,21 @@
+<html>
+ <head><title>Back to IE</title>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+
+ <script type="text/javascript">
+ function test() {
+ var test_name = 'navigate_out';
+ if (isRunningInMSIE()) {
+ onSuccess(test_name, 1);
+ } else {
+ onFailure(test_name, 1, 'Failed');
+ }
+ }
+ </script>
+ </head>
+ <body onLoad="test();">
+ <h2>Redirected!</h2>
+ <p>This page should have loaded in IE</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/cf_protocol.html b/chrome_frame/test/data/cf_protocol.html
new file mode 100644
index 0000000..0036555
--- /dev/null
+++ b/chrome_frame/test/data/cf_protocol.html
@@ -0,0 +1,20 @@
+<html>
+ <head><title>cf: protocol test</title>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+
+ <script type="text/javascript">
+ function test() {
+ if (isRunningInMSIE()) {
+ reloadUsingCFProtocol();
+ } else {
+ onSuccess("chrome_frame_protocol", 1);
+ }
+ }
+ </script>
+ </head>
+ <body onLoad="setTimeout(test, 100);">
+ <h2>Prepare to be redirected!</h2>
+ <p>Redirects the same page to its 'cf:' version</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/chrome_frame_mime_filter_test.html b/chrome_frame/test/data/chrome_frame_mime_filter_test.html
new file mode 100644
index 0000000..7520758
--- /dev/null
+++ b/chrome_frame/test/data/chrome_frame_mime_filter_test.html
@@ -0,0 +1,32 @@
+<!-- saved from url=(0014)about:internet -->
+<!-- Note that for the above Mark of the Web to work, the comment must
+ be followed by a CR/LF ending, so please do not change the line endings. -->
+<html>
+<!-- This page is meant to load inside a host browser like IE/FF -->
+<head>
+<meta http-equiv="X-UA-Compatible" content="chrome=1"/>
+<script type="text/javascript" src="chrome_frame_tester_helpers.js"></script>
+<script type="text/javascript">
+
+function TestIfRunningInChrome() {
+ var is_chrome = /chrome/.test(navigator.userAgent.toLowerCase());
+ if (is_chrome) {
+ onSuccess("MIMEFilter", "MIME filter worked!");
+ } else {
+ onFailure("MIMEFilter", "MIME filter failed :-(",
+ "User agent = " + navigator.userAgent.toLowerCase());
+ }
+}
+
+</script>
+</head>
+
+<body onload="TestIfRunningInChrome();">
+<div id="statusPanel" style="border: 1px solid red; width: 100%">
+Test running....
+</div>
+
+<p>
+
+</body>
+</html> \ No newline at end of file
diff --git a/chrome_frame/test/data/chrome_frame_resize.html b/chrome_frame/test/data/chrome_frame_resize.html
new file mode 100644
index 0000000..afba53b
--- /dev/null
+++ b/chrome_frame/test/data/chrome_frame_resize.html
@@ -0,0 +1,138 @@
+<!-- saved from url=(0014)about:internet -->
+<!-- Please preserve the CR/LF at the end of the previous line. -->
+<html>
+<!-- This page is meant to load inside the host browser like IE/FF -->
+<head>
+<script type="text/javascript" src="chrome_frame_tester_helpers.js"></script>
+<script type="text/javascript">
+function onLoad() {
+ var chromeFrame = GetChromeFrame();
+ chromeFrame.onmessage = OnChromeFrameResize;
+ setTimeout(NavigateToURL, 100);
+}
+
+function NavigateToURL() {
+ var chromeFrame = GetChromeFrame();
+ chromeFrame.src = "chrome_frame_resize_hosted.html";
+ setTimeout(CheckIfNavigationFailed, 15000);
+}
+
+var navigation_success = false;
+
+function CheckIfNavigationFailed() {
+ if (!navigation_success) {
+ onFailure("Resize", 1, "ChromeFrame Navigation failed");
+ }
+}
+
+function OnNavigationSucceeded() {
+ navigation_success = true;
+ appendStatus("ChromeFrame hosted page loaded, beginning test...");
+ setTimeout(ResizeChromeFrame, 100);
+}
+
+var resize_step = 0;
+
+function ResizeChromeFrame() {
+ var chromeFrame = GetChromeFrame();
+
+ if (resize_step == 0) {
+ appendStatus("Setting chromeFrame to 100x100");
+ resize_step = 1;
+ chromeFrame.width = 100;
+ setTimeout("OnResizeFailure(0)", 2000);
+ } else if (resize_step == 1) {
+ resize_step = 2;
+ chromeFrame.height = 100;
+ setTimeout("OnResizeFailure(1)", 2000);
+ } else if (resize_step == 2) {
+ appendStatus("Setting chromeFrame to 10x10");
+ resize_step = 3;
+ chromeFrame.width = 10;
+ setTimeout("OnResizeFailure(0)", 2000);
+ } else if (resize_step == 3) {
+ resize_step = 4;
+ chromeFrame.height = 10;
+ setTimeout("OnResizeFailure(1)", 2000);
+ }
+
+ // Note that setting the ChromeFrame window to 0x0 (or < 2x2 if we have the
+ // WS_BORDER style defined on our window) currently results
+ // in a check failure from the child chrome.exe process.
+ // TODO(robertshield): Figure out why and fix it.
+}
+
+var resize_step_received = 0;
+
+function OnChromeFrameResize(evt) {
+ resize_step_received++;
+ appendStatus("ChromeFrame resized: " + evt.data + "step=" +
+ resize_step_received);
+
+ if (resize_step == 4) {
+ onSuccess("Resize", 1);
+ } else {
+ setTimeout(ResizeChromeFrame, 100);
+ }
+}
+
+function OnResizeFailure(step) {
+ // It turns out that the hosted page gets two calls to onresize()
+ // every time a single size parameter (i.e. width OR height) is changed.
+ // As such this check doesn't quite guarantee success, but if it fails,
+ // then we should fail the unit test.
+ if (step >= resize_step_received) {
+ onFailure("Resize", 1, "Did not receive resize reply back from frame.");
+ }
+}
+
+function GetChromeFrame() {
+ return window.document.ChromeFrame;
+}
+
+var debug_counter = 0;
+
+function DebugResizeChromeFrame(delta) {
+ var chromeFrame = GetChromeFrame();
+ var newWidth = chromeFrame.clientWidth + delta;
+ var newHeight = chromeFrame.clientHeight + delta;
+
+ appendStatus(debug_counter + ". DEBUG resizing CF to (" + newWidth + "," +
+ newHeight + ")");
+
+ debug_counter++;
+
+ chromeFrame.width = newWidth;
+ chromeFrame.height = newHeight;
+}
+
+</script>
+</head>
+
+<body onload="onLoad();">
+<div id="description" style="border: 2px solid black; width: 100%">
+ Test for resizing the chrome frame control.
+</div>
+<div id="statusPanel" style="border: 1px solid red; width: 100%">
+Test running....
+</div>
+
+<object id="ChromeFrame" codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A"
+ style="border: 1px solid blue">
+ <param name="onload" value="return OnNavigationSucceeded();" />
+ <embed id="ChromeFramePlugin" name="ChromeFrame"
+ onload="return OnNavigationSucceeded();"
+ type="application/chromeframe"
+ style="border: 1px solid green">
+ </embed>
+</object>
+<br />
+<br />
+
+<button onclick="javascript:DebugResizeChromeFrame(20);">Bigger</button>
+<button onclick="javascript:DebugResizeChromeFrame(-20);">Smaller</button>
+
+</body>
+</html>
+
diff --git a/chrome_frame/test/data/chrome_frame_resize_hosted.html b/chrome_frame/test/data/chrome_frame_resize_hosted.html
new file mode 100644
index 0000000..95528ec
--- /dev/null
+++ b/chrome_frame/test/data/chrome_frame_resize_hosted.html
@@ -0,0 +1,48 @@
+<html>
+<!-- This page is meant to load inside ChromeFrame -->
+<head>
+
+<script type="text/javascript" src="chrome_frame_tester_helpers.js"></script>
+<script type="text/javascript">
+
+function onLoad() {
+ var host = window.externalHost;
+ if (host) {
+ host.postMessage(
+ "ChromeFrame navigated to: " + window.location);
+ } else {
+ appendStatus("Running in non-hosted mode");
+ }
+}
+
+var resize_event_counter = 0;
+
+function OnResizeEvent() {
+ width = window.innerWidth;
+ height = window.innerHeight;
+
+ appendStatus(resize_event_counter + ". Resized to (" + width +
+ "," + height + ")");
+
+ var host = window.externalHost;
+ if (host) {
+ host.postMessage(
+ resize_event_counter + ":(" + width + "," + height + ")");
+ } else {
+ appendStatus("window.externalHost is null!");
+ }
+}
+</script>
+</head>
+
+<body onload="onLoad();" bgcolor="#999999" onresize="OnResizeEvent();">
+<div id="description" style="border: 2px solid black; width: 100%">
+ Hosted resize test component.
+</div>
+
+<div id="statusPanel" style="border: 1px solid red; width: 100%">
+Test running....
+</div>
+
+</body>
+</html>
diff --git a/chrome_frame/test/data/chrome_frame_tester_helpers.js b/chrome_frame/test/data/chrome_frame_tester_helpers.js
new file mode 100644
index 0000000..1c914ee
--- /dev/null
+++ b/chrome_frame/test/data/chrome_frame_tester_helpers.js
@@ -0,0 +1,142 @@
+//
+// This script provides some mechanics for testing ChromeFrame
+//
+function onSuccess(name, id) {
+ onFinished(name, id, "OK");
+}
+
+function onFailure(name, id, status) {
+ onFinished(name, id, status);
+}
+
+function getXHRObject(){
+ var XMLHTTP_PROGIDS = ['Msxml2.XMLHTTP', 'Microsoft.XMLHTTP',
+ 'Msxml2.XMLHTTP.4.0'];
+ var http = null;
+ try {
+ http = new XMLHttpRequest();
+ } catch(e) {
+ }
+
+ if (http)
+ return http;
+
+ for (var i = 0; i < 3; ++i) {
+ var progid = XMLHTTP_PROGIDS[i];
+ try {
+ http = new ActiveXObject(progid);
+ } catch(e) {
+ }
+
+ if (http)
+ break;
+ }
+ return http;
+}
+
+var reportURL = "/writefile/";
+
+function shutdownServer() {
+ var xhr = getXHRObject();
+ if(!xhr)
+ return;
+
+ xhr.open("POST", "/kill", false);
+ try {
+ xhr.send(null);
+ } catch(e) {
+ appendStatus("XHR send failed. Error: " + e.description);
+ }
+}
+
+// Optionally send the server a notification that onload was fired.
+// To be called from within window.onload.
+function sendOnLoadEvent() {
+ writeToServer("OnLoadEvent", "loaded");
+}
+
+function writeToServer(name, result) {
+ var xhr = getXHRObject();
+ if(!xhr)
+ return;
+
+ // synchronously POST the results
+ xhr.open("POST", reportURL + name, false);
+ xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
+ try {
+ xhr.send(result);
+ } catch(e) {
+ appendStatus("XHR send failed. Error: " + e.description);
+ }
+}
+
+function postResult(name, result) {
+ writeToServer(name, result);
+ // NOTE:
+ // not watching for failure or return status issues. What should we do here?
+ shutdownServer();
+}
+
+// Finish running a test by setting the status
+// and the cookie.
+function onFinished(name, id, result) {
+ appendStatus(result);
+
+ // set a cookie to report the results...
+ var cookie = name + "." + id + ".status=" + result + "; path=/";
+ document.cookie = cookie;
+
+ // ...and POST the status back to the server
+ postResult(name, result);
+}
+
+function appendStatus(message) {
+ var statusPanel = document.getElementById("statusPanel");
+ if (statusPanel) {
+ statusPanel.innerHTML += '<BR>' + message;
+ }
+}
+
+function readCookie(name) {
+ var cookie_name = name + "=";
+ var ca = document.cookie.split(';');
+
+ for(var i = 0 ; i < ca.length ; i++) {
+ var c = ca[i];
+ while (c.charAt(0) == ' ') {
+ c = c.substring(1,c.length);
+ }
+ if (c.indexOf(cookie_name) == 0) {
+ return c.substring(cookie_name.length, c.length);
+ }
+ }
+ return null;
+}
+
+function createCookie(name,value,days) {
+ var expires = "";
+ if (days) {
+ var date = new Date();
+ date.setTime(date.getTime() + (days * 24 * 60 * 60 * 1000));
+ expires = "; expires=" + date.toGMTString();
+ }
+ document.cookie = name+"="+value+expires+"; path=/";
+}
+
+function eraseCookie(name) {
+ createCookie(name, "", -1);
+}
+
+function isRunningInMSIE() {
+ if (/MSIE (\d+\.\d+);/.test(navigator.userAgent))
+ return true;
+
+ return false;
+}
+
+function reloadUsingCFProtocol() {
+ var redirect_location = "cf:";
+ redirect_location += window.location;
+ window.location = redirect_location;
+}
+
diff --git a/chrome_frame/test/data/event_listener.html b/chrome_frame/test/data/event_listener.html
new file mode 100644
index 0000000..6fbd158
--- /dev/null
+++ b/chrome_frame/test/data/event_listener.html
@@ -0,0 +1,42 @@
+<html>
+<!-- This page is meant to load inside the host browser like IE/FF -->
+<head>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js"></script>
+ <script language="javascript">
+ var g_test_name = 'EventListener';
+
+ function onChromeFrameLoaded() {
+ appendStatus('Chrome frame loaded.');
+ onSuccess(g_test_name, 1);
+ }
+
+ function onEventNotFired() {
+ onFailure(g_test_name, 1, 'Did not receive onload event');
+ }
+
+ function onDocumentLoad() {
+ appendStatus('document loaded');
+ var cf = getCf();
+ cf.addEventListener("load", onChromeFrameLoaded, false);
+ setTimeout(onEventNotFired, 10000)
+ cf.src = "CFInstance_basic_frame.html";
+ }
+
+ function getCf() {
+ return window.document.ChromeFrame;
+ }
+ </script>
+</head>
+<body onload="onDocumentLoad();">
+ <object id="ChromeFrame" width="500" height ="300"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <embed id="ChromeFramePlugin" name="ChromeFrame" width="500"
+ height="500" type="application/chromeframe">
+ </embed>
+ </object>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+</body>
+</html>
diff --git a/chrome_frame/test/data/iframe_basic_host.html b/chrome_frame/test/data/iframe_basic_host.html
new file mode 100644
index 0000000..f9a4c0c
--- /dev/null
+++ b/chrome_frame/test/data/iframe_basic_host.html
@@ -0,0 +1,13 @@
+<!-- saved from url=(0014)about:internet -->
+<html>
+<!-- This page is meant to load inside the host browser like IE/FF -->
+<head></head>
+<body>
+ <iframe src ="postmessage_basic_host.html" width="50%" height="600">
+ </iframe>
+
+ <br>
+ <br>
+ Test for ChromeFrame inside iframe
+</body>
+</html>
diff --git a/chrome_frame/test/data/in_head.html b/chrome_frame/test/data/in_head.html
new file mode 100644
index 0000000..a093c71
--- /dev/null
+++ b/chrome_frame/test/data/in_head.html
@@ -0,0 +1,62 @@
+<html>
+ <!-- This page is meant to load inside the host browser like IE/FF -->
+ <head><title>Initialize hidden chrome frame</title>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript">
+ var g_failure_timeout = null;
+ var g_test_id = 1;
+ var g_test_name = 'InHead';
+ var g_cf3_loaded = false;
+
+ function OnNavigationFailed() {
+ onFailure(g_test_name, g_test_id, 'ChromeFrame Navigation failed');
+ }
+
+ function OnObjectFocusFailed() {
+ appendStatus('chrome frame focus failed');
+ onFailure(g_test_name, g_test_id, 'Embed in head test failed');
+ }
+
+ function OnFrameMessage(evt) {
+ appendStatus('Chrome frame visible and focused');
+ if (evt.data == 'btnOnFocus') {
+ window.clearTimeout(g_failure_timeout);
+ g_failure_timeout = null;
+ appendStatus('Chrome frame visible and focused');
+
+ onSuccess(g_test_name, g_test_id);
+ }
+ }
+
+ function OnFrameLoad() {
+ document.ChromeFrame.focus();
+ }
+
+ function OnLoad() {
+ g_failure_timeout = window.setTimeout(OnObjectFocusFailed, 10000);
+ }
+ </script>
+ <object id="ChromeFrame" width="300" height="80" tabindex="0"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <param name="src" value="simple_object_focus_cf.html">
+ <param name="onload" value="OnFrameLoad();">
+ <param name="onloaderror" value="OnNavigationFailed();">
+ <param name="onmessage" value="OnFrameMessage(arguments[0]);">
+ <embed width="300" height="80" name="ChromeFrame"
+ type="application/chromeframe"
+ src="simple_object_focus_cf.html"
+ onload="OnFrameLoad();"
+ onloaderror="OnNavigationFailed();"
+ onmessage="OnFrameMessage(arguments[0]);">
+ </embed>
+ </object>
+ </head>
+ <body onload = "OnLoad();">
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+ <div id = "dummy"> </div>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/initialize_hidden.html b/chrome_frame/test/data/initialize_hidden.html
new file mode 100644
index 0000000..2da0917
--- /dev/null
+++ b/chrome_frame/test/data/initialize_hidden.html
@@ -0,0 +1,120 @@
+<html>
+ <!-- This page is meant to load inside the host browser like IE/FF -->
+ <head><title>Initialize hidden chrome frame</title>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript">
+ var g_failure_timeout = null;
+ var g_test_id = 1;
+ var g_test_name = 'InitializeHidden';
+ var g_cf3_loaded = false;
+
+ function OnNavigationFailed() {
+ onFailure(g_test_name, g_test_id, 'ChromeFrame Navigation failed');
+ }
+
+ function OnObjectFocusFailed() {
+ appendStatus('chrome frame focus failed');
+ onFailure(g_test_name, g_test_id, 'Visibility test failed');
+ }
+
+ function OnCF1Loaded() {
+ appendStatus('Chrome frame 1 loaded, not visible yet.');
+ try {
+ // Make chrome frame visible
+ var cf1 = document.getElementById('CFDiv1');
+ cf1.style.visibility = 'visible';
+ appendStatus('Chrome frame 1 visibility - ' + cf1.style.visibility);
+ // Set focus to chrome frame. This should set focus to the
+ // first element inside the frame, which a script inside the
+ // page will detect and notify us back by sending us a message.
+ document.ChromeFrame1.focus();
+ g_failure_timeout = window.setTimeout(OnObjectFocusFailed, 10000);
+ } catch(e) {
+ appendStatus('Error setting focus to CF1. Error: ' + e.description);
+ onFailure(g_test_name, g_test_id, 'CF1 focus() error');
+ }
+ }
+
+ function OnCF1Message(evt) {
+ if (evt.data == 'btnOnFocus') {
+ window.clearTimeout(g_failure_timeout);
+ g_failure_timeout = null;
+ appendStatus('CF1 visible and focused');
+
+ // Now make second chrome frame instance visible
+ document.getElementById('CFDiv2').style.display = 'block';
+ appendStatus('Chrome frame 2 visible, should start loading now');
+ g_failure_timeout = window.setTimeout(OnObjectFocusFailed, 10000);
+ }
+ }
+
+ function OnCF2Loaded() {
+ appendStatus('Chrome frame 2 loaded');
+ try {
+ // Set focus to chrome frame. This should set focus to the
+ // first element inside the frame, which a script inside the
+ // page will detect and notify us back by sending us a message.
+ // We set focus on a timeout as on IE it takes a while for the window
+ // to become visible.
+ setTimeout(SetFocusToCF2, 100);
+ } catch(e) {
+ appendStatus('Error setting focus to CF2. Error: ' + e.description);
+ onFailure(g_test_name, g_test_id, 'CF2 focus() error');
+ }
+ }
+
+ function SetFocusToCF2() {
+ document.ChromeFrame2.focus();
+ }
+
+ function OnCF2Message(evt) {
+ if (evt.data == 'btnOnFocus') {
+ window.clearTimeout(g_failure_timeout);
+ g_failure_timeout = null;
+ appendStatus('CF2 visible and focused');
+ onSuccess(g_test_name, g_test_id);
+ }
+ }
+ </script>
+ </head>
+ <body>
+ <div id="CFDiv1" style="visibility: hidden;">
+ <object id="ChromeFrame1" width="300" height="80" tabindex="0"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <param name="src" value="simple_object_focus_cf.html">
+ <param name="onload" value="OnCF1Loaded();">
+ <param name="onloaderror" value="OnNavigationFailed();">
+ <param name="onmessage" value="OnCF1Message(arguments[0]);">
+ <embed width="300" height="80" name="ChromeFrame1"
+ type="application/chromeframe"
+ src="simple_object_focus_cf.html"
+ onload="OnCF1Loaded();"
+ onloaderror="OnNavigationFailed();"
+ onmessage="OnCF1Message(arguments[0]);">
+ </embed>
+ </object>
+ </div>
+ <div id="CFDiv2" style="display: none;">
+ <object id="ChromeFrame2" width="300" height="80" tabindex="1"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <param name="src" value="simple_object_focus_cf.html">
+ <param name="onload" value="OnCF2Loaded();">
+ <param name="onloaderror" value="OnNavigationFailed();">
+ <param name="onmessage" value="OnCF2Message(arguments[0]);">
+ <embed width="300" height="80" name="ChromeFrame2"
+ type="application/chromeframe"
+ src="simple_object_focus_cf.html"
+ onload="OnCF2Loaded();"
+ onloaderror="OnNavigationFailed();"
+ onmessage="OnCF2Message(arguments[0]);">
+ </embed>
+ </object>
+ </div>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/meta_tag.html b/chrome_frame/test/data/meta_tag.html
new file mode 100644
index 0000000..9b17406
--- /dev/null
+++ b/chrome_frame/test/data/meta_tag.html
@@ -0,0 +1,21 @@
+<html>
+ <head>
+ <meta http-equiv="x-ua-compatible" content="chrome=1" />
+ <title>Load chrome frame using meta tag</title>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+
+ <script type="text/javascript">
+ function test() {
+ if (isRunningInMSIE()) {
+ onFailure("meta_tag", 1, "Failed");
+ } else {
+ onSuccess("meta_tag", 1);
+ }
+ }
+ </script>
+ </head>
+ <body onLoad="setTimeout(test, 100);">
+ chrome trame in tab mode
+ </body>
+</html>
diff --git a/chrome_frame/test/data/navigate_out.html b/chrome_frame/test/data/navigate_out.html
new file mode 100644
index 0000000..7b910b4
--- /dev/null
+++ b/chrome_frame/test/data/navigate_out.html
@@ -0,0 +1,20 @@
+<html>
+ <head><title>Test to make sure that navigations sent back to IE</title>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+
+ <script type="text/javascript">
+ function test() {
+ if (isRunningInMSIE()) {
+ reloadUsingCFProtocol();
+ } else {
+ window.location = "back_to_ie.html";
+ }
+ }
+ </script>
+ </head>
+ <body onLoad="setTimeout(test, 100);">
+ <h2>Prepare to be redirected!</h2>
+ <p>Redirects the same page to its 'cf:' version and </p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/navigateurl_absolute_host.html b/chrome_frame/test/data/navigateurl_absolute_host.html
new file mode 100644
index 0000000..03e1de2
--- /dev/null
+++ b/chrome_frame/test/data/navigateurl_absolute_host.html
@@ -0,0 +1,64 @@
+<html>
+<!-- This page is meant to load inside the host browser like IE/FF -->
+ <head>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript">
+ function onLoad() {
+ var chromeFrame = GetChromeFrame();
+ chromeFrame.onloaderror = OnNavigationFailed;
+ setTimeout(NavigateToURL, 100);
+ }
+
+ function NavigateToURL() {
+ var frame_location = new String(window.location);
+ frame_location = frame_location.replace(
+ /navigateurl_absolute_host.html/, "navigateurl_basic_frame.html");
+ var chromeFrame = GetChromeFrame();
+ chromeFrame.src = frame_location;
+ setTimeout(OnNavigationTimeout, 10000);
+ }
+
+ var navigation_success = 0;
+
+ function OnNavigationFailed(msg) {
+ if (!navigation_success) {
+ onFailure("NavigateURL", 1, 'ChromeFrame Navigation failed: ' + msg);
+ }
+ }
+
+ function OnNavigationTimeout() {
+ OnNavigationFailed('TIMEOUT');
+ }
+
+ function OnChromeFrameLoaded() {
+ navigation_success = 1;
+ onSuccess("NavigateURL", 1);
+ }
+
+ function GetChromeFrame() {
+ return window.document.ChromeFrame;
+ }
+ </script>
+ </head>
+
+ <body onload="onLoad();">
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+ <object id="ChromeFrame" width="500" height="500"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <param name="onload" value="return OnChromeFrameLoaded();">
+ <embed id="ChromeFramePlugin" width="500" height="500"
+ name="ChromeFrame" onload="return OnChromeFrameLoaded();"
+ type="application/chromeframe">
+ </embed>
+ </OBJECT>
+ <br />
+ <br />
+
+ <p>Tests ChromeFrame Navigation</p>
+
+ </body>
+</html>
diff --git a/chrome_frame/test/data/navigateurl_basic_frame.html b/chrome_frame/test/data/navigateurl_basic_frame.html
new file mode 100644
index 0000000..4d99b43
--- /dev/null
+++ b/chrome_frame/test/data/navigateurl_basic_frame.html
@@ -0,0 +1,12 @@
+<html>
+<!-- This page is meant to load inside ChromeFrame -->
+
+<body>
+<div id="statusPanel" style="border: 1px solid red; width: 100%">
+Test running....
+</div>
+
+<p>ChromeFrame NavigateURL Test<br>
+Tests ChromeFrame Navigation</p>
+</body>
+</html>
diff --git a/chrome_frame/test/data/navigateurl_relative_host.html b/chrome_frame/test/data/navigateurl_relative_host.html
new file mode 100644
index 0000000..06ec63e
--- /dev/null
+++ b/chrome_frame/test/data/navigateurl_relative_host.html
@@ -0,0 +1,60 @@
+<html>
+ <!-- This page is meant to load inside the host browser like IE/FF -->
+ <head>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript">
+ function onLoad() {
+ var chromeFrame = GetChromeFrame();
+ chromeFrame.onloaderror = OnNavigationFailed;
+ setTimeout(NavigateToURL, 100);
+ }
+
+ function NavigateToURL() {
+ var chromeFrame = GetChromeFrame();
+ chromeFrame.src = "navigateurl_basic_frame.html";
+ setTimeout(OnNavigationTimeout, 10000);
+ }
+
+ var navigation_complete = 0;
+
+ function OnNavigationFailed(msg) {
+ if (!navigation_complete) {
+ onFailure("NavigateURL", 1, 'ChromeFrame Navigation failed: ' + msg);
+ }
+ }
+
+ function OnNavigationTimeout() {
+ OnNavigationFailed('TIMEOUT');
+ }
+
+ function OnChromeFrameLoaded() {
+ navigation_success = 1;
+ onSuccess("NavigateURL", 1);
+ }
+
+ function GetChromeFrame() {
+ return window.document.ChromeFrame;
+ }
+ </script>
+ </head>
+
+ <body onload="onLoad();">
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+ <object id="ChromeFrame" width="500" height="500"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <param name="onload" value="return OnChromeFrameLoaded();">
+ <embed id="ChromeFramePlugin" width="500" height="500"
+ name="ChromeFrame" onload="return OnChromeFrameLoaded();"
+ type="application/chromeframe">
+ </embed>
+ </OBJECT>
+ <br />
+ <br />
+
+ <p>Tests ChromeFrame Navigation</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/persistent_cookie_test_page.html b/chrome_frame/test/data/persistent_cookie_test_page.html
new file mode 100644
index 0000000..ea56262
--- /dev/null
+++ b/chrome_frame/test/data/persistent_cookie_test_page.html
@@ -0,0 +1,36 @@
+<html>
+ <head><title>Persistent host browser chrome frame cookie test</title>
+ <script type="text/javascript"
+ src="chrome_frame_tester_helpers.js"></script>
+
+ <script type="text/javascript">
+ function validatePersistentCookie() {
+ if (readCookie("PersistentCookie1") != "Cookie1" ||
+ readCookie("PersistentCookie2") != "Cookie2") {
+ onFailure("PersistentCookieTest", 1, "Failed");
+ } else {
+ onSuccess("PersistentCookieTest", 1);
+ }
+ eraseCookie("PersistentCookie1");
+ eraseCookie("PersistentCookie2");
+ }
+
+ function setPersistentCookieAndRedirect() {
+ if (isRunningInMSIE()) {
+ eraseCookie("PersistentCookie1");
+ eraseCookie("PersistentCookie2");
+ createCookie("PersistentCookie1", "Cookie1", 365);
+ createCookie("PersistentCookie2", "Cookie2", 365);
+ reloadUsingCFProtocol();
+ } else {
+ validatePersistentCookie();
+ }
+ }
+ </script>
+ </head>
+ <body onLoad="setTimeout(setPersistentCookieAndRedirect, 100);">
+ <h2>Prepare to be redirected!</h2>
+ <p>Sets two persistent cookies in the host and redirects ChromeFrame <br />
+ to the same page </p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/postmessage_basic_frame.html b/chrome_frame/test/data/postmessage_basic_frame.html
new file mode 100644
index 0000000..76f8cb3
--- /dev/null
+++ b/chrome_frame/test/data/postmessage_basic_frame.html
@@ -0,0 +1,27 @@
+<!-- saved from url=(0014)about:internet -->
+<html>
+<!-- This page is meant to load inside ChromeFrame -->
+ <head>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript">
+ function OnLoad() {
+ externalHost.onmessage = OnHostMessage;
+ }
+
+ function OnHostMessage(evt) {
+ appendStatus('Host message: ' + evt.data);
+ externalHost.postMessage("Hello from ChromeFrame");
+ }
+ </script>
+ </head>
+
+ <body onload="OnLoad();">
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <p>ChromeFrame PostMessage Test
+ <br>Test for PostMessage from the host browser to ChromeFrame and back</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/postmessage_basic_host.html b/chrome_frame/test/data/postmessage_basic_host.html
new file mode 100644
index 0000000..e5ecef9
--- /dev/null
+++ b/chrome_frame/test/data/postmessage_basic_host.html
@@ -0,0 +1,69 @@
+<!-- saved from url=(0014)about:internet -->
+<html>
+<!-- This page is meant to load inside the host browser like IE/FF -->
+ <head>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript">
+ var post_message_reply_received = 0;
+
+ function onChromeFrameLoaded() {
+ appendStatus('Chrome frame loaded...');
+ document.ChromeFrame.postMessage('Hello from host');
+ setTimeout(onPostMessageFailure, 10000);
+ }
+
+ function onNavigationFailed(msg) {
+ onFailure('PostMessage', 1, 'ChromeFrame Navigation failed: ' + msg);
+ }
+
+ function onChromeFrameMessage(evt) {
+ try {
+ var d = new String(evt.data);
+ appendStatus('Message: ' + d);
+ if (d == 'Hello from ChromeFrame') {
+ post_message_reply_received = 1;
+ onSuccess('PostMessage', 1);
+ } else {
+ onFailure('PostMessage', 1, 'unexpected data');
+ }
+ } catch (e) {
+ onFailure('PostMessage', 1, 'exception in onChromeFrameMessage');
+ }
+ }
+
+ function onPostMessageFailure() {
+ if (!post_message_reply_received) {
+ onFailure('PostMessage', 1, 'Did not receive reply back from frame');
+ }
+ }
+ </script>
+ </head>
+
+ <body>
+ <object id="ChromeFrame" width="500" height ="300"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <param name="src" value="postmessage_basic_frame.html">
+ <param name="onload" value="onChromeFrameLoaded();">
+ <param name="onloaderror" value="onNavigationFailed();">
+ <param name="onmessage" value="onChromeFrameMessage(arguments[0]);">
+ <embed id="ChromeFramePlugin" name="ChromeFrame"
+ width="500" height="500"
+ src="postmessage_basic_frame.html"
+ type="application/chromeframe"
+ onload="onChromeFrameLoaded();"
+ onloaderror="onNavigationFailed();"
+ onmessage="onChromeFrameMessage(arguments[0]);">
+ </embed>
+ </object>
+ <br>
+ <br>
+ <p>Test for PostMessage from the host browser to ChromeFrame and back</p>
+ <button onclick="document.ChromeFrame.postMessage('Message from button');">
+ Send message to frame</button>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/privileged_apis_frame.html b/chrome_frame/test/data/privileged_apis_frame.html
new file mode 100644
index 0000000..9e51152
--- /dev/null
+++ b/chrome_frame/test/data/privileged_apis_frame.html
@@ -0,0 +1,33 @@
+<!-- saved from url=(0014)about:internet -->
+<html>
+<!-- This page is meant to load inside ChromeFrame -->
+ <head>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript">
+ function OnLoad() {
+ externalHost.onmessage = OnHostMessage;
+ }
+
+ function OnHostMessage(evt) {
+ // Any time we receive a message, we reflect it back both
+ // with a nonsensical target, and with "*".
+ appendStatus('Host message: ' + evt.data);
+ externalHost.postMessage(evt.data,
+ "privileged_target");
+ appendStatus('After postMessage(' + evt.data + ', "privileged_target)"');
+ externalHost.postMessage(evt.data);
+ appendStatus('After postMessage(' + evt.data + '")');
+ }
+ </script>
+ </head>
+
+ <body onload="OnLoad();">
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <p>ChromeFrame PrivilegeApis Test
+ <br>Tests that private messaging is not available to regular web pages</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/privileged_apis_host.html b/chrome_frame/test/data/privileged_apis_host.html
new file mode 100644
index 0000000..7261704
--- /dev/null
+++ b/chrome_frame/test/data/privileged_apis_host.html
@@ -0,0 +1,85 @@
+<html>
+ <head><title>Privileged Apis test</title>
+ <script type='text/javascript' src='chrome_frame_tester_helpers.js'>
+ </script>
+ <script type='text/javascript'>
+ var testName = 'PrivilegedApis';
+ function OnNavigationFailed(msg) {
+ onFailure(testName, 1, 'ChromeFrame Navigation failed: ' + msg);
+ }
+
+ function OnPrivateMessage() {
+ onFailure(testName, 1, 'OnPrivateMessage should not execute');
+ }
+
+ function OnChromeFrameMessage(evt) {
+ try {
+ var d = new String(evt.data);
+ appendStatus('Message: ' + d);
+ if (d == 'succeed') {
+ onSuccess(testName, 1);
+ } else {
+ onFailure(testName, 1, 'unexpected data');
+ }
+ } catch (e) {
+ onFailure(testName, 1, 'exception in OnChromeFrameMessage');
+ }
+ }
+
+ function OnChromeFrameLoaded(url) {
+ var cf = GetChromeFrame();
+
+ try {
+ // Any message received by this listener is a failure.
+ // This succeeds in FF, but throws an exception in IE.
+ cf.addEventListener('onprivatemessage', OnPrivateMessage, false);
+ } catch(e) {
+ cf.onprivatemessage =
+ appendStatus('addEventListener onprivatemessage threw exception')
+ }
+
+ // If this invocation succeeds, then 'fail' is reflected by the frame
+ // and we fail in the OnChromeFrameMessage handler above.
+ try {
+ cf.postPrivateMessage('fail', String(document.location), '*');
+ onFailure(testName, 1, 'postPrivateMessage should throw');
+ } catch(e) {
+ }
+ appendStatus('After postPrivateMessage')
+ // The frame reflects this twice, first to a bogus target
+ // and again to the default target '*'. We succeed if we
+ // get the reflected message to OnChromeFrameMessage and not to
+ // OnPrivateMessage.
+ cf.postMessage('succeed');
+ }
+
+ function GetChromeFrame() {
+ return window.document.ChromeFrame;
+ }
+ </script>
+ </head>
+ <body>
+ <div id='statusPanel' style='border: 1px solid red; width: 100%'>
+ Test running....
+ </div>
+
+ <!-- TODO(siggi): Test setting onprivatemessage in these params -->
+ <object id='ChromeFrame' width='500' height='500'
+ codebase='http://www.google.com'
+ classid='CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A'>
+ <param name='src' value='privileged_apis_frame.html'>
+ <param name='onload' value='OnChromeFrameLoaded(arguments[0]);'>
+ <param name='onloaderror' value='OnNavigationFailed();'>
+ <param name='onmessage' value='OnChromeFrameMessage(arguments[0]);'>
+ <embed id='ChromeFramePlugin' width='500' height='500' name='ChromeFrame'
+ src='privileged_apis_frame.html'
+ type='application/chromeframe'
+ onload='OnChromeFrameLoaded(arguments[0]);'
+ onloaderror='OnNavigationFailed();'
+ onmessage='return OnChromeFrameMessage(arguments[0]);'
+ privileged_mode='1'
+ </embed>
+ </object>
+ <p>Tests that privileged apis are unavailable from regular pages</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/simple_object_focus.html b/chrome_frame/test/data/simple_object_focus.html
new file mode 100644
index 0000000..138ffa5
--- /dev/null
+++ b/chrome_frame/test/data/simple_object_focus.html
@@ -0,0 +1,95 @@
+<!-- saved from url=(0014)about:internet -->
+<html>
+<!-- This page is meant to load inside the host browser like IE/FF -->
+<head>
+<script type="text/javascript" src="chrome_frame_tester_helpers.js"></script>
+<script type="text/javascript">
+var g_failure_timeout = null;
+var g_test_id = 1;
+var g_test_name = "ObjectFocus";
+
+function onLoad() {
+ status("onload");
+
+ try {
+ var cf = getCf();
+ cf.onmessage = OnChromeFrameMessage;
+ window.setTimeout(NavigateToURL, 100);
+ } catch(e) {
+ status("error: onload");
+ onFailure(g_test_name, g_test_id, "error in onload");
+ }
+
+ sendOnLoadEvent();
+}
+
+function NavigateToURL() {
+ try {
+ status("Navigate to URL");
+ var cf = getCf();
+ cf.src = "simple_object_focus_cf.html";
+ g_failure_timeout = window.setTimeout(OnObjectFocusFailed, 10000);
+ } catch(e) {
+ status("error: NavigateToURL");
+ onFailure(g_test_name, g_test_id, "NavigateToURL error");
+ }
+}
+
+function OnObjectFocusFailed() {
+ status("OnNavigationFailed");
+ onFailure(g_test_name, g_test_id, "focus test failed");
+}
+
+function OnChromeFrameLoaded() {
+ status("OnChromeFrameLoaded");
+ try {
+ // Set focus to chrome frame. This should set focus to the first element
+ // inside the frame, which a script inside the page will detect and notify
+ // us back by sending us a message.
+ getCf().focus();
+ } catch(e) {
+ status("error: can't set focus");
+ onFailure(g_test_name, g_test_id, "focus() error");
+ }
+}
+
+function OnChromeFrameMessage(evt) {
+ if (evt.data != "btnOnFocus") {
+ status("unexpected message: " + evt.data + " from " + evt.origin);
+ } else {
+ window.clearTimeout(g_failure_timeout);
+ g_failure_timeout = null;
+ status("success");
+ }
+ onSuccess(g_test_name, g_test_id);
+}
+
+function getCf() {
+ // Fetching chrome frame with getElementById doesn't work in Firefox.
+ // Most likely due to object vs embed.
+ return document.ChromeFrame;
+}
+
+// Useful while writing and debugging the unit test.
+function status(s) {
+ var panel = document.getElementById("status_panel");
+ panel.innerHTML = s;
+}
+
+</script>
+</head>
+<body onload="onLoad();">
+<div id="status_panel" style="border: 1px solid red; width: 100%">
+Test running....
+</div>
+<object id="ChromeFrame" width="300" height="60" tabindex="0"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <param name="onload" value="return OnChromeFrameLoaded();">
+ <embed width="300" height="60" name="ChromeFrame"
+ onload="return OnChromeFrameLoaded();"
+ type="application/chromeframe">
+ </embed>
+</object>
+</body>
+</html>
diff --git a/chrome_frame/test/data/simple_object_focus_cf.html b/chrome_frame/test/data/simple_object_focus_cf.html
new file mode 100644
index 0000000..9b06711
--- /dev/null
+++ b/chrome_frame/test/data/simple_object_focus_cf.html
@@ -0,0 +1,10 @@
+<html>
+<!-- This page is meant to load inside Chrome Frame -->
+ <body>
+ <button onfocus="externalHost.postMessage('btnOnFocus');">
+ hello world</button>
+ <div id="statusPanel" style="border: 1px solid green; width: 100%">
+ Inside Chrome Frame....
+ </div>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/src_property_frame1.html b/chrome_frame/test/data/src_property_frame1.html
new file mode 100644
index 0000000..1eaa3cf
--- /dev/null
+++ b/chrome_frame/test/data/src_property_frame1.html
@@ -0,0 +1,13 @@
+<html>
+ <head><title>src property test - page 1</title>
+ <script type="text/javascript">
+ function redirect(){
+ window.location = "src_property_frame2.html";
+ }
+ </script>
+ </head>
+ <body onLoad="setTimeout(redirect, 100);">
+ <h2>Prepare to be redirected!</h2>
+ <p>Redirecting to a new page within frame...</p>
+ </body>
+</html> \ No newline at end of file
diff --git a/chrome_frame/test/data/src_property_frame2.html b/chrome_frame/test/data/src_property_frame2.html
new file mode 100644
index 0000000..c5c0364
--- /dev/null
+++ b/chrome_frame/test/data/src_property_frame2.html
@@ -0,0 +1,8 @@
+<html>
+ <head><title>src property test - page 2</title>
+ </head>
+ <body>
+ <h2>Redirected!</h2>
+ <p>All finished.</p>
+ </body>
+</html> \ No newline at end of file
diff --git a/chrome_frame/test/data/src_property_host.html b/chrome_frame/test/data/src_property_host.html
new file mode 100644
index 0000000..7b7b358
--- /dev/null
+++ b/chrome_frame/test/data/src_property_host.html
@@ -0,0 +1,65 @@
+<html>
+ <head><title>src property test</title>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript">
+ function OnNavigationFailed() {
+ onFailure("ChromeFrame_SrcTest", 1, "ChromeFrame Navigation failed");
+ }
+
+ var load_count = 2;
+
+ function OnChromeFrameLoaded(url) {
+ url = url.data;
+
+ var chromeFrame = GetChromeFrame();
+ var frame_url = chromeFrame.src;
+
+ appendStatus("Loaded URL: " + url + " Frame url: " + frame_url);
+ load_count--;
+
+ if (load_count) {
+ // For the first load, the URLs should match.
+ if (frame_url != url) {
+ onFailure("SrcProperty", 1, "Url: " + url);
+ }
+ } else {
+ // Previous versions changed the frame URL when internal navigation
+ // was performed. This does not match how iframes behave, and so we
+ // report success only in the case that they continue to match, even
+ // though the "internal" URL is different (and not visible) to the
+ // external host.
+ if (frame_url == url) {
+ onFailure("SrcProperty", 1, "Url: " + url);
+ } else {
+ onSuccess("SrcProperty", 1);
+ }
+ }
+ }
+
+ function GetChromeFrame() {
+ return window.document.ChromeFrame;
+ }
+ </script>
+ </head>
+ <body>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+
+ <object id="ChromeFrame" width="500" height="500"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <param name="src" value="src_property_frame1.html">
+ <param name="onload" value="return OnChromeFrameLoaded(arguments[0]);">
+ <param name="onloaderror" value="return OnNavigationFailed(arguments[0]);">
+ <embed id="ChromeFramePlugin" width="500" height="500" name="ChromeFrame"
+ src="src_property_frame1.html"
+ type="application/chromeframe"
+ onload="return OnChromeFrameLoaded(arguments[0]);"
+ onloaderror="return OnNavigationFailed(arguments[0]);">
+ </embed>
+ </object>
+ <p>Tests ChromeFrame Navigation</p>
+ </body>
+</html>
diff --git a/chrome_frame/test/data/version.html b/chrome_frame/test/data/version.html
new file mode 100644
index 0000000..b585a6d
--- /dev/null
+++ b/chrome_frame/test/data/version.html
@@ -0,0 +1,29 @@
+<html>
+<!-- This page is meant to load inside the host browser like IE/FF -->
+ <head>
+ <script type="text/javascript" src="chrome_frame_tester_helpers.js">
+ </script>
+ <script type="text/javascript">
+ function onLoad() {
+ appendStatus('Chrome frame version: ' + document.ChromeFrame.version);
+ onFinished('version', 1, document.ChromeFrame.version);
+ }
+ </script>
+ </head>
+
+ <body onload="onLoad();">
+ <object id="ChromeFrame"
+ codebase="http://www.google.com"
+ classid="CLSID:E0A900DF-9611-4446-86BD-4B1D47E7DB2A">
+ <embed id="ChromeFramePlugin" name="ChromeFrame"
+ type="application/chromeframe"
+ </embed>
+ </object>
+ <br>
+ <br>
+ <p>Test for Chrome frame version</p>
+ <div id="statusPanel" style="border: 1px solid red; width: 100%">
+ Test running....
+ </div>
+ </body>
+</html>
diff --git a/chrome_frame/test/function_stub_unittest.cc b/chrome_frame/test/function_stub_unittest.cc
new file mode 100644
index 0000000..6ef6f36
--- /dev/null
+++ b/chrome_frame/test/function_stub_unittest.cc
@@ -0,0 +1,66 @@
+// Copyright (c) 2009 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/function_stub.h"
+
+#include "chrome_frame/test/chrome_frame_unittests.h"
+
+#define NO_INLINE __declspec(noinline)
+
+namespace {
+
+typedef int (__stdcall* FooPrototype)();
+
+NO_INLINE int __stdcall Foo() {
+ return 1;
+}
+
+NO_INLINE int __stdcall PatchedFoo(FooPrototype original) {
+ return original() + 1;
+}
+
+} // end namespace
+
+TEST(PatchTests, FunctionStub) {
+ EXPECT_EQ(Foo(), 1);
+ // Create a function stub that calls PatchedFoo and supplies it with
+ // a pointer to Foo.
+ FunctionStub* stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(&Foo),
+ &PatchedFoo);
+ EXPECT_TRUE(stub != NULL);
+ // Call the stub as it were Foo(). The call should get forwarded to Foo().
+ FooPrototype patch = reinterpret_cast<FooPrototype>(stub->code());
+ EXPECT_EQ(patch(), 2);
+ // Now neutralize the stub so that it calls Foo() directly without touching
+ // PatchedFoo().
+ // stub->BypassStub(&Foo);
+ stub->BypassStub(reinterpret_cast<void*>(stub->argument()));
+ EXPECT_EQ(patch(), 1);
+ // We're done with the stub.
+ FunctionStub::Destroy(stub);
+}
+
+// Basic tests to check the validity of a stub.
+TEST(PatchTests, FunctionStubCompare) {
+ EXPECT_EQ(Foo(), 1);
+
+ // Detect the absence of a stub
+ FunctionStub* stub = reinterpret_cast<FunctionStub*>(&Foo);
+ EXPECT_FALSE(stub->is_valid());
+
+ stub = FunctionStub::Create(reinterpret_cast<uintptr_t>(&Foo), &PatchedFoo);
+ EXPECT_TRUE(stub != NULL);
+ EXPECT_TRUE(stub->is_valid());
+
+ FooPrototype patch = reinterpret_cast<FooPrototype>(stub->code());
+ EXPECT_EQ(patch(), 2);
+
+ // See if we can get the correct absolute pointer to the hook function
+ // back from the stub.
+ EXPECT_EQ(stub->absolute_target(), reinterpret_cast<uintptr_t>(&PatchedFoo));
+
+ // Also verify that the argument being passed to the hook function is indeed
+ // the pointer to the original function (again, absolute not relative).
+ EXPECT_EQ(stub->argument(), reinterpret_cast<uintptr_t>(&Foo));
+}
diff --git a/chrome_frame/test/helper_gmock.h b/chrome_frame/test/helper_gmock.h
new file mode 100644
index 0000000..7f6d0a7
--- /dev/null
+++ b/chrome_frame/test/helper_gmock.h
@@ -0,0 +1,597 @@
+// Copyright (c) 2006-2009 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.
+#ifndef CHROME_FRAME_TEST_HELPER_GMOCK_H_
+#define CHROME_FRAME_TEST_HELPER_GMOCK_H_
+// This intention of this file is to make possible gmock WithArgs<> in
+// Chromium code base.
+// MutantImpl is like CallbackImpl, but also has prebound arguments (like Task)
+// There is also functor wrapper around it that should be used with
+// testing::Invoke, for example:
+// testing::WithArgs<0, 2>(
+// testing::Invoke(CBF(&mock_object, &MockObject::Something, &tmp_obj, 12)));
+// This will invoke MockObject::Something(tmp_obj, 12, arg_0, arg_2)
+
+// DispatchToMethod supporting two sets of arguments -
+// prebound (P) and calltime (C)
+// 1 - 1
+template <class ObjT, class Method, class P1, class C1>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<P1>& p,
+ const Tuple1<C1>& c) {
+ (obj->*method)(p.a, c.a);
+}
+// 2 - 1
+template <class ObjT, class Method, class P1, class P2, class C1>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<P1, P2>& p,
+ const Tuple1<C1>& c) {
+ (obj->*method)(p.a, p.b, c.a);
+}
+// 3 - 1
+template <class ObjT, class Method, class P1, class P2, class P3, class C1>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<P1, P2, P3>& p,
+ const Tuple1<C1>& c) {
+ (obj->*method)(p.a, p.b, p.c, c.a);
+}
+// 4 - 1
+template <class ObjT, class Method, class P1, class P2, class P3,
+ class P4, class C1>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<P1, P2, P3, P4>& p,
+ const Tuple1<C1>& c) {
+ (obj->*method)(p.a, p.b, p.c, p.d, c.a);
+}
+
+// 1 - 2
+template <class ObjT, class Method, class P1, class C1, class C2>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<P1>& p,
+ const Tuple2<C1, C2>& c) {
+ (obj->*method)(p.a, c.a, c.b);
+}
+
+// 2 - 2
+template <class ObjT, class Method, class P1, class P2, class C1, class C2>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<P1, P2>& p,
+ const Tuple2<C1, C2>& c) {
+ (obj->*method)(p.a, p.b, c.a, c.b);
+}
+
+// 3 - 2
+template <class ObjT, class Method, class P1, class P2, class P3, class C1,
+ class C2>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<P1, P2, P3>& p,
+ const Tuple2<C1, C2>& c) {
+ (obj->*method)(p.a, p.b, p.c, c.a, c.b);
+}
+
+// 4 - 2
+template <class ObjT, class Method, class P1, class P2, class P3, class P4,
+ class C1, class C2>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<P1, P2, P3, P4>& p,
+ const Tuple2<C1, C2>& c) {
+ (obj->*method)(p.a, p.b, p.c, p.d, c.a, c.b);
+}
+
+// 1 - 3
+template <class ObjT, class Method, class P1, class C1, class C2, class C3>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<P1>& p,
+ const Tuple3<C1, C2, C3>& c) {
+ (obj->*method)(p.a, c.a, c.b, c.c);
+}
+
+// 2 - 3
+template <class ObjT, class Method, class P1, class P2, class C1, class C2,
+ class C3>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<P1, P2>& p,
+ const Tuple3<C1, C2, C3>& c) {
+ (obj->*method)(p.a, p.b, c.a, c.b, c.c);
+}
+
+// 3 - 3
+template <class ObjT, class Method, class P1, class P2, class P3, class C1,
+ class C2, class C3>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<P1, P2, P3>& p,
+ const Tuple3<C1, C2, C3>& c) {
+ (obj->*method)(p.a, p.b, p.c, c.a, c.b, c.c);
+}
+
+// 4 - 3
+template <class ObjT, class Method, class P1, class P2, class P3, class P4,
+ class C1, class C2, class C3>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<P1, P2, P3, P4>& p,
+ const Tuple3<C1, C2, C3>& c) {
+ (obj->*method)(p.a, p.b, p.c, p.d, c.a, c.b, c.c);
+}
+
+// 1 - 4
+template <class ObjT, class Method, class P1, class C1, class C2, class C3,
+ class C4>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple1<P1>& p,
+ const Tuple4<C1, C2, C3, C4>& c) {
+ (obj->*method)(p.a, c.a, c.b, c.c, c.d);
+}
+
+// 2 - 4
+template <class ObjT, class Method, class P1, class P2, class C1, class C2,
+ class C3, class C4>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple2<P1, P2>& p,
+ const Tuple4<C1, C2, C3, C4>& c) {
+ (obj->*method)(p.a, p.b, c.a, c.b, c.c, c.d);
+}
+
+// 3 - 4
+template <class ObjT, class Method, class P1, class P2, class P3,
+ class C1, class C2, class C3, class C4>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple3<P1, P2, P3>& p,
+ const Tuple4<C1, C2, C3, C4>& c) {
+ (obj->*method)(p.a, p.b, p.c, c.a, c.b, c.c, c.d);
+}
+
+// 4 - 4
+template <class ObjT, class Method, class P1, class P2, class P3, class P4,
+ class C1, class C2, class C3, class C4>
+inline void DispatchToMethod(ObjT* obj, Method method,
+ const Tuple4<P1, P2, P3, P4>& p,
+ const Tuple4<C1, C2, C3, C4>& c) {
+ (obj->*method)(p.a, p.b, p.c, p.d, c.a, c.b, c.c, c.d);
+}
+
+
+////////////////////////////////////////////////////////////////////////////////
+
+// Like CallbackImpl but has prebound arguments (like Task)
+template <class T, typename Method, typename PreBound, typename Params>
+class MutantImpl : public CallbackStorage<T, Method>,
+ public CallbackRunner<Params> {
+ public:
+ MutantImpl(T* obj, Method meth, const PreBound& pb)
+ : CallbackStorage<T, Method>(obj, meth),
+ pb_(pb) {
+ }
+
+ virtual void RunWithParams(const Params& params) {
+ // use "this->" to force C++ to look inside our templatized base class; see
+ // Effective C++, 3rd Ed, item 43, p210 for details.
+ DispatchToMethod(this->obj_, this->meth_, pb_, params);
+ }
+
+ PreBound pb_;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+// Mutant creation simplification
+// 1 - 1
+template <class T, typename P1, typename A1>
+inline typename Callback1<A1>::Type* NewMutant(T* obj,
+ void (T::*method)(P1, A1),
+ P1 p1) {
+ return new MutantImpl<T, void (T::*)(P1, A1), P1, A1>(obj, method,
+ MakeTuple(p1));
+}
+
+// 1 - 2
+template <class T, typename P1, typename A1, typename A2>
+inline typename Callback2<A1, A2>::Type* NewMutant(T* obj,
+ void (T::*method)(P1, A1, A2),
+ P1 p1) {
+ return new MutantImpl<T, void (T::*)(P1, A1, A2), Tuple1<P1>, Tuple2<A1, A2> >
+ (obj, method, MakeTuple(p1));
+}
+
+// 1 - 3
+template <class T, typename P1, typename A1, typename A2, typename A3>
+inline typename Callback3<A1, A2, A3>::Type*
+NewMutant(T* obj, void (T::*method)(P1, A1, A2, A3), P1 p1) {
+ return new MutantImpl<T, void (T::*)(P1, A1, A2, A3), Tuple1<P1>,
+ Tuple3<A1, A2, A3> >(obj, method, MakeTuple(p1));
+}
+
+// 1 - 4
+template <class T, typename P1, typename A1, typename A2, typename A3,
+ typename A4>
+inline typename Callback4<A1, A2, A3, A4>::Type*
+NewMutant(T* obj, void (T::*method)(P1, A1, A2, A3, A4), P1 p1) {
+ return new MutantImpl<T, void (T::*)(P1, A1, A2, A3, A4), Tuple1<P1>,
+ Tuple4<A1, A2, A3, A4> >(obj, method, MakeTuple(p1));
+}
+
+
+// 2 - 1
+template <class T, typename P1, typename P2, typename A1>
+inline typename Callback1<A1>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, A1), P1 p1, P2 p2) {
+ return new MutantImpl<T, void (T::*)(P1, P2, A1), Tuple2<P1, P2>,
+ Tuple1<A1> >(obj, method, MakeTuple(p1, p2));
+}
+
+// 2 - 2
+template <class T, typename P1, typename P2, typename A1, typename A2>
+inline typename Callback2<A1, A2>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, A1, A2), P1 p1, P2 p2) {
+ return new MutantImpl<T, void (T::*)(P1, P2, A1, A2), Tuple2<P1, P2>,
+ Tuple2<A1, A2> >(obj, method, MakeTuple(p1, p2));
+}
+
+// 2 - 3
+template <class T, typename P1, typename P2, typename A1, typename A2,
+ typename A3>
+inline typename Callback3<A1, A2, A3>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, A1, A2, A3), P1 p1, P2 p2) {
+ return new MutantImpl<T, void (T::*)(P1, P2, A1, A2, A3), Tuple2<P1, P2>,
+ Tuple3<A1, A2, A3> >(obj, method, MakeTuple(p1, p2));
+}
+
+// 2 - 4
+template <class T, typename P1, typename P2, typename A1, typename A2,
+ typename A3, typename A4>
+inline typename Callback4<A1, A2, A3, A4>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, A1, A2, A3, A4), P1 p1, P2 p2) {
+ return new MutantImpl<T, void (T::*)(P1, P2, A1, A2, A3, A4), Tuple2<P1, P2>,
+ Tuple3<A1, A2, A3, A4> >(obj, method, MakeTuple(p1, p2));
+}
+
+// 3 - 1
+template <class T, typename P1, typename P2, typename P3, typename A1>
+inline typename Callback1<A1>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, P3, A1), P1 p1, P2 p2, P3 p3) {
+ return new MutantImpl<T, void (T::*)(P1, P2, P3, A1), Tuple3<P1, P2, P3>,
+ Tuple1<A1> >(obj, method, MakeTuple(p1, p2, p3));
+}
+
+// 3 - 2
+template <class T, typename P1, typename P2, typename P3, typename A1,
+ typename A2>
+inline typename Callback2<A1, A2>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, P3, A1, A2), P1 p1, P2 p2, P3 p3) {
+ return new MutantImpl<T, void (T::*)(P1, P2, P3, A1, A2), Tuple3<P1, P2, P3>,
+ Tuple2<A1, A2> >(obj, method, MakeTuple(p1, p2, p3));
+}
+
+// 3 - 3
+template <class T, typename P1, typename P2, typename P3, typename A1,
+ typename A2, typename A3>
+inline typename Callback3<A1, A2, A3>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, P3, A1, A2, A3), P1 p1, P2 p2,
+ P3 p3) {
+ return new MutantImpl<T, void (T::*)(P1, P2, P3, A1, A2, A3),
+ Tuple3<P1, P2, P3>, Tuple3<A1, A2, A3> >(obj, method,
+ MakeTuple(p1, p2, p3));
+}
+
+// 3 - 4
+template <class T, typename P1, typename P2, typename P3, typename A1,
+ typename A2, typename A3, typename A4>
+inline typename Callback4<A1, A2, A3, A4>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, P3, A1, A2, A3, A4), P1 p1, P2 p2,
+ P3 p3) {
+ return new MutantImpl<T, void (T::*)(P1, P2, P3, A1, A2, A3, A4),
+ Tuple3<P1, P2, P3>, Tuple3<A1, A2, A3, A4> >(obj, method,
+ MakeTuple(p1, p2, p3));
+}
+
+// 4 - 1
+template <class T, typename P1, typename P2, typename P3, typename P4,
+ typename A1>
+inline typename Callback1<A1>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, P3, P4, A1), P1 p1, P2 p2, P3 p3,
+ P4 p4) {
+ return new MutantImpl<T, void (T::*)(P1, P2, P3, P4, A1),
+ Tuple4<P1, P2, P3, P4>, Tuple1<A1> >(obj, method,
+ MakeTuple(p1, p2, p3, p4));
+}
+
+// 4 - 2
+template <class T, typename P1, typename P2, typename P3, typename P4,
+ typename A1, typename A2>
+inline typename Callback2<A1, A2>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, P3, P4, A1, A2), P1 p1, P2 p2,
+ P3 p3, P4 p4) {
+ return new MutantImpl<T, void (T::*)(P1, P2, P3, P4, A1, A2),
+ Tuple4<P1, P2, P3, P4>, Tuple2<A1, A2> >(obj, method,
+ MakeTuple(p1, p2, p3, p4));
+}
+
+// 4 - 3
+template <class T, typename P1, typename P2, typename P3, typename P4,
+ typename A1, typename A2, typename A3>
+inline typename Callback3<A1, A2, A3>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, P3, P4, A1, A2, A3), P1 p1, P2 p2,
+ P3 p3, P4 p4) {
+ return new MutantImpl<T, void (T::*)(P1, P2, P3, P4, A1, A2, A3),
+ Tuple4<P1, P2, P3, P4>, Tuple3<A1, A2, A3> >(obj, method,
+ MakeTuple(p1, p2, p3, p4));
+}
+
+// 4 - 4
+template <class T, typename P1, typename P2, typename P3, typename P4,
+ typename A1, typename A2, typename A3, typename A4>
+inline typename Callback4<A1, A2, A3, A4>::Type*
+NewMutant(T* obj, void (T::*method)(P1, P2, P3, P4, A1, A2, A3, A4),
+ P1 p1, P2 p2, P3 p3, P4 p4) {
+ return new MutantImpl<T, void (T::*)(P1, P2, P3, P4, A1, A2, A3, A4),
+ Tuple4<P1, P2, P3, P4>, Tuple3<A1, A2, A3, A4> >(obj, method,
+ MakeTuple(p1, p2, p3, p4));
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// Simple callback wrapper acting as a functor.
+// Redirects operator() to CallbackRunner<Params>::Run
+// We cannot delete the inner impl_ in object's destructor because
+// this object is copied few times inside from GMock machinery.
+template <typename Params>
+struct CallbackFunctor {
+ explicit CallbackFunctor(CallbackRunner<Params>* cb) : impl_(cb) {}
+
+ template <typename Arg1>
+ inline void operator()(const Arg1& a) {
+ impl_->Run(a);
+ delete impl_;
+ impl_ = NULL;
+ }
+
+ template <typename Arg1, typename Arg2>
+ inline void operator()(const Arg1& a, const Arg2& b) {
+ impl_->Run(a, b);
+ delete impl_;
+ impl_ = NULL;
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3>
+ inline void operator()(const Arg1& a, const Arg2& b, const Arg3& c) {
+ impl_->Run(a, b, c);
+ delete impl_;
+ impl_ = NULL;
+ }
+
+ template <typename Arg1, typename Arg2, typename Arg3, typename Arg4>
+ inline void operator()(const Arg1& a, const Arg2& b, const Arg3& c,
+ const Arg4& d) {
+ impl_->Run(a, b, c, d);
+ delete impl_;
+ impl_ = NULL;
+ }
+
+ private:
+ CallbackFunctor();
+ CallbackRunner<Params>* impl_;
+};
+
+
+///////////////////////////////////////////////////////////////////////////////
+// CallbackFunctors creation
+
+// 0 - 1
+template <class T, typename A1>
+inline CallbackFunctor<Tuple1<A1> >
+CBF(T* obj, void (T::*method)(A1)) {
+ return CallbackFunctor<Tuple1<A1> >(NewCallback(obj, method));
+}
+
+// 0 - 2
+template <class T, typename A1, typename A2>
+inline CallbackFunctor<Tuple2<A1, A2> >
+CBF(T* obj, void (T::*method)(A1, A2)) {
+ return CallbackFunctor<Tuple2<A1, A2> >(NewCallback(obj, method));
+}
+
+// 0 - 3
+template <class T, typename A1, typename A2, typename A3>
+inline CallbackFunctor<Tuple3<A1, A2, A3> >
+CBF(T* obj, void (T::*method)(A1, A2, A3)) {
+ return CallbackFunctor<Tuple3<A1, A2, A3> >(NewCallback(obj, method));
+}
+
+// 0 - 4
+template <class T, typename A1, typename A2, typename A3, typename A4>
+inline CallbackFunctor<Tuple4<A1, A2, A3, A4> >
+CBF(T* obj, void (T::*method)(A1, A2, A3, A4)) {
+ return CallbackFunctor<Tuple4<A1, A2, A3, A4> >(NewCallback(obj, method));
+}
+
+// 1 - 1
+template <class T, typename P1, typename A1>
+inline CallbackFunctor<Tuple1<A1> >
+CBF(T* obj, void (T::*method)(P1, A1), P1 p1) {
+ Callback1<A1>::Type* t = new MutantImpl<T, void (T::*)(P1, A1), Tuple1<P1>,
+ Tuple1<A1> >(obj, method, MakeTuple(p1));
+ return CallbackFunctor<Tuple1<A1> >(t);
+}
+
+// 1 - 2
+template <class T, typename P1, typename A1, typename A2>
+inline CallbackFunctor<Tuple2<A1, A2> >
+CBF(T* obj, void (T::*method)(P1, A1, A2), P1 p1) {
+ Callback2<A1, A2>::Type* t = new MutantImpl<T, void (T::*)(P1, A1, A2),
+ Tuple1<P1>, Tuple2<A1, A2> >(obj, method, MakeTuple(p1));
+ return CallbackFunctor<Tuple2<A1, A2> >(t);
+}
+
+// 1 - 3
+template <class T, typename P1, typename A1, typename A2, typename A3>
+inline CallbackFunctor<Tuple3<A1, A2, A3> >
+CBF(T* obj, void (T::*method)(P1, A1, A2, A3), P1 p1) {
+ Callback3<A1, A2, A3>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, A1, A2, A3), Tuple1<P1>,
+ Tuple3<A1, A2, A3> >(obj, method, MakeTuple(p1));
+ return CallbackFunctor<Tuple3<A1, A2, A3> >(t);
+}
+
+// 1 - 4
+template <class T, typename P1, typename A1, typename A2, typename A3,
+ typename A4>
+inline CallbackFunctor<Tuple4<A1, A2, A3, A4> >
+CBF(T* obj, void (T::*method)(P1, A1, A2, A3, A4), P1 p1) {
+ Callback4<A1, A2, A3>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, A1, A2, A3, A4), Tuple1<P1>,
+ Tuple4<A1, A2, A3, A4> >(obj, method, MakeTuple(p1));
+ return CallbackFunctor<Tuple4<A1, A2, A3, A4> >(t);
+}
+
+// 2 - 1
+template <class T, typename P1, typename P2, typename A1>
+inline CallbackFunctor<Tuple1<A1> >
+CBF(T* obj, void (T::*method)(P1, P2, A1), P1 p1, P2 p2) {
+ Callback1<A1>::Type* t = new MutantImpl<T, void (T::*)(P1, P2, A1),
+ Tuple2<P1, P2>, Tuple1<A1> >(obj, method, MakeTuple(p1, p2));
+ return CallbackFunctor<Tuple1<A1> >(t);
+}
+
+// 2 - 2
+template <class T, typename P1, typename P2, typename A1, typename A2>
+inline CallbackFunctor<Tuple2<A1, A2> >
+CBF(T* obj, void (T::*method)(P1, P2, A1, A2), P1 p1, P2 p2) {
+ Callback2<A1, A2>::Type* t = new MutantImpl<T, void (T::*)(P1, P2, A1, A2),
+ Tuple2<P1, P2>, Tuple2<A1, A2> >(obj, method, MakeTuple(p1, p2));
+ return CallbackFunctor<Tuple2<A1, A2> >(t);
+}
+
+// 2 - 3
+template <class T, typename P1, typename P2, typename A1, typename A2,
+ typename A3> inline CallbackFunctor<Tuple3<A1, A2, A3> >
+CBF(T* obj, void (T::*method)(P1, P2, A1, A2, A3), P1 p1, P2 p2) {
+ Callback3<A1, A2, A3>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, P2, A1, A2, A3), Tuple2<P1, P2>,
+ Tuple3<A1, A2, A3> >(obj, method, MakeTuple(p1, p2));
+ return CallbackFunctor<Tuple3<A1, A2, A3> >(t);
+}
+
+// 2 - 4
+template <class T, typename P1, typename P2, typename A1, typename A2,
+ typename A3, typename A4>
+inline CallbackFunctor<Tuple4<A1, A2, A3, A4> >
+CBF(T* obj, void (T::*method)(P1, P2, A1, A2, A3, A4), P1 p1, P2 p2) {
+ Callback4<A1, A2, A3>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, P2, A1, A2, A3, A4), Tuple2<P1, P2>,
+ Tuple4<A1, A2, A3, A4> >(obj, method, MakeTuple(p1, p2));
+ return CallbackFunctor<Tuple4<A1, A2, A3, A4> >(t);
+}
+
+
+// 3 - 1
+template <class T, typename P1, typename P2, typename P3, typename A1>
+inline CallbackFunctor<Tuple1<A1> >
+CBF(T* obj, void (T::*method)(P1, P2, P3, A1), P1 p1, P2 p2, P3 p3) {
+ Callback1<A1>::Type* t = new MutantImpl<T, void (T::*)(P1, P2, P3, A1),
+ Tuple3<P1, P2, P3>, Tuple1<A1> >(obj, method, MakeTuple(p1, p2, p3));
+ return CallbackFunctor<Tuple1<A1> >(t);
+}
+
+
+// 3 - 2
+template <class T, typename P1, typename P2, typename P3, typename A1,
+ typename A2> inline CallbackFunctor<Tuple2<A1, A2> >
+CBF(T* obj, void (T::*method)(P1, P2, P3, A1, A2), P1 p1, P2 p2, P3 p3) {
+ Callback2<A1, A2>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, P2, P3, A1, A2), Tuple3<P1, P2, P3>,
+ Tuple2<A1, A2> >(obj, method, MakeTuple(p1, p2, p3));
+ return CallbackFunctor<Tuple2<A1, A2> >(t);
+}
+
+// 3 - 3
+template <class T, typename P1, typename P2, typename P3, typename A1,
+ typename A2, typename A3>
+inline CallbackFunctor<Tuple3<A1, A2, A3> >
+CBF(T* obj, void (T::*method)(P1, P2, P3, A1, A2, A3), P1 p1, P2 p2, P3 p3) {
+ Callback3<A1, A2, A3>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, P2, P3, A1, A2, A3), Tuple3<P1, P2, P3>,
+ Tuple3<A1, A2, A3> >(obj, method, MakeTuple(p1, p2, p3));
+ return CallbackFunctor<Tuple3<A1, A2, A3> >(t);
+}
+
+// 3 - 4
+template <class T, typename P1, typename P2, typename P3, typename A1,
+ typename A2, typename A3, typename A4>
+inline CallbackFunctor<Tuple4<A1, A2, A3, A4> >
+CBF(T* obj, void (T::*method)(P1, P2, P3, A1, A2, A3, A4),
+ P1 p1, P2 p2, P3 p3) {
+ Callback4<A1, A2, A3>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, P2, P3, A1, A2, A3, A4),
+ Tuple3<P1, P2, P3>, Tuple4<A1, A2, A3, A4> >(obj, method,
+ MakeTuple(p1, p2, p3));
+ return CallbackFunctor<Tuple4<A1, A2, A3, A4> >(t);
+}
+
+
+
+// 4 - 1
+template <class T, typename P1, typename P2, typename P3, typename P4,
+ typename A1>
+inline CallbackFunctor<Tuple1<A1> >
+CBF(T* obj, void (T::*method)(P1, P2, P3, P4, A1), P1 p1, P2 p2, P3 p3, P4 p4) {
+ Callback1<A1>::Type* t = new MutantImpl<T, void (T::*)(P1, P2, P3, P4, A1),
+ Tuple4<P1, P2, P3, P4>, Tuple1<A1> >
+ (obj, method, MakeTuple(p1, p2, p3, p4));
+ return CallbackFunctor<Tuple1<A1> >(t);
+}
+
+
+// 4 - 2
+template <class T, typename P1, typename P2, typename P3, typename P4,
+ typename A1, typename A2>
+inline CallbackFunctor<Tuple2<A1, A2> >
+CBF(T* obj, void (T::*method)(P1, P2, P3, P4, A1, A2),
+ P1 p1, P2 p2, P3 p3, P4 p4) {
+ Callback2<A1, A2>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, P2, P3, P4, A1, A2),
+ Tuple4<P1, P2, P3, P4>,
+ Tuple2<A1, A2> >(obj, method, MakeTuple(p1, p2, p3, p4));
+ return CallbackFunctor<Tuple2<A1, A2> >(t);
+}
+
+// 4 - 3
+template <class T, typename P1, typename P2, typename P3, typename P4,
+ typename A1, typename A2, typename A3>
+inline CallbackFunctor<Tuple3<A1, A2, A3> >
+CBF(T* obj, void (T::*method)(P1, P2, P3, P4, A1, A2, A3),
+ P1 p1, P2 p2, P3 p3, P4 p4) {
+ Callback3<A1, A2, A3>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, P2, P3, P4, A1, A2, A3),
+ Tuple4<P1, P2, P3, P4>, Tuple3<A1, A2, A3> >
+ (obj, method, MakeTuple(p1, p2, p3, p4));
+ return CallbackFunctor<Tuple3<A1, A2, A3> >(t);
+}
+
+// 4 - 4
+template <class T, typename P1, typename P2, typename P3, typename P4,
+ typename A1, typename A2, typename A3, typename A4>
+inline CallbackFunctor<Tuple4<A1, A2, A3, A4> >
+CBF(T* obj, void (T::*method)(P1, P2, P3, P4, A1, A2, A3, A4),
+ P1 p1, P2 p2, P3 p3, P4 p4) {
+ Callback4<A1, A2, A3>::Type* t =
+ new MutantImpl<T, void (T::*)(P1, P2, P3, P4, A1, A2, A3, A4),
+ Tuple4<P1, P2, P3, P4>, Tuple4<A1, A2, A3, A4> >(obj, method,
+ MakeTuple(p1, p2, p3, p4));
+ return CallbackFunctor<Tuple4<A1, A2, A3, A4> >(t);
+}
+
+
+// Simple task wrapper acting as a functor.
+// Redirects operator() to Task::Run. We cannot delete the inner impl_ object
+// in object's destructor because this object is copied few times inside
+// from GMock machinery.
+struct TaskHolder {
+ explicit TaskHolder(Task* impl) : impl_(impl) {}
+ void operator()() {
+ impl_->Run();
+ delete impl_;
+ impl_ = NULL;
+ }
+ private:
+ TaskHolder();
+ Task* impl_;
+};
+
+#endif // CHROME_FRAME_TEST_HELPER_GMOCK_H_
diff --git a/chrome_frame/test/html_util_test_data/basic_test.html b/chrome_frame/test/html_util_test_data/basic_test.html
new file mode 100644
index 0000000..f0cd17a
--- /dev/null
+++ b/chrome_frame/test/html_util_test_data/basic_test.html
@@ -0,0 +1,11 @@
+<HTML>
+
+ <HEAD>
+ <!-- Note the capitalization in CONtent to test the
+ case-insensitiveness -->
+ <META http-equiv="X-UA-Compatible" CONtent="chrome=1" />
+ </HEAD>
+ <BODY>
+
+ Wooo!
+ </BODY></HTML>
diff --git a/chrome_frame/test/html_util_test_data/degenerate_cases_test.html b/chrome_frame/test/html_util_test_data/degenerate_cases_test.html
new file mode 100644
index 0000000..d527496a
--- /dev/null
+++ b/chrome_frame/test/html_util_test_data/degenerate_cases_test.html
@@ -0,0 +1,7 @@
+<><foo ">
+</head>
+<"foo">
+<!-- Note that the meta tag shouldn't be picked up since we are still
+ inside a quote block. -->
+<META http-equiv="X-UA-Compatible" CONtent="chrome=1" />
+<fee>
diff --git a/chrome_frame/test/html_util_test_data/multiple_tags.html b/chrome_frame/test/html_util_test_data/multiple_tags.html
new file mode 100644
index 0000000..9bd5cea
--- /dev/null
+++ b/chrome_frame/test/html_util_test_data/multiple_tags.html
@@ -0,0 +1,17 @@
+<HTML><HEAD>
+
+ <META http-equiv="X-UA-Compatible" CONtent="chrome=1" />
+ <META http-equiv="X-UA-Compatible" content="chrome=1" />
+ <METAman http-equiv="X-UA-Compatible" CONtent="notchrome=1" />
+ <transMETA http-equiv="X-UA-Compatible" CONtent="notchrome=1" />
+ <IMETAGIRL http-equiv="X-UA-Compatible" CONtent="notchrome=1" />
+ <metA http-equiv="X-UA-Compatible" content="chrome=1" />
+ <!-- shouldn't pick up commented meta tags! -->
+ <!-- <metA http-equiv="X-UA-Compatible" content="chrome=1" /> -->
+
+ <!-- The following cases should also not be matched -->
+ <meta http-equiv="X-UA-Compatibleeee" content="chrome=1" />
+ <meta http-equiv="X-UA-Compatible!" content="chrome=1" />
+ <meta http-equiv="!X-UA-Compatible" content="chrome=1" />
+ <meta http-equiv="\"X-UA-Compatible\"" content="chrome=1" />
+<fee> \ No newline at end of file
diff --git a/chrome_frame/test/html_util_test_data/quotes_test.html b/chrome_frame/test/html_util_test_data/quotes_test.html
new file mode 100644
index 0000000..03ce96d
--- /dev/null
+++ b/chrome_frame/test/html_util_test_data/quotes_test.html
@@ -0,0 +1,10 @@
+<HTML>
+
+ <HEAD>
+ <DANGER red="herring>'" testing = "do'><><>quotes\"\\'work?">
+ <META http-equiv=X-UA-Compatible CONtent="chrome=1" />
+ </HEAD>
+ <BODY>
+
+ Wooo!
+ </BODY></HTML>
diff --git a/chrome_frame/test/html_util_unittests.cc b/chrome_frame/test/html_util_unittests.cc
new file mode 100644
index 0000000..131b185
--- /dev/null
+++ b/chrome_frame/test/html_util_unittests.cc
@@ -0,0 +1,215 @@
+// Copyright (c) 2006-2009 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 <atlsecurity.h>
+#include <shellapi.h>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "base/message_loop.h"
+#include "base/path_service.h"
+#include "base/process_util.h"
+#include "base/ref_counted.h"
+#include "base/scoped_handle.h"
+#include "base/task.h"
+#include "base/win_util.h"
+#include "net/base/net_util.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/html_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class HtmlUtilUnittest : public testing::Test {
+ protected:
+ // Constructor
+ HtmlUtilUnittest() {}
+
+ // Returns the test path given a test case.
+ virtual bool GetTestPath(const std::wstring& test_case, std::wstring* path) {
+ if (!path) {
+ NOTREACHED();
+ return false;
+ }
+
+ std::wstring test_path;
+ if (!PathService::Get(base::DIR_SOURCE_ROOT, &test_path)) {
+ NOTREACHED();
+ return false;
+ }
+
+ file_util::AppendToPath(&test_path, L"chrome_frame");
+ file_util::AppendToPath(&test_path, L"test");
+ file_util::AppendToPath(&test_path, L"html_util_test_data");
+ file_util::AppendToPath(&test_path, test_case);
+
+ *path = test_path;
+ return true;
+ }
+
+ virtual bool GetTestData(const std::wstring& test_case, std::wstring* data) {
+ if (!data) {
+ NOTREACHED();
+ return false;
+ }
+
+ std::wstring path;
+ if (!GetTestPath(test_case, &path)) {
+ NOTREACHED();
+ return false;
+ }
+
+ std::string raw_data;
+ file_util::ReadFileToString(path, &raw_data);
+
+ // Convert to wide using the "best effort" assurance described in
+ // string_util.h
+ data->assign(UTF8ToWide(raw_data));
+ return true;
+ }
+};
+
+TEST_F(HtmlUtilUnittest, BasicTest) {
+ std::wstring test_data;
+ GetTestData(L"basic_test.html", &test_data);
+
+ HTMLScanner scanner(test_data.c_str());
+
+ // Grab the meta tag from the document and ensure that we get exactly one.
+ HTMLScanner::StringRangeList tag_list;
+ scanner.GetTagsByName(L"meta", &tag_list, L"body");
+ ASSERT_EQ(1, tag_list.size());
+
+ // Pull out the http-equiv attribute and check its value:
+ HTMLScanner::StringRange attribute_value;
+ EXPECT_TRUE(tag_list[0].GetTagAttribute(L"http-equiv", &attribute_value));
+ EXPECT_TRUE(attribute_value.Equals(L"X-UA-Compatible"));
+
+ // Pull out the content attribute and check its value:
+ EXPECT_TRUE(tag_list[0].GetTagAttribute(L"content", &attribute_value));
+ EXPECT_TRUE(attribute_value.Equals(L"chrome=1"));
+}
+
+TEST_F(HtmlUtilUnittest, QuotesTest) {
+ std::wstring test_data;
+ GetTestData(L"quotes_test.html", &test_data);
+
+ HTMLScanner scanner(test_data.c_str());
+
+ // Grab the meta tag from the document and ensure that we get exactly one.
+ HTMLScanner::StringRangeList tag_list;
+ scanner.GetTagsByName(L"meta", &tag_list, L"body");
+ ASSERT_EQ(1, tag_list.size());
+
+ // Pull out the http-equiv attribute and check its value:
+ HTMLScanner::StringRange attribute_value;
+ EXPECT_TRUE(tag_list[0].GetTagAttribute(L"http-equiv", &attribute_value));
+ EXPECT_TRUE(attribute_value.Equals(L"X-UA-Compatible"));
+
+ // Pull out the content attribute and check its value:
+ EXPECT_TRUE(tag_list[0].GetTagAttribute(L"content", &attribute_value));
+ EXPECT_TRUE(attribute_value.Equals(L"chrome=1"));
+}
+
+TEST_F(HtmlUtilUnittest, DegenerateCasesTest) {
+ std::wstring test_data;
+ GetTestData(L"degenerate_cases_test.html", &test_data);
+
+ HTMLScanner scanner(test_data.c_str());
+
+ // Scan for meta tags in the document. We expect not to pick up the one
+ // that appears to be there since it is technically inside a quote block.
+ HTMLScanner::StringRangeList tag_list;
+ scanner.GetTagsByName(L"meta", &tag_list, L"body");
+ EXPECT_TRUE(tag_list.empty());
+}
+
+TEST_F(HtmlUtilUnittest, MultipleTagsTest) {
+ std::wstring test_data;
+ GetTestData(L"multiple_tags.html", &test_data);
+
+ HTMLScanner scanner(test_data.c_str());
+
+ // Grab the meta tag from the document and ensure that we get exactly three.
+ HTMLScanner::StringRangeList tag_list;
+ scanner.GetTagsByName(L"meta", &tag_list, L"body");
+ EXPECT_EQ(7, tag_list.size());
+
+ // Pull out the content attribute for each tag and check its value:
+ HTMLScanner::StringRange attribute_value;
+ HTMLScanner::StringRangeList::const_iterator tag_list_iter(
+ tag_list.begin());
+ int valid_tag_count = 0;
+ for (; tag_list_iter != tag_list.end(); tag_list_iter++) {
+ HTMLScanner::StringRange attribute_value;
+ if (tag_list_iter->GetTagAttribute(L"http-equiv", &attribute_value) &&
+ attribute_value.Equals(L"X-UA-Compatible")) {
+ EXPECT_TRUE(tag_list_iter->GetTagAttribute(L"content", &attribute_value));
+ EXPECT_TRUE(attribute_value.Equals(L"chrome=1"));
+ valid_tag_count++;
+ }
+ }
+ EXPECT_EQ(3, valid_tag_count);
+}
+
+TEST_F(HtmlUtilUnittest, ShortDegenerateTest1) {
+ std::wstring test_data(
+ L"<foo><META http-equiv=X-UA-Compatible content='chrome=1'");
+
+ HTMLScanner scanner(test_data.c_str());
+
+ // Scan for meta tags in the document. We expect not to pick up the one
+ // that is there since it is not properly closed.
+ HTMLScanner::StringRangeList tag_list;
+ scanner.GetTagsByName(L"meta", &tag_list, L"body");
+ EXPECT_TRUE(tag_list.empty());
+}
+
+TEST_F(HtmlUtilUnittest, ShortDegenerateTest2) {
+ std::wstring test_data(
+ L"<foo <META http-equiv=X-UA-Compatible content='chrome=1'/>");
+
+ HTMLScanner scanner(test_data.c_str());
+
+ // Scan for meta tags in the document. We expect not to pick up the one
+ // that appears to be there since it is inside a non-closed tag.
+ HTMLScanner::StringRangeList tag_list;
+ scanner.GetTagsByName(L"meta", &tag_list, L"body");
+ EXPECT_TRUE(tag_list.empty());
+}
+
+TEST_F(HtmlUtilUnittest, QuoteInsideHTMLCommentTest) {
+ std::wstring test_data(
+ L"<!-- comment' --><META http-equiv=X-UA-Compatible content='chrome=1'/>");
+
+ HTMLScanner scanner(test_data.c_str());
+
+ // Grab the meta tag from the document and ensure that we get exactly one.
+ HTMLScanner::StringRangeList tag_list;
+ scanner.GetTagsByName(L"meta", &tag_list, L"body");
+ ASSERT_EQ(1, tag_list.size());
+
+ // Pull out the http-equiv attribute and check its value:
+ HTMLScanner::StringRange attribute_value;
+ EXPECT_TRUE(tag_list[0].GetTagAttribute(L"http-equiv", &attribute_value));
+ EXPECT_TRUE(attribute_value.Equals(L"X-UA-Compatible"));
+
+ // Pull out the content attribute and check its value:
+ EXPECT_TRUE(tag_list[0].GetTagAttribute(L"content", &attribute_value));
+ EXPECT_TRUE(attribute_value.Equals(L"chrome=1"));
+}
+
+TEST_F(HtmlUtilUnittest, CloseTagInsideHTMLCommentTest) {
+ std::wstring test_data(
+ L"<!-- comment> <META http-equiv=X-UA-Compatible content='chrome=1'/>-->");
+
+ HTMLScanner scanner(test_data.c_str());
+
+ // Grab the meta tag from the document and ensure that we get exactly one.
+ HTMLScanner::StringRangeList tag_list;
+ scanner.GetTagsByName(L"meta", &tag_list, L"body");
+ ASSERT_TRUE(tag_list.empty());
+}
diff --git a/chrome_frame/test/http_server.cc b/chrome_frame/test/http_server.cc
new file mode 100644
index 0000000..f2cc333
--- /dev/null
+++ b/chrome_frame/test/http_server.cc
@@ -0,0 +1,56 @@
+// Copyright (c) 2006-2009 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/http_server.h"
+
+const wchar_t kDocRoot[] = L"chrome_frame\\test\\data";
+
+void ChromeFrameHTTPServer::SetUp() {
+ std::wstring document_root(kDocRoot);
+ server_ = HTTPTestServer::CreateServer(document_root, NULL, 30, 1000);
+ ASSERT_TRUE(server_ != NULL);
+
+ // copy CFInstance.js into the test directory
+ FilePath cf_source_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &cf_source_path);
+ cf_source_path = cf_source_path.Append(FILE_PATH_LITERAL("chrome_frame"));
+
+ file_util::CopyFile(cf_source_path.Append(FILE_PATH_LITERAL("CFInstance.js")),
+ cf_source_path.Append(
+ FILE_PATH_LITERAL("test")).Append(
+ FILE_PATH_LITERAL("data")).Append(
+ FILE_PATH_LITERAL("CFInstance.js"))); // NOLINT
+}
+
+void ChromeFrameHTTPServer::TearDown() {
+ if (server_) {
+ server_ = NULL;
+ }
+
+ // clobber CFInstance.js
+ FilePath cfi_path;
+ PathService::Get(base::DIR_SOURCE_ROOT, &cfi_path);
+ cfi_path = cfi_path
+ .Append(FILE_PATH_LITERAL("chrome_frame"))
+ .Append(FILE_PATH_LITERAL("test"))
+ .Append(FILE_PATH_LITERAL("data"))
+ .Append(FILE_PATH_LITERAL("CFInstance.js"));
+
+ file_util::Delete(cfi_path, false);
+}
+
+bool ChromeFrameHTTPServer::WaitToFinish(int milliseconds) {
+ if (!server_)
+ return true;
+
+ return server_->WaitToFinish(milliseconds);
+}
+
+GURL ChromeFrameHTTPServer::Resolve(const wchar_t* relative_url) {
+ return server_->TestServerPageW(relative_url);
+}
+
+std::wstring ChromeFrameHTTPServer::GetDataDir() {
+ return server_->GetDataDirectory().ToWStringHack();
+}
+
diff --git a/chrome_frame/test/http_server.h b/chrome_frame/test/http_server.h
new file mode 100644
index 0000000..acac5b5
--- /dev/null
+++ b/chrome_frame/test/http_server.h
@@ -0,0 +1,32 @@
+// Copyright (c) 2006-2009 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.
+#ifndef CHROME_FRAME_TEST_HTTP_SERVER_H_
+#define CHROME_FRAME_TEST_HTTP_SERVER_H_
+
+#include <windows.h>
+#include <string>
+
+#include "googleurl/src/gurl.h"
+#include "base/ref_counted.h"
+#include "net/url_request/url_request_unittest.h"
+
+// chrome frame specilization of http server from net.
+class ChromeFrameHTTPServer {
+ public:
+ void SetUp();
+ void TearDown();
+ bool WaitToFinish(int milliseconds);
+ GURL Resolve(const wchar_t* relative_url);
+ std::wstring GetDataDir();
+
+ HTTPTestServer* server() {
+ return server_;
+ }
+
+ protected:
+ scoped_refptr<HTTPTestServer> server_;
+};
+
+#endif // CHROME_FRAME_TEST_HTTP_SERVER_H_
+
diff --git a/chrome_frame/test/icu_stubs_unittests.cc b/chrome_frame/test/icu_stubs_unittests.cc
new file mode 100644
index 0000000..4da4a40
--- /dev/null
+++ b/chrome_frame/test/icu_stubs_unittests.cc
@@ -0,0 +1,73 @@
+// Copyright (c) 2006-2009 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/chrome_frame_unittests.h"
+
+// Need to include these first since they're included
+#include "base/logging.h"
+#include "base/string_util.h"
+#include "googleurl/src/url_canon.h"
+
+// Include the implementation of our stubs into a special namespace so that
+// we can separate them from Chrome's implementation.
+namespace icu_stubs {
+ // This struct is only to avoid build problems for the two googleurl stubs
+ // that currently are noops.
+ struct CanonOutputW { };
+
+ #include "chrome_frame/icu_stubs.cc"
+} // namespace icu_stubs
+
+// anonymous namespace for test data.
+namespace {
+
+ // Test strings borrowed from base/string_util_unittest.cc
+ static const wchar_t* const kConvertRoundtripCases[] = {
+ L"",
+ L"Google Vid¯ôfY »"
+ L"\x7f51\x9875\x0020\x56fe\x7247\x0020\x8d44\x8baf\x66f4\x591a\x0020\x00bb",
+ // " ±³ºÌü¹¿Â ÃÄÌÂ"
+ L"\x03a0\x03b1\x03b3\x03ba\x03cc\x03c3\x03bc\x03b9"
+ L"\x03bf\x03c2\x0020\x0399\x03c3\x03c4\x03cc\x03c2",
+ // ">8A: AB@0=8F =0 @CAA:><"
+ L"\x041f\x043e\x0438\x0441\x043a\x0020\x0441\x0442"
+ L"\x0440\x0430\x043d\x0438\x0446\x0020\x043d\x0430"
+ L"\x0020\x0440\x0443\x0441\x0441\x043a\x043e\x043c",
+ // "È´ÌÁD¾¤Â"
+ L"\xc804\xccb4\xc11c\xbe44\xc2a4",
+
+ // Test characters that take more than 16 bits. This will depend on whether
+ // wchar_t is 16 or 32 bits.
+ #if defined(WCHAR_T_IS_UTF16)
+ L"\xd800\xdf00",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\xd807\xdd40\xd807\xdd41\xd807\xdd42\xd807\xdd43\xd807\xdd44",
+ #elif defined(WCHAR_T_IS_UTF32)
+ L"\x10300",
+ // ????? (Mathematical Alphanumeric Symbols (U+011d40 - U+011d44 : A,B,C,D,E)
+ L"\x11d40\x11d41\x11d42\x11d43\x11d44",
+ #endif
+ };
+
+} // namespace
+
+TEST(IcuStubsTests, UTF8AndWideStubTest) {
+ // Test code borrowed from ConvertUTF8AndWide in base/string_util_unittest.cc.
+
+ // The difference is that we want to make sure that our stubs work the same
+ // way as chrome's implementation of WideToUTF8 and UTF8ToWide.
+ for (size_t i = 0; i < arraysize(kConvertRoundtripCases); ++i) {
+ std::ostringstream utf8_base, utf8_stub;
+ utf8_base << WideToUTF8(kConvertRoundtripCases[i]);
+ utf8_stub << icu_stubs::WideToUTF8(kConvertRoundtripCases[i]);
+
+ EXPECT_EQ(utf8_base.str(), utf8_stub.str());
+
+ std::wostringstream wide_base, wide_stub;
+ wide_base << UTF8ToWide(utf8_base.str());
+ wide_stub << icu_stubs::UTF8ToWide(utf8_base.str());
+
+ EXPECT_EQ(wide_base.str(), wide_stub.str());
+ }
+}
diff --git a/chrome_frame/test/net/dialog_watchdog.cc b/chrome_frame/test/net/dialog_watchdog.cc
new file mode 100644
index 0000000..27a01a0
--- /dev/null
+++ b/chrome_frame/test/net/dialog_watchdog.cc
@@ -0,0 +1,146 @@
+// Copyright (c) 2009 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 <oleacc.h>
+
+#include "chrome_frame/test/net/dialog_watchdog.h"
+
+#include "base/logging.h"
+#include "base/scoped_comptr_win.h"
+#include "base/string_util.h"
+
+#include "chrome_frame/test/chrome_frame_test_utils.h"
+#include "chrome_frame/function_stub.h"
+
+namespace {
+// 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;
+ 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;
+}
+
+} // namespace
+
+SupplyProxyCredentials::SupplyProxyCredentials(const char* username,
+ const char* password)
+ : username_(username), password_(password) {
+}
+
+bool SupplyProxyCredentials::OnDialogDetected(HWND hwnd,
+ const std::string& caption) {
+ // IE's dialog caption (en-US).
+ if (caption.compare("Windows Security") != 0)
+ return false;
+
+ 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.
+ chrome_frame_test::ForceSetForegroundWindow(hwnd);
+ CHECK(SetFocusToAccessibleWindow(props.username_));
+ chrome_frame_test::SendString(username_.c_str());
+ Sleep(100);
+
+ chrome_frame_test::SendVirtualKey(VK_TAB);
+ Sleep(100);
+ chrome_frame_test::SendString(password_.c_str());
+
+ Sleep(100);
+ chrome_frame_test::SendVirtualKey(VK_RETURN);
+
+ return true;
+}
+
+// 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;
+}
+
+DialogWatchdog::DialogWatchdog() : hook_(NULL), hook_stub_(NULL) {
+ Initialize();
+}
+
+DialogWatchdog::~DialogWatchdog() {
+ Uninitialize();
+}
+
+bool DialogWatchdog::Initialize() {
+ DCHECK(hook_ == NULL);
+ DCHECK(hook_stub_ == NULL);
+ hook_stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(this),
+ WinEventHook);
+ hook_ = SetWinEventHook(EVENT_OBJECT_SHOW, EVENT_OBJECT_SHOW, NULL,
+ reinterpret_cast<WINEVENTPROC>(hook_stub_->code()), 0,
+ 0, WINEVENT_OUTOFCONTEXT | WINEVENT_SKIPOWNPROCESS);
+
+ return hook_ != NULL;
+}
+
+void DialogWatchdog::Uninitialize() {
+ if (hook_) {
+ ::UnhookWinEvent(hook_);
+ hook_ = NULL;
+ FunctionStub::Destroy(hook_stub_);
+ hook_stub_ = NULL;
+ }
+}
+
+// static
+void DialogWatchdog::WinEventHook(DialogWatchdog* me, HWINEVENTHOOK hook,
+ DWORD event, HWND hwnd, LONG object_id,
+ LONG child_id, DWORD event_thread_id,
+ DWORD event_time) {
+ // Check for a dialog class ("#32770") and notify observers if we find one.
+ char class_name[MAX_PATH] = {0};
+ ::GetClassNameA(hwnd, class_name, arraysize(class_name));
+ if (lstrcmpA(class_name, "#32770") == 0) {
+ int len = ::GetWindowTextLength(hwnd);
+ std::string text;
+ ::GetWindowTextA(hwnd, WriteInto(&text, len + 1), len + 1);
+ me->OnDialogFound(hwnd, text);
+ }
+}
+
+void DialogWatchdog::OnDialogFound(HWND hwnd, const std::string& caption) {
+ std::vector<DialogWatchdogObserver*>::iterator it = observers_.begin();
+ while (it != observers_.end()) {
+ if ((*it)->OnDialogDetected(hwnd, caption))
+ break;
+ it++;
+ }
+}
diff --git a/chrome_frame/test/net/dialog_watchdog.h b/chrome_frame/test/net/dialog_watchdog.h
new file mode 100644
index 0000000..dfb8989
--- /dev/null
+++ b/chrome_frame/test/net/dialog_watchdog.h
@@ -0,0 +1,64 @@
+// Copyright (c) 2009 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.
+
+#ifndef CHROME_FRAME_TEST_NET_DIALOG_WATCHDOG_H_
+#define CHROME_FRAME_TEST_NET_DIALOG_WATCHDOG_H_
+
+#include <windows.h>
+
+#include <string>
+#include <vector>
+
+struct FunctionStub;
+
+class DialogWatchdogObserver { // NOLINT
+ public:
+ // returns true if this observer handled the dialog.
+ virtual bool OnDialogDetected(HWND hwnd, const std::string& caption) = 0;
+};
+
+class SupplyProxyCredentials : public DialogWatchdogObserver {
+ public:
+ SupplyProxyCredentials(const char* username, const char* password);
+
+ protected:
+ struct DialogProps {
+ HWND username_;
+ HWND password_;
+ };
+
+ virtual bool OnDialogDetected(HWND hwnd, const std::string& caption);
+ static BOOL CALLBACK EnumChildren(HWND hwnd, LPARAM param);
+
+ protected:
+ std::string username_;
+ std::string password_;
+};
+
+class DialogWatchdog {
+ public:
+ DialogWatchdog();
+ ~DialogWatchdog();
+
+ inline void AddObserver(DialogWatchdogObserver* observer) {
+ observers_.push_back(observer);
+ }
+
+ bool Initialize();
+ void Uninitialize();
+
+ protected:
+ static void CALLBACK WinEventHook(DialogWatchdog* me, HWINEVENTHOOK hook,
+ DWORD event, HWND hwnd, LONG object_id, LONG child_id,
+ DWORD event_thread_id, DWORD event_time);
+
+ void OnDialogFound(HWND hwnd, const std::string& caption);
+
+ protected:
+ HWINEVENTHOOK hook_;
+ std::vector<DialogWatchdogObserver*> observers_;
+ FunctionStub* hook_stub_;
+};
+
+#endif // CHROME_FRAME_TEST_NET_DIALOG_WATCHDOG_H_
diff --git a/chrome_frame/test/net/fake_external_tab.cc b/chrome_frame/test/net/fake_external_tab.cc
new file mode 100644
index 0000000..eebd2d9b
--- /dev/null
+++ b/chrome_frame/test/net/fake_external_tab.cc
@@ -0,0 +1,391 @@
+// Copyright (c) 2009 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 <exdisp.h>
+
+#include "app/app_paths.h"
+#include "app/resource_bundle.h"
+#include "app/win_util.h"
+
+#include "base/command_line.h"
+#include "base/file_util.h"
+#include "base/icu_util.h"
+#include "base/path_service.h"
+#include "base/scoped_bstr_win.h"
+#include "base/scoped_comptr_win.h"
+#include "base/scoped_variant_win.h"
+
+#include "chrome/browser/browser_prefs.h"
+#include "chrome/browser/chrome_thread.h"
+#include "chrome/browser/process_singleton.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/browser/renderer_host/render_process_host.h"
+
+#include "chrome_frame/utils.h"
+#include "chrome_frame/test/chrome_frame_test_utils.h"
+#include "chrome_frame/test/net/dialog_watchdog.h"
+#include "chrome_frame/test/net/test_automation_resource_message_filter.h"
+
+namespace {
+
+// A special command line switch to allow developers to manually launch the
+// browser and debug CF inside the browser.
+const wchar_t kManualBrowserLaunch[] = L"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 wchar_t kPromptAfterSetup[] = L"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>";
+
+bool ShouldLaunchBrowser() {
+ return !CommandLine::ForCurrentProcess()->HasSwitch(kManualBrowserLaunch);
+}
+
+bool PromptAfterSetup() {
+ return CommandLine::ForCurrentProcess()->HasSwitch(kPromptAfterSetup);
+}
+
+} // end namespace
+
+FakeExternalTab::FakeExternalTab() {
+ PathService::Get(chrome::DIR_USER_DATA, &overridden_user_dir_);
+ user_data_dir_ = FilePath::FromWStringHack(GetProfilePath());
+ PathService::Override(chrome::DIR_USER_DATA, user_data_dir_);
+ process_singleton_.reset(new ProcessSingleton(user_data_dir_));
+}
+
+FakeExternalTab::~FakeExternalTab() {
+ if (!overridden_user_dir_.empty()) {
+ PathService::Override(chrome::DIR_USER_DATA, overridden_user_dir_);
+ }
+}
+
+std::wstring FakeExternalTab::GetProfileName() {
+ return L"iexplore";
+}
+
+std::wstring FakeExternalTab::GetProfilePath() {
+ std::wstring path;
+ GetUserProfileBaseDirectory(&path);
+ file_util::AppendToPath(&path, GetProfileName());
+ return path;
+}
+
+void FakeExternalTab::Initialize() {
+ DCHECK(g_browser_process == NULL);
+
+ // The gears plugin causes the PluginRequestInterceptor to kick in and it
+ // will cause problems when it tries to intercept URL requests.
+ PathService::Override(chrome::FILE_GEARS_PLUGIN, FilePath());
+
+ icu_util::Initialize();
+
+ chrome::RegisterPathProvider();
+ app::RegisterPathProvider();
+
+ ResourceBundle::InitSharedInstance(L"en-US");
+ ResourceBundle::GetSharedInstance().LoadThemeResources();
+
+ const CommandLine* cmd = CommandLine::ForCurrentProcess();
+ browser_process_.reset(new BrowserProcessImpl(*cmd));
+ RenderProcessHost::set_run_renderer_in_process(true);
+ // BrowserProcessImpl's constructor should set g_browser_process.
+ DCHECK(g_browser_process);
+
+ Profile* profile = g_browser_process->profile_manager()->
+ GetDefaultProfile(FilePath(user_data()));
+ PrefService* prefs = profile->GetPrefs();
+ PrefService* local_state = browser_process_->local_state();
+ local_state->RegisterStringPref(prefs::kApplicationLocale, L"");
+ local_state->RegisterBooleanPref(prefs::kMetricsReportingEnabled, false);
+
+ browser::RegisterAllPrefs(prefs, local_state);
+
+ // Override some settings to avoid hitting some preferences that have not
+ // been registered.
+ prefs->SetBoolean(prefs::kPasswordManagerEnabled, false);
+ prefs->SetBoolean(prefs::kAlternateErrorPagesEnabled, false);
+ prefs->SetBoolean(prefs::kSafeBrowsingEnabled, false);
+
+ profile->InitExtensions();
+}
+
+void FakeExternalTab::Shutdown() {
+ browser_process_.reset();
+ g_browser_process = NULL;
+ process_singleton_.reset();
+
+ ResourceBundle::CleanupSharedInstance();
+}
+
+CFUrlRequestUnittestRunner::CFUrlRequestUnittestRunner(int argc, char** argv)
+ : NetTestSuite(argc, argv),
+ chrome_frame_html_("/chrome_frame", kChromeFrameHtml) {
+ fake_chrome_.Initialize();
+ pss_subclass_.reset(new ProcessSingletonSubclass(this));
+ EXPECT_TRUE(pss_subclass_->Subclass(fake_chrome_.user_data()));
+ StartChromeFrameInHostBrowser();
+}
+
+CFUrlRequestUnittestRunner::~CFUrlRequestUnittestRunner() {
+ fake_chrome_.Shutdown();
+}
+
+DWORD WINAPI NavigateIE(void* param) {
+ return 0;
+ win_util::ScopedCOMInitializer com;
+ BSTR url = reinterpret_cast<BSTR>(param);
+
+ bool found = false;
+ int retries = 0;
+ const int kMaxRetries = 20;
+ while (!found && retries < kMaxRetries) {
+ ScopedComPtr<IShellWindows> windows;
+ HRESULT hr = ::CoCreateInstance(__uuidof(ShellWindows), NULL, CLSCTX_ALL,
+ IID_IShellWindows, reinterpret_cast<void**>(windows.Receive()));
+ DCHECK(SUCCEEDED(hr)) << "CoCreateInstance";
+
+ if (SUCCEEDED(hr)) {
+ hr = HRESULT_FROM_WIN32(ERROR_NOT_FOUND);
+ long count = 0; // NOLINT
+ windows->get_Count(&count);
+ VARIANT i = { VT_I4 };
+ for (i.lVal = 0; i.lVal < count; ++i.lVal) {
+ ScopedComPtr<IDispatch> folder;
+ windows->Item(i, folder.Receive());
+ if (folder != NULL) {
+ ScopedComPtr<IWebBrowser2> browser;
+ if (SUCCEEDED(browser.QueryFrom(folder))) {
+ found = true;
+ browser->Stop();
+ Sleep(1000);
+ VARIANT empty = ScopedVariant::kEmptyVariant;
+ hr = browser->Navigate(url, &empty, &empty, &empty, &empty);
+ DCHECK(SUCCEEDED(hr)) << "Failed to navigate";
+ break;
+ }
+ }
+ }
+ }
+ if (!found) {
+ DLOG(INFO) << "Waiting for browser to initialize...";
+ ::Sleep(100);
+ retries++;
+ }
+ }
+
+ DCHECK(retries < kMaxRetries);
+ DCHECK(found);
+
+ ::SysFreeString(url);
+
+ return 0;
+}
+
+void CFUrlRequestUnittestRunner::StartChromeFrameInHostBrowser() {
+ if (!ShouldLaunchBrowser())
+ return;
+
+ win_util::ScopedCOMInitializer com;
+ chrome_frame_test::CloseAllIEWindows();
+
+ test_http_server_.reset(new test_server::SimpleWebServer(kTestServerPort));
+ test_http_server_->AddResponse(&chrome_frame_html_);
+ std::wstring url(StringPrintf(L"http://localhost:%i/chrome_frame",
+ kTestServerPort).c_str());
+
+ // Launch IE. This launches IE correctly on Vista too.
+ 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 (ShouldLaunchBrowser()) {
+ win_util::ScopedCOMInitializer com;
+ chrome_frame_test::CloseAllIEWindows();
+ }
+}
+
+// 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.
+ InitializeLogging();
+ base::Time::EnableHiResClockForTests();
+
+#if !defined(PURIFY) && defined(OS_WIN)
+ logging::SetLogAssertHandler(UnitTestAssertHandler);
+#endif // !defined(PURIFY)
+
+ // Next, do some initialization for NetTestSuite.
+ NetTestSuite::InitializeTestThread();
+}
+
+void CFUrlRequestUnittestRunner::Shutdown() {
+ DCHECK(::GetCurrentThreadId() == test_thread_id_);
+ NetTestSuite::Shutdown();
+}
+
+void CFUrlRequestUnittestRunner::OnConnectAutomationProviderToChannel(
+ const std::string& channel_id) {
+ Profile* profile = g_browser_process->profile_manager()->
+ GetDefaultProfile(fake_chrome_.user_data());
+
+ AutomationProviderList* list =
+ g_browser_process->InitAutomationProviderList();
+ DCHECK(list);
+ list->AddProvider(TestAutomationProvider::NewAutomationProvider(profile,
+ channel_id, this));
+}
+
+void CFUrlRequestUnittestRunner::OnInitialTabLoaded() {
+ test_http_server_.reset();
+ StartTests();
+}
+
+void CFUrlRequestUnittestRunner::RunMainUIThread() {
+ DCHECK(MessageLoop::current());
+ DCHECK(MessageLoop::current()->type() == MessageLoop::TYPE_UI);
+
+ // Register the main thread by instantiating it, but don't call any methods.
+ ChromeThread main_thread;
+ DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
+
+ MessageLoop::current()->Run();
+}
+
+void CFUrlRequestUnittestRunner::StartTests() {
+ if (PromptAfterSetup())
+ MessageBoxA(NULL, "click ok to run", "", MB_OK);
+
+ DCHECK_EQ(test_thread_.IsValid(), false);
+ test_thread_.Set(::CreateThread(NULL, 0, RunAllUnittests, this, 0,
+ &test_thread_id_));
+ DCHECK(test_thread_.IsValid());
+}
+
+// static
+DWORD CFUrlRequestUnittestRunner::RunAllUnittests(void* param) {
+ PlatformThread::SetName("CFUrlRequestUnittestRunner");
+ CFUrlRequestUnittestRunner* me =
+ reinterpret_cast<CFUrlRequestUnittestRunner*>(param);
+ me->Run();
+ me->fake_chrome_.ui_loop()->PostTask(FROM_HERE,
+ NewRunnableFunction(TakeDownBrowser, me));
+ return 0;
+}
+
+// static
+void CFUrlRequestUnittestRunner::TakeDownBrowser(
+ CFUrlRequestUnittestRunner* me) {
+ if (PromptAfterSetup())
+ MessageBoxA(NULL, "click ok to exit", "", MB_OK);
+
+ me->ShutDownHostBrowser();
+}
+
+void CFUrlRequestUnittestRunner::InitializeLogging() {
+ FilePath exe;
+ PathService::Get(base::FILE_EXE, &exe);
+ FilePath log_filename = exe.ReplaceExtension(FILE_PATH_LITERAL("log"));
+ logging::InitLogging(log_filename.value().c_str(),
+ logging::LOG_TO_BOTH_FILE_AND_SYSTEM_DEBUG_LOG,
+ logging::LOCK_LOG_FILE,
+ logging::DELETE_OLD_LOG_FILE);
+ // 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 FilterDisabledTests() {
+ if (::testing::FLAGS_gtest_filter.GetLength() &&
+ ::testing::FLAGS_gtest_filter.Compare("*") != 0) {
+ // 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.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",
+
+ // 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",
+
+ // Tests chrome's network stack's cache (might not apply to CF).
+ "URLRequestTestHTTP.VaryHeader",
+
+ // 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",
+ };
+
+ std::string filter("-"); // All following filters will be negative.
+ for (int i = 0; i < arraysize(disabled_tests); ++i) {
+ if (i > 0)
+ filter += ":";
+ filter += disabled_tests[i];
+ }
+
+ ::testing::FLAGS_gtest_filter = filter;
+}
+
+int main(int argc, char** argv) {
+ DialogWatchdog watchdog;
+ // See url_request_unittest.cc for these credentials.
+ SupplyProxyCredentials credentials("user", "secret");
+ watchdog.AddObserver(&credentials);
+ testing::InitGoogleTest(&argc, argv);
+ FilterDisabledTests();
+ CFUrlRequestUnittestRunner test_suite(argc, argv);
+ test_suite.RunMainUIThread();
+ return 0;
+}
diff --git a/chrome_frame/test/net/fake_external_tab.h b/chrome_frame/test/net/fake_external_tab.h
new file mode 100644
index 0000000..6ce4f93
--- /dev/null
+++ b/chrome_frame/test/net/fake_external_tab.h
@@ -0,0 +1,106 @@
+// Copyright (c) 2009 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.
+
+#ifndef CHROME_FRAME_TEST_NET_FAKE_EXTERNAL_TAB_H_
+#define CHROME_FRAME_TEST_NET_FAKE_EXTERNAL_TAB_H_
+
+#include <string>
+
+#include "base/file_path.h"
+#include "base/message_loop.h"
+
+#include "chrome/app/scoped_ole_initializer.h"
+#include "chrome/browser/browser_process_impl.h"
+
+#include "chrome_frame/test/test_server.h"
+#include "chrome_frame/test/net/test_automation_provider.h"
+#include "chrome_frame/test/net/process_singleton_subclass.h"
+
+#include "net/base/net_test_suite.h"
+
+class ProcessSingleton;
+
+class FakeExternalTab {
+ public:
+ FakeExternalTab();
+ ~FakeExternalTab();
+
+ virtual std::wstring GetProfileName();
+
+ virtual std::wstring GetProfilePath();
+ virtual void Initialize();
+ virtual void Shutdown();
+
+ const FilePath& user_data() const {
+ return user_data_dir_;
+ }
+
+ MessageLoopForUI* ui_loop() {
+ return &loop_;
+ }
+
+ protected:
+ MessageLoopForUI loop_;
+ scoped_ptr<BrowserProcess> browser_process_;
+ FilePath overridden_user_dir_;
+ FilePath user_data_dir_;
+ ScopedOleInitializer ole_initializer_; // For RegisterDropTarget etc to work.
+ scoped_ptr<ProcessSingleton> process_singleton_;
+};
+
+// The "master class" that spins the UI and test threads.
+class CFUrlRequestUnittestRunner
+ : public NetTestSuite,
+ public ProcessSingletonSubclassDelegate,
+ public TestAutomationProviderDelegate {
+ public:
+ CFUrlRequestUnittestRunner(int argc, char** argv);
+ ~CFUrlRequestUnittestRunner();
+
+ virtual void StartChromeFrameInHostBrowser();
+
+ virtual void ShutDownHostBrowser();
+
+ // Overrides to not call icu initialize
+ virtual void Initialize();
+ virtual void Shutdown();
+
+ // ProcessSingletonSubclassDelegate.
+ virtual void OnConnectAutomationProviderToChannel(
+ const std::string& channel_id);
+
+ // TestAutomationProviderDelegate.
+ virtual void OnInitialTabLoaded();
+
+ void RunMainUIThread();
+
+ void StartTests();
+
+ protected:
+ // This is the thread that runs all the UrlRequest tests.
+ // Within its context, the Initialize() and Shutdown() routines above
+ // will be called.
+ static DWORD WINAPI RunAllUnittests(void* param);
+
+ static void TakeDownBrowser(CFUrlRequestUnittestRunner* me);
+
+ protected:
+ // Borrowed from TestSuite::Initialize().
+ void InitializeLogging();
+
+ protected:
+ ScopedHandle test_thread_;
+ DWORD test_thread_id_;
+ scoped_ptr<MessageLoop> test_thread_message_loop_;
+
+ scoped_ptr<test_server::SimpleWebServer> test_http_server_;
+ test_server::SimpleResponse chrome_frame_html_;
+
+ // The fake chrome instance. This instance owns the UI message loop
+ // on the main thread.
+ FakeExternalTab fake_chrome_;
+ scoped_ptr<ProcessSingletonSubclass> pss_subclass_;
+};
+
+#endif // CHROME_FRAME_TEST_NET_FAKE_EXTERNAL_TAB_H_
diff --git a/chrome_frame/test/net/process_singleton_subclass.cc b/chrome_frame/test/net/process_singleton_subclass.cc
new file mode 100644
index 0000000..2206a74
--- /dev/null
+++ b/chrome_frame/test/net/process_singleton_subclass.cc
@@ -0,0 +1,111 @@
+// Copyright (c) 2009 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/process_singleton_subclass.h"
+
+#include "base/command_line.h"
+#include "base/path_service.h"
+#include "base/string_util.h"
+#include "chrome/browser/browser_process_impl.h"
+#include "chrome/browser/profile_manager.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/common/chrome_switches.h"
+#include "chrome_frame/test/net/test_automation_provider.h"
+#include "chrome_frame/function_stub.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+ProcessSingletonSubclass::ProcessSingletonSubclass(
+ ProcessSingletonSubclassDelegate* delegate)
+ : stub_(NULL), delegate_(delegate), original_wndproc_(NULL) {
+}
+
+ProcessSingletonSubclass::~ProcessSingletonSubclass() {
+ if (stub_) {
+ stub_->BypassStub(reinterpret_cast<void*>(original_wndproc_));
+ }
+}
+
+bool ProcessSingletonSubclass::Subclass(const FilePath& user_data_dir) {
+ DCHECK(stub_ == NULL);
+ DCHECK(original_wndproc_ == NULL);
+ HWND hwnd = FindWindowEx(HWND_MESSAGE, NULL, chrome::kMessageWindowClass,
+ user_data_dir.ToWStringHack().c_str());
+ if (!::IsWindow(hwnd))
+ return false;
+
+ // The window must be in this process for us to be able to subclass it.
+ DWORD pid = 0;
+ ::GetWindowThreadProcessId(hwnd, &pid);
+ EXPECT_EQ(pid, ::GetCurrentProcessId());
+
+ original_wndproc_ = reinterpret_cast<WNDPROC>(::GetWindowLongPtr(hwnd,
+ GWLP_WNDPROC));
+ stub_ = FunctionStub::Create(reinterpret_cast<uintptr_t>(this),
+ &SubclassWndProc);
+ DCHECK(stub_);
+ ::SetWindowLongPtr(hwnd, GWLP_WNDPROC,
+ reinterpret_cast<LONG_PTR>(stub_->code()));
+ return true;
+}
+
+// static
+LRESULT ProcessSingletonSubclass::SubclassWndProc(ProcessSingletonSubclass* me,
+ HWND hwnd, UINT msg,
+ WPARAM wp, LPARAM lp) {
+ switch (msg) {
+ case WM_COPYDATA:
+ return me->OnCopyData(hwnd, reinterpret_cast<HWND>(wp),
+ reinterpret_cast<COPYDATASTRUCT*>(lp));
+ default:
+ break;
+ }
+
+ return me->original_wndproc_(hwnd, msg, wp, lp);
+}
+
+// static
+LRESULT ProcessSingletonSubclass::OnCopyData(HWND hwnd, HWND from_hwnd,
+ const COPYDATASTRUCT* cds) {
+ // We should have enough room for the shortest command (min_message_size)
+ // and also be a multiple of wchar_t bytes. The shortest command
+ // possible is L"START\0\0" (empty current directory and command line).
+ static const int kMinMessageSize = sizeof(L"START\0");
+ EXPECT_TRUE(kMinMessageSize <= cds->cbData);
+
+ if (kMinMessageSize > cds->cbData)
+ return TRUE;
+
+ // We split the string into 4 parts on NULLs.
+ const wchar_t* begin = reinterpret_cast<const wchar_t*>(cds->lpData);
+ const wchar_t* end = begin + (cds->cbData / sizeof(wchar_t));
+ const wchar_t kNull = L'\0';
+ const wchar_t* eos = wmemchr(begin, kNull, end - begin);
+ EXPECT_NE(eos, end);
+ if (lstrcmpW(begin, L"START") == 0) {
+ begin = eos + 1;
+ EXPECT_TRUE(begin <= end);
+ eos = wmemchr(begin, kNull, end - begin);
+ EXPECT_NE(eos, end);
+
+ // Get current directory.
+ const wchar_t* cur_dir = begin;
+ begin = eos + 1;
+ EXPECT_TRUE(begin <= end);
+ eos = wmemchr(begin, kNull, end - begin);
+ // eos might be equal to end at this point.
+
+ // Get command line.
+ std::wstring cmd_line(begin, static_cast<size_t>(end - begin));
+
+ CommandLine parsed_command_line(L"");
+ parsed_command_line.ParseFromString(cmd_line);
+ std::string channel_id(WideToASCII(parsed_command_line.GetSwitchValue(
+ switches::kAutomationClientChannelID)));
+ EXPECT_FALSE(channel_id.empty());
+
+ delegate_->OnConnectAutomationProviderToChannel(channel_id);
+ }
+ return TRUE;
+}
diff --git a/chrome_frame/test/net/process_singleton_subclass.h b/chrome_frame/test/net/process_singleton_subclass.h
new file mode 100644
index 0000000..dd9ce36
--- /dev/null
+++ b/chrome_frame/test/net/process_singleton_subclass.h
@@ -0,0 +1,36 @@
+// Copyright (c) 2009 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.
+#ifndef CHROME_FRAME_TEST_NET_PROCESS_SINGLETON_SUBCLASS_H_
+#define CHROME_FRAME_TEST_NET_PROCESS_SINGLETON_SUBCLASS_H_
+
+#include <windows.h>
+#include "base/file_path.h"
+
+struct FunctionStub;
+
+class ProcessSingletonSubclassDelegate {
+ public:
+ virtual void OnConnectAutomationProviderToChannel(
+ const std::string& channel_id) = 0;
+};
+
+class ProcessSingletonSubclass {
+ public:
+ ProcessSingletonSubclass(ProcessSingletonSubclassDelegate* delegate);
+ ~ProcessSingletonSubclass();
+
+ bool Subclass(const FilePath& user_data_dir);
+
+ protected:
+ static LRESULT CALLBACK SubclassWndProc(ProcessSingletonSubclass* me,
+ HWND hwnd, UINT msg, WPARAM wp, LPARAM lp);
+ LRESULT OnCopyData(HWND hwnd, HWND from_hwnd, const COPYDATASTRUCT* cds);
+ protected:
+ FunctionStub* stub_;
+ ProcessSingletonSubclassDelegate* delegate_;
+ WNDPROC original_wndproc_;
+};
+
+#endif // CHROME_FRAME_TEST_NET_PROCESS_SINGLETON_SUBCLASS_H_
+
diff --git a/chrome_frame/test/net/test_automation_provider.cc b/chrome_frame/test/net/test_automation_provider.cc
new file mode 100644
index 0000000..3a56aa4
--- /dev/null
+++ b/chrome_frame/test/net/test_automation_provider.cc
@@ -0,0 +1,89 @@
+// Copyright (c) 2009 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/test_automation_provider.h"
+
+#include "base/command_line.h"
+#include "chrome/test/automation/automation_messages.h"
+
+#include "chrome_frame/test/net/test_automation_resource_message_filter.h"
+
+namespace {
+
+// A special command line switch to just run the unit tests without CF in
+// the picture. Can be useful when the harness itself needs to be debugged.
+const wchar_t kNoCfTestRun[] = L"no-cf-test-run";
+
+bool CFTestsDisabled() {
+ static bool switch_present = CommandLine::ForCurrentProcess()->
+ HasSwitch(kNoCfTestRun);
+ return switch_present;
+}
+
+} // end namespace
+
+TestAutomationProvider::TestAutomationProvider(
+ Profile* profile,
+ TestAutomationProviderDelegate* delegate)
+ : AutomationProvider(profile), tab_handle_(-1), delegate_(delegate) {
+ filter_ = new TestAutomationResourceMessageFilter(this);
+ URLRequest::RegisterRequestInterceptor(this);
+}
+
+TestAutomationProvider::~TestAutomationProvider() {
+ URLRequest::UnregisterRequestInterceptor(this);
+}
+
+void TestAutomationProvider::OnMessageReceived(const IPC::Message& msg) {
+ if (filter_->OnMessageReceived(msg))
+ return; // Message handled by the filter.
+
+ __super::OnMessageReceived(msg);
+}
+
+// IPC override to grab the tab handle.
+bool TestAutomationProvider::Send(IPC::Message* msg) {
+ if (msg->type() == AutomationMsg_TabLoaded::ID) {
+ DCHECK(tab_handle_ == -1) << "Currently only support one tab";
+ void* iter = NULL;
+ CHECK(msg->ReadInt(&iter, &tab_handle_));
+ DLOG(INFO) << "Got tab handle: " << tab_handle_;
+ DCHECK(tab_handle_ != -1 && tab_handle_ != 0);
+ delegate_->OnInitialTabLoaded();
+ }
+
+ return AutomationProvider::Send(msg);
+}
+
+URLRequestJob* TestAutomationProvider::MaybeIntercept(URLRequest* request) {
+ if (CFTestsDisabled())
+ return NULL;
+
+ if (request->url().SchemeIs("http") || request->url().SchemeIs("https")) {
+ // Only look at requests that don't have any user data.
+ // ResourceDispatcherHost uses the user data for requests that it manages.
+ // We don't want to mess with those.
+
+ // We could also check if the current thread is our TestUrlRequest thread
+ // and only intercept requests that belong to that thread.
+ if (request->GetUserData(NULL) == NULL) {
+ DCHECK(tab_handle_ != -1);
+ URLRequestAutomationJob* job = new URLRequestAutomationJob(request,
+ tab_handle_, filter_);
+ return job;
+ }
+ }
+
+ return NULL;
+}
+
+// static
+TestAutomationProvider* TestAutomationProvider::NewAutomationProvider(
+ Profile* p, const std::string& channel,
+ TestAutomationProviderDelegate* delegate) {
+ TestAutomationProvider* automation = new TestAutomationProvider(p, delegate);
+ automation->ConnectToChannel(channel);
+ automation->SetExpectedTabCount(1);
+ return automation;
+}
diff --git a/chrome_frame/test/net/test_automation_provider.h b/chrome_frame/test/net/test_automation_provider.h
new file mode 100644
index 0000000..75830ba
--- /dev/null
+++ b/chrome_frame/test/net/test_automation_provider.h
@@ -0,0 +1,52 @@
+// Copyright (c) 2009 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.
+#ifndef CHROME_FRAME_TEST_NET_TEST_AUTOMATION_PROVIDER_H_
+#define CHROME_FRAME_TEST_NET_TEST_AUTOMATION_PROVIDER_H_
+
+#include "chrome/browser/automation/automation_provider.h"
+
+class TestAutomationResourceMessageFilter;
+
+// Callback interface for TestAutomationProvider.
+class TestAutomationProviderDelegate {
+ public:
+ virtual void OnInitialTabLoaded() = 0;
+};
+
+// A slightly customized version of AutomationProvider.
+// We override AutomationProvider to be able to filter received messages
+// (see TestAutomationResourceMessageFilter) and know when the initial
+// ExternalTab has been loaded.
+// In order to intercept UrlRequests and make the URLRequestAutomationJob class
+// handle requests from unit tests, we also implement URLRequest::Interceptor.
+class TestAutomationProvider
+ : public AutomationProvider,
+ public URLRequest::Interceptor {
+ public:
+ explicit TestAutomationProvider(Profile* profile,
+ TestAutomationProviderDelegate* delegate);
+
+ virtual ~TestAutomationProvider();
+
+ // AutomationProvider overrides.
+ virtual void OnMessageReceived(const IPC::Message& msg);
+ virtual bool Send(IPC::Message* msg);
+
+ // URLRequest::Interceptor.
+ virtual URLRequestJob* MaybeIntercept(URLRequest* request);
+
+ // Call to instantiate and initialize a new instance of
+ // TestAutomationProvider.
+ static TestAutomationProvider* NewAutomationProvider(
+ Profile* p,
+ const std::string& channel,
+ TestAutomationProviderDelegate* delegate);
+
+ protected:
+ scoped_refptr<TestAutomationResourceMessageFilter> filter_;
+ int tab_handle_;
+ TestAutomationProviderDelegate* delegate_;
+};
+
+#endif CHROME_FRAME_TEST_NET_TEST_AUTOMATION_PROVIDER_H_
diff --git a/chrome_frame/test/net/test_automation_resource_message_filter.cc b/chrome_frame/test/net/test_automation_resource_message_filter.cc
new file mode 100644
index 0000000..32ef532
--- /dev/null
+++ b/chrome_frame/test/net/test_automation_resource_message_filter.cc
@@ -0,0 +1,60 @@
+// Copyright (c) 2009 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/test_automation_resource_message_filter.h"
+
+TestAutomationResourceMessageFilter::TestAutomationResourceMessageFilter(
+ AutomationProvider* automation) : automation_(automation) {
+}
+
+bool TestAutomationResourceMessageFilter::Send(IPC::Message* message) {
+ return automation_->Send(message);
+}
+
+// static
+void TestAutomationResourceMessageFilter::OnRequestMessage(
+ URLRequestAutomationJob* job, IPC::Message* msg) {
+ job->OnMessage(*msg);
+ delete msg;
+}
+
+bool TestAutomationResourceMessageFilter::OnMessageReceived(
+ const IPC::Message& message) {
+ // See if we want to process this message... call the base class
+ // for filter messages, send the message to the correct thread
+ // for URL requests.
+ bool handled = false;
+ int request_id = URLRequestAutomationJob::MayFilterMessage(message);
+ if (request_id) {
+ RequestMap::iterator it = requests_.find(request_id);
+ if (it != requests_.end()) {
+ handled = true;
+ IPC::Message* msg = new IPC::Message(message);
+ RequestJob& job = it->second;
+ job.loop_->PostTask(FROM_HERE, NewRunnableFunction(OnRequestMessage,
+ job.job_, msg));
+ }
+ } else {
+ handled = AutomationResourceMessageFilter::OnMessageReceived(message);
+ }
+ return handled;
+}
+
+// Add request to the list of outstanding requests.
+bool TestAutomationResourceMessageFilter::RegisterRequest(
+ URLRequestAutomationJob* job) {
+ // Store the request in an internal map like the parent class
+ // does, but also store the current loop pointer so we can send
+ // request messages to that loop.
+ DCHECK(requests_.end() == requests_.find(job->id()));
+ RequestJob request_job = { MessageLoop::current(), job };
+ requests_[job->id()] = request_job;
+ return true;
+}
+
+// Remove request from the list of outstanding requests.
+void TestAutomationResourceMessageFilter::UnRegisterRequest(
+ URLRequestAutomationJob* job) {
+ requests_.erase(job->id());
+}
diff --git a/chrome_frame/test/net/test_automation_resource_message_filter.h b/chrome_frame/test/net/test_automation_resource_message_filter.h
new file mode 100644
index 0000000..310307a
--- /dev/null
+++ b/chrome_frame/test/net/test_automation_resource_message_filter.h
@@ -0,0 +1,50 @@
+// Copyright (c) 2009 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.
+#ifndef CHROME_FRAME_TEST_NET_TEST_AUTOMATION_RESOURCE_MESSAGE_FILTER_H_
+#define CHROME_FRAME_TEST_NET_TEST_AUTOMATION_RESOURCE_MESSAGE_FILTER_H_
+
+#include "chrome/browser/automation/automation_provider.h"
+#include "chrome/browser/automation/url_request_automation_job.h"
+
+// Performs the same duties as AutomationResourceMessageFilter but with one
+// difference. Instead of being tied to an IPC channel running on Chrome's
+// IO thread, this instance runs on the unit test's IO thread (all URL request
+// tests have their own IO loop) and is tied to an instance of
+// AutomationProvider (TestAutomationProvider to be exact).
+//
+// Messages from the AutomationProvider that are destined to request objects
+// owned by this class are marshaled over to the request's IO thread instead
+// of using the thread the messages are received on. This way we allow the
+// URL request tests to run sequentially as they were written while still
+// allowing the automation layer to work as it normally does (i.e. process
+// its messages on the receiving thread).
+class TestAutomationResourceMessageFilter
+ : public AutomationResourceMessageFilter {
+ public:
+ TestAutomationResourceMessageFilter(AutomationProvider* automation);
+
+ virtual bool Send(IPC::Message* message);
+
+ static void OnRequestMessage(URLRequestAutomationJob* job,
+ IPC::Message* msg);
+
+ virtual bool OnMessageReceived(const IPC::Message& message);
+
+ // Add request to the list of outstanding requests.
+ virtual bool RegisterRequest(URLRequestAutomationJob* job);
+ // Remove request from the list of outstanding requests.
+ virtual void UnRegisterRequest(URLRequestAutomationJob* job);
+
+ protected:
+ AutomationProvider* automation_;
+ // declare the request map.
+ struct RequestJob {
+ MessageLoop* loop_;
+ scoped_refptr<URLRequestAutomationJob> job_;
+ };
+ typedef std::map<int, RequestJob> RequestMap;
+ RequestMap requests_;
+};
+
+#endif // CHROME_FRAME_TEST_NET_TEST_AUTOMATION_RESOURCE_MESSAGE_FILTER_H_
diff --git a/chrome_frame/test/perf/chrome_frame_perftest.cc b/chrome_frame/test/perf/chrome_frame_perftest.cc
new file mode 100644
index 0000000..79bbd7b
--- /dev/null
+++ b/chrome_frame/test/perf/chrome_frame_perftest.cc
@@ -0,0 +1,1137 @@
+// Copyright (c) 2006-2009 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/perf/chrome_frame_perftest.h"
+
+#include <atlwin.h>
+#include <atlhost.h>
+#include <map>
+#include <vector>
+#include <string>
+
+#include "chrome_tab.h" // Generated from chrome_tab.idl.
+
+#include "base/file_util.h"
+#include "base/registry.h"
+#include "base/scoped_ptr.h"
+#include "base/scoped_bstr_win.h"
+#include "base/scoped_comptr_win.h"
+#include "base/string_util.h"
+#include "base/time.h"
+#include "chrome/common/chrome_constants.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome/test/chrome_process_util.h"
+#include "chrome/test/perf/mem_usage.h"
+#include "chrome/test/ui/ui_test.h"
+
+#include "chrome_frame/test_utils.h"
+#include "chrome_frame/utils.h"
+
+const wchar_t kSilverlightControlKey[] =
+ L"CLSID\\{DFEAF541-F3E1-4c24-ACAC-99C30715084A}\\InprocServer32";
+
+const wchar_t kFlashControlKey[] =
+ L"CLSID\\{D27CDB6E-AE6D-11cf-96B8-444553540000}\\InprocServer32";
+
+using base::TimeDelta;
+using base::TimeTicks;
+
+// Callback description for onload, onloaderror, onmessage
+static _ATL_FUNC_INFO g_single_param = {CC_STDCALL, VT_EMPTY, 1, {VT_VARIANT}};
+// Simple class that forwards the callbacks.
+template <typename T>
+class DispCallback
+ : public IDispEventSimpleImpl<1, DispCallback<T>, &IID_IDispatch> {
+ public:
+ typedef HRESULT (T::*Method)(VARIANT* param);
+
+ DispCallback(T* owner, Method method) : owner_(owner), method_(method) {
+ }
+
+ BEGIN_SINK_MAP(DispCallback)
+ SINK_ENTRY_INFO(1, IID_IDispatch, DISPID_VALUE, OnCallback, &g_single_param)
+ END_SINK_MAP()
+
+ virtual ULONG STDMETHODCALLTYPE AddRef() {
+ return owner_->AddRef();
+ }
+ virtual ULONG STDMETHODCALLTYPE Release() {
+ return owner_->Release();
+ }
+
+ STDMETHOD(OnCallback)(VARIANT param) {
+ return (owner_->*method_)(&param);
+ }
+
+ IDispatch* ToDispatch() {
+ return reinterpret_cast<IDispatch*>(this);
+ }
+
+ T* owner_;
+ Method method_;
+};
+
+// This class implements an ActiveX container which hosts the ChromeFrame
+// ActiveX control. It provides hooks which can be implemented by derived
+// classes for implementing performance measurement, etc.
+class ChromeFrameActiveXContainer
+ : public CWindowImpl<ChromeFrameActiveXContainer, CWindow, CWinTraits <
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ WS_EX_APPWINDOW | WS_EX_WINDOWEDGE> >,
+ public CComObjectRootEx<CComSingleThreadModel>,
+ public IPropertyNotifySink {
+ public:
+ ~ChromeFrameActiveXContainer() {
+ if (m_hWnd)
+ DestroyWindow();
+ }
+
+ DECLARE_WND_CLASS_EX(L"ChromeFrameActiveX_container", 0, 0)
+
+ BEGIN_COM_MAP(ChromeFrameActiveXContainer)
+ COM_INTERFACE_ENTRY(IPropertyNotifySink)
+ END_COM_MAP()
+
+ BEGIN_MSG_MAP(ChromeFrameActiveXContainer)
+ MESSAGE_HANDLER(WM_CREATE, OnCreate)
+ MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+ END_MSG_MAP()
+
+ HRESULT OnMessageCallback(VARIANT* param) {
+ DLOG(INFO) << __FUNCTION__;
+ OnMessageCallbackImpl(param);
+ return S_OK;
+ }
+
+ HRESULT OnLoadErrorCallback(VARIANT* param) {
+ DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
+ OnLoadErrorCallbackImpl(param);
+ return S_OK;
+ }
+
+ HRESULT OnLoadCallback(VARIANT* param) {
+ DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
+ OnLoadCallbackImpl(param);
+ return S_OK;
+ }
+
+ ChromeFrameActiveXContainer() :
+ prop_notify_cookie_(0),
+ onmsg_(this, &ChromeFrameActiveXContainer::OnMessageCallback),
+ onloaderror_(this, &ChromeFrameActiveXContainer::OnLoadErrorCallback),
+ onload_(this, &ChromeFrameActiveXContainer::OnLoadCallback) {
+ }
+
+ LRESULT OnCreate(UINT , WPARAM , LPARAM , BOOL& ) {
+ chromeview_.Attach(m_hWnd);
+ return 0;
+ }
+
+ // This will be called twice.
+ // Once from CAxHostWindow::OnDestroy (through DefWindowProc)
+ // and once more from the ATL since CAxHostWindow::OnDestroy claims the
+ // message is not handled.
+ LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL& handled) { // NOLINT
+ if (prop_notify_cookie_) {
+ AtlUnadvise(tab_, IID_IPropertyNotifySink, prop_notify_cookie_);
+ prop_notify_cookie_ = 0;
+ }
+
+ tab_.Release();
+ return 0;
+ }
+
+ virtual void OnFinalMessage(HWND /*hWnd*/) {
+ ::PostQuitMessage(6);
+ }
+
+ static const wchar_t* GetWndCaption() {
+ return L"ChromeFrame Container";
+ }
+
+ // IPropertyNotifySink
+ STDMETHOD(OnRequestEdit)(DISPID disp_id) {
+ OnRequestEditImpl(disp_id);
+ return S_OK;
+ }
+
+ STDMETHOD(OnChanged)(DISPID disp_id) {
+ if (disp_id != DISPID_READYSTATE)
+ return S_OK;
+
+ long ready_state;
+ HRESULT hr = tab_->get_readyState(&ready_state);
+ DCHECK(hr == S_OK);
+
+ OnReadyStateChanged(ready_state);
+
+ if (ready_state == READYSTATE_COMPLETE) {
+ if(!starting_url_.empty()) {
+ Navigate(starting_url_.c_str());
+ } else {
+ PostMessage(WM_CLOSE);
+ }
+ } else if (ready_state == READYSTATE_UNINITIALIZED) {
+ DLOG(ERROR) << __FUNCTION__ << " Chrome launch failed.";
+ }
+
+ return S_OK;
+ }
+
+ void CreateChromeFrameWindow(const std::string& starting_url) {
+ starting_url_ = starting_url;
+ RECT rc = { 0, 0, 800, 600 };
+ Create(NULL, rc);
+ DCHECK(m_hWnd);
+ ShowWindow(SW_SHOWDEFAULT);
+ }
+
+ void CreateControl(bool setup_event_sinks) {
+ HRESULT hr = chromeview_.CreateControl(L"ChromeTab.ChromeFrame");
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ hr = chromeview_.QueryControl(tab_.Receive());
+ EXPECT_HRESULT_SUCCEEDED(hr);
+
+ if (setup_event_sinks)
+ SetupEventSinks();
+ }
+
+ void Navigate(const char* url) {
+ BeforeNavigateImpl(url);
+
+ HRESULT hr = tab_->put_src(ScopedBstr(UTF8ToWide(url).c_str()));
+ DCHECK(hr == S_OK) << "Chrome frame NavigateToURL(" << url
+ << StringPrintf(L") failed 0x%08X", hr);
+ }
+
+ void SetupEventSinks() {
+ HRESULT hr = AtlAdvise(tab_, this, IID_IPropertyNotifySink,
+ &prop_notify_cookie_);
+ DCHECK(hr == S_OK) << "AtlAdvice for IPropertyNotifySink failed " << hr;
+
+ CComVariant onmessage(onmsg_.ToDispatch());
+ CComVariant onloaderror(onloaderror_.ToDispatch());
+ CComVariant onload(onload_.ToDispatch());
+ EXPECT_HRESULT_SUCCEEDED(tab_->put_onmessage(onmessage));
+ EXPECT_HRESULT_SUCCEEDED(tab_->put_onloaderror(onloaderror));
+ EXPECT_HRESULT_SUCCEEDED(tab_->put_onload(onload));
+ }
+
+ protected:
+ // These functions are implemented by derived classes for special behavior
+ // like performance measurement, etc.
+ virtual void OnReadyStateChanged(long ready_state) {}
+ virtual void OnRequestEditImpl(DISPID disp_id) {}
+
+ virtual void OnMessageCallbackImpl(VARIANT* param) {}
+
+ virtual void OnLoadCallbackImpl(VARIANT* param) {
+ PostMessage(WM_CLOSE);
+ }
+
+ virtual void OnLoadErrorCallbackImpl(VARIANT* param) {
+ PostMessage(WM_CLOSE);
+ }
+ virtual void BeforeNavigateImpl(const char* url) {}
+
+ CAxWindow chromeview_;
+ ScopedComPtr<IChromeFrame> tab_;
+ DWORD prop_notify_cookie_;
+ DispCallback<ChromeFrameActiveXContainer> onmsg_;
+ DispCallback<ChromeFrameActiveXContainer> onloaderror_;
+ DispCallback<ChromeFrameActiveXContainer> onload_;
+ std::string starting_url_;
+};
+
+// This class overrides the hooks provided by the ChromeFrameActiveXContainer
+// class and measures performance at various stages, like initialzation of
+// the Chrome frame widget, navigation, etc.
+class ChromeFrameActiveXContainerPerf : public ChromeFrameActiveXContainer {
+ public:
+ ChromeFrameActiveXContainerPerf() {}
+
+ void CreateControl(bool setup_event_sinks) {
+ perf_initialize_.reset(new PerfTimeLogger("Fully initialized"));
+ PerfTimeLogger perf_create("Create Control");
+
+ HRESULT hr = chromeview_.CreateControl(L"ChromeTab.ChromeFrame");
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ hr = chromeview_.QueryControl(tab_.Receive());
+ EXPECT_HRESULT_SUCCEEDED(hr);
+
+ perf_create.Done();
+ if (setup_event_sinks)
+ SetupEventSinks();
+ }
+
+ protected:
+ virtual void OnReadyStateChanged(long ready_state) {
+ // READYSTATE_COMPLETE is fired when the automation server is ready.
+ if (ready_state == READYSTATE_COMPLETE) {
+ perf_initialize_->Done();
+ } else if (ready_state == READYSTATE_INTERACTIVE) {
+ // Window ready. Currently we never receive this notification because it
+ // is fired before we finish setting up our hosting environment.
+ // This is because of how ATL is written. Moving forward we might
+ // have our own hosting classes and then have more control over when we
+ // set up the prop notify sink.
+ } else {
+ DCHECK(ready_state != READYSTATE_UNINITIALIZED) << "failed to initialize";
+ }
+ }
+
+ virtual void OnLoadCallbackImpl(VARIANT* param) {
+ PostMessage(WM_CLOSE);
+ perf_navigate_->Done();
+ }
+
+ virtual void OnLoadErrorCallbackImpl(VARIANT* param) {
+ PostMessage(WM_CLOSE);
+ perf_navigate_->Done();
+ }
+
+ virtual void BeforeNavigateImpl(const char* url ) {
+ std::string test_name = "Navigate ";
+ test_name += url;
+ perf_navigate_.reset(new PerfTimeLogger(test_name.c_str()));
+ }
+
+ scoped_ptr<PerfTimeLogger> perf_initialize_;
+ scoped_ptr<PerfTimeLogger> perf_navigate_;
+};
+
+// This class provides common functionality which can be used for most of the
+// ChromeFrame/Tab performance tests.
+class ChromeFramePerfTestBase : public UITest {
+ public:
+ ChromeFramePerfTestBase() {}
+ protected:
+ scoped_ptr<ScopedChromeFrameRegistrar> chrome_frame_registrar_;
+};
+
+class ChromeFrameStartupTest : public ChromeFramePerfTestBase {
+ public:
+ ChromeFrameStartupTest() {}
+
+ virtual void SetUp() {
+ ASSERT_TRUE(PathService::Get(chrome::DIR_APP, &dir_app_));
+
+ chrome_dll_ = dir_app_.Append(FILE_PATH_LITERAL("chrome.dll"));
+ chrome_exe_ = dir_app_.Append(
+ FilePath::FromWStringHack(chrome::kBrowserProcessExecutableName));
+ chrome_frame_dll_ = dir_app_.Append(
+ FILE_PATH_LITERAL("servers\\npchrome_tab.dll"));
+ }
+ virtual void TearDown() {}
+
+ // TODO(iyengar)
+ // This function is similar to the RunStartupTest function used in chrome
+ // startup tests. Refactor into a common implementation.
+ void RunStartupTest(const char* graph, const char* trace,
+ const char* startup_url, bool test_cold,
+ int total_binaries, const FilePath binaries_to_evict[],
+ bool important, bool ignore_cache_error) {
+ const int kNumCycles = 20;
+
+ startup_url_ = startup_url;
+
+ TimeDelta timings[kNumCycles];
+
+ for (int i = 0; i < kNumCycles; ++i) {
+ if (test_cold) {
+ for (int binary_index = 0; binary_index < total_binaries;
+ binary_index++) {
+ bool result = EvictFileFromSystemCacheWrapper(
+ binaries_to_evict[binary_index]);
+ if (!ignore_cache_error) {
+ ASSERT_TRUE(result);
+ } else if (!result) {
+ printf("\nFailed to evict file %ls from cache. Not running test\n",
+ binaries_to_evict[binary_index].value().c_str());
+ return;
+ }
+ }
+ }
+
+ TimeTicks start_time, end_time;
+
+ RunStartupTestImpl(&start_time, &end_time);
+
+ timings[i] = end_time - start_time;
+
+ CoFreeUnusedLibraries();
+ ASSERT_TRUE(GetModuleHandle(L"npchrome_tab.dll") == NULL);
+
+ // TODO(beng): Can't shut down so quickly. Figure out why, and fix. If we
+ // do, we crash.
+ PlatformThread::Sleep(50);
+ }
+
+ std::string times;
+ for (int i = 0; i < kNumCycles; ++i)
+ StringAppendF(&times, "%.2f,", timings[i].InMillisecondsF());
+
+ PrintResultList(graph, "", trace, times, "ms", important);
+ }
+
+ FilePath dir_app_;
+ FilePath chrome_dll_;
+ FilePath chrome_exe_;
+ FilePath chrome_frame_dll_;
+
+ protected:
+ // Individual startup tests should implement this function.
+ virtual void RunStartupTestImpl(TimeTicks* start_time,
+ TimeTicks* end_time) {}
+
+ // The host is torn down by this function. It should not be used after
+ // this function returns.
+ static void ReleaseHostComReferences(CAxWindow& host) {
+ CComPtr<IAxWinHostWindow> spWinHost;
+ host.QueryHost(&spWinHost);
+ ASSERT_TRUE(spWinHost != NULL);
+
+ // Hack to get the host to release all interfaces and thus ensure that
+ // the COM server can be unloaded.
+ CAxHostWindow* host_window = static_cast<CAxHostWindow*>(spWinHost.p);
+ host_window->ReleaseAll();
+ host.DestroyWindow();
+ }
+
+ std::string startup_url_;
+};
+
+class ChromeFrameStartupTestActiveX : public ChromeFrameStartupTest {
+ public:
+ virtual void SetUp() {
+ // Register the Chrome Frame DLL in the build directory.
+ chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
+
+ ChromeFrameStartupTest::SetUp();
+ }
+
+ protected:
+ virtual void RunStartupTestImpl(TimeTicks* start_time,
+ TimeTicks* end_time) {
+ *start_time = TimeTicks::Now();
+ SimpleModule module;
+ AtlAxWinInit();
+ CComObjectStackEx<ChromeFrameActiveXContainer> wnd;
+ wnd.CreateChromeFrameWindow(startup_url_);
+ wnd.CreateControl(true);
+ module.RunMessageLoop();
+ *end_time = TimeTicks::Now();
+ }
+};
+
+// This class measures the load time of chrome and chrome frame binaries
+class ChromeFrameBinariesLoadTest : public ChromeFrameStartupTestActiveX {
+ protected:
+ virtual void RunStartupTestImpl(TimeTicks* start_time,
+ TimeTicks* end_time) {
+ *start_time = TimeTicks::Now();
+
+ HMODULE chrome_exe = LoadLibrary(chrome_exe_.ToWStringHack().c_str());
+ ASSERT_TRUE(chrome_exe != NULL);
+
+ HMODULE chrome_dll = LoadLibrary(chrome_dll_.ToWStringHack().c_str());
+ ASSERT_TRUE(chrome_dll != NULL);
+
+ HMODULE chrome_tab_dll =
+ LoadLibrary(chrome_frame_dll_.ToWStringHack().c_str());
+ ASSERT_TRUE(chrome_tab_dll != NULL);
+
+ *end_time = TimeTicks::Now();
+
+ FreeLibrary(chrome_exe);
+ FreeLibrary(chrome_dll);
+ FreeLibrary(chrome_tab_dll);
+ }
+};
+
+// This class provides functionality to run the startup performance test for
+// the ChromeFrame ActiveX against a reference build. At this point we only run
+// this test in warm mode.
+class ChromeFrameStartupTestActiveXReference
+ : public ChromeFrameStartupTestActiveX {
+ public:
+ // override the browser directory to use the reference build instead.
+ virtual void SetUp() {
+ // Register the reference build Chrome Frame DLL.
+ chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
+ chrome_frame_registrar_->RegisterReferenceChromeFrameBuild();
+
+ ChromeFrameStartupTest::SetUp();
+ chrome_frame_dll_ = FilePath::FromWStringHack(
+ chrome_frame_registrar_->GetChromeFrameDllPath());
+ }
+
+ virtual void TearDown() {
+ // Reregister the Chrome Frame DLL in the build directory.
+ chrome_frame_registrar_.reset(NULL);
+ }
+};
+
+// This class provides base functionality to measure ChromeFrame memory
+// usage.
+// TODO(iyengar)
+// Some of the functionality in this class like printing the results, etc
+// is based on the chrome\test\memory_test.cc. We need to factor out
+// the common code.
+class ChromeFrameMemoryTest : public ChromeFramePerfTestBase {
+
+ // Contains information about the memory consumption of a process.
+ class ProcessMemoryInfo {
+ public:
+ // Default constructor
+ // Added to enable us to add ProcessMemoryInfo instances to a map.
+ ProcessMemoryInfo()
+ : process_id_(0),
+ peak_virtual_size_(0),
+ virtual_size_(0),
+ peak_working_set_size_(0),
+ working_set_size_(0),
+ chrome_browser_process_(false),
+ chrome_frame_memory_test_instance_(NULL) {}
+
+ ProcessMemoryInfo(base::ProcessId process_id, bool chrome_browser_process,
+ ChromeFrameMemoryTest* memory_test_instance)
+ : process_id_(process_id),
+ peak_virtual_size_(0),
+ virtual_size_(0),
+ peak_working_set_size_(0),
+ working_set_size_(0),
+ chrome_browser_process_(chrome_browser_process),
+ chrome_frame_memory_test_instance_(memory_test_instance) {}
+
+ bool GetMemoryConsumptionDetails() {
+ return GetMemoryInfo(process_id_,
+ &peak_virtual_size_,
+ &virtual_size_,
+ &peak_working_set_size_,
+ &working_set_size_);
+ }
+
+ void Print(const char* test_name) {
+ std::string trace_name(test_name);
+
+ ASSERT_TRUE(chrome_frame_memory_test_instance_ != NULL);
+
+ if (chrome_browser_process_) {
+ chrome_frame_memory_test_instance_->PrintResult(
+ "vm_final_browser", "", trace_name + "_vm_b",
+ virtual_size_ / 1024, "KB", false /* not important */);
+ chrome_frame_memory_test_instance_->PrintResult(
+ "ws_final_browser", "", trace_name + "_ws_b",
+ working_set_size_ / 1024, "KB", false /* not important */);
+ } else if (process_id_ == GetCurrentProcessId()) {
+ chrome_frame_memory_test_instance_->PrintResult(
+ "vm_current_process", "", trace_name + "_vm_c",
+ virtual_size_ / 1024, "KB", false /* not important */);
+ chrome_frame_memory_test_instance_->PrintResult(
+ "ws_current_process", "", trace_name + "_ws_c",
+ working_set_size_ / 1024, "KB", false /* not important */);
+ }
+
+ printf("\n");
+ }
+
+ int process_id_;
+ size_t peak_virtual_size_;
+ size_t virtual_size_;
+ size_t peak_working_set_size_;
+ size_t working_set_size_;
+ // Set to true if this is the chrome browser process.
+ bool chrome_browser_process_;
+
+ // A reference to the ChromeFrameMemoryTest instance. Used to print memory
+ // consumption information.
+ ChromeFrameMemoryTest* chrome_frame_memory_test_instance_;
+ };
+
+ // This map tracks memory usage for a process. It is keyed on the process
+ // id.
+ typedef std::map<DWORD, ProcessMemoryInfo> ProcessMemoryConsumptionMap;
+
+ public:
+ ChromeFrameMemoryTest()
+ : current_url_index_(0),
+ browser_pid_(0) {}
+
+ virtual void SetUp() {
+ // Register the Chrome Frame DLL in the build directory.
+ chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
+ }
+
+ void RunTest(const char* test_name, char* urls[], int total_urls) {
+ ASSERT_TRUE(urls != NULL);
+ ASSERT_GT(total_urls, 0);
+
+ // Record the initial CommitCharge. This is a system-wide measurement,
+ // so if other applications are running, they can create variance in this
+ // test.
+ start_commit_charge_ = GetSystemCommitCharge();
+
+ for (int i = 0; i < total_urls; i++)
+ urls_.push_back(urls[i]);
+
+ std::string url;
+ GetNextUrl(&url);
+ ASSERT_TRUE(!url.empty());
+
+ StartTest(url, test_name);
+ }
+
+ void OnNavigationSuccess(VARIANT* param) {
+ ASSERT_TRUE(param != NULL);
+ ASSERT_EQ(VT_BSTR, param->vt);
+
+ DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
+ InitiateNextNavigation();
+ }
+
+ void OnNavigationFailure(VARIANT* param) {
+ ASSERT_TRUE(param != NULL);
+ ASSERT_EQ(VT_BSTR, param->vt);
+
+ DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
+ InitiateNextNavigation();
+ }
+
+ protected:
+ bool GetNextUrl(std::string* url) {
+ if (current_url_index_ >= urls_.size())
+ return false;
+
+ *url = urls_[current_url_index_++];
+ return true;
+ }
+
+ // Returns the path of the current chrome.exe being used by this test.
+ // This could return the regular chrome path or that of the reference
+ // build.
+ std::wstring GetChromeExePath() {
+ std::wstring chrome_exe_path =
+ chrome_frame_registrar_->GetChromeFrameDllPath();
+ EXPECT_FALSE(chrome_exe_path.empty());
+
+ file_util::UpOneDirectory(&chrome_exe_path);
+
+ std::wstring chrome_exe_test_path = chrome_exe_path;
+ file_util::AppendToPath(&chrome_exe_test_path,
+ chrome::kBrowserProcessExecutableName);
+
+ if (!file_util::PathExists(chrome_exe_test_path)) {
+ file_util::UpOneDirectory(&chrome_exe_path);
+
+ chrome_exe_test_path = chrome_exe_path;
+ file_util::AppendToPath(&chrome_exe_test_path,
+ chrome::kBrowserProcessExecutableName);
+ }
+
+ EXPECT_TRUE(file_util::PathExists(chrome_exe_test_path));
+
+ return chrome_exe_path;
+ }
+
+ void InitiateNextNavigation() {
+ if (browser_pid_ == 0) {
+ std::wstring profile_directory;
+ if (GetUserProfileBaseDirectory(&profile_directory)) {
+ file_util::AppendToPath(&profile_directory,
+ GetHostProcessName(false));
+ }
+
+ user_data_dir_ = FilePath::FromWStringHack(profile_directory);
+ browser_pid_ = ChromeBrowserProcessId(user_data_dir_);
+ }
+
+ EXPECT_TRUE(static_cast<int>(browser_pid_) > 0);
+
+ // Get the memory consumption information for the child processes
+ // of the chrome browser.
+ ChromeProcessList child_processes = GetBrowserChildren();
+ ChromeProcessList::iterator index;
+ for (index = child_processes.begin(); index != child_processes.end();
+ ++index) {
+ AccountProcessMemoryUsage(*index);
+ }
+
+ // TODO(iyengar): Bug 2953
+ // Need to verify if this is still true.
+ // The automation crashes periodically if we cycle too quickly.
+ // To make these tests more reliable, slowing them down a bit.
+ Sleep(200);
+
+ std::string url;
+ bool next_url = GetNextUrl(&url);
+ if (!url.empty()) {
+ NavigateImpl(url);
+ } else {
+ TestCompleted();
+ }
+ }
+
+ void PrintResults(const char* test_name) {
+ PrintMemoryUsageInfo(test_name);
+ memory_consumption_map_.clear();
+
+ // Added to give the OS some time to flush the used pages for the
+ // chrome processes which would have exited by now.
+ Sleep(200);
+
+ size_t end_commit_charge = GetSystemCommitCharge();
+ size_t commit_size = end_commit_charge - start_commit_charge_;
+
+ std::string trace_name(test_name);
+ trace_name.append("_cc");
+
+ PrintResult("commit_charge", "", trace_name,
+ commit_size / 1024, "KB", true /* important */);
+ printf("\n");
+ }
+
+ ChromeProcessList GetBrowserChildren() {
+ ChromeProcessList list = GetRunningChromeProcesses(user_data_dir_);
+ ChromeProcessList::iterator browser =
+ std::find(list.begin(), list.end(), browser_pid_);
+ if (browser != list.end()) {
+ list.erase(browser);
+ }
+ return list;
+ }
+
+ void AccountProcessMemoryUsage(DWORD process_id) {
+ ProcessMemoryInfo process_memory_info(process_id,
+ process_id == browser_pid_, this);
+
+ ASSERT_TRUE(process_memory_info.GetMemoryConsumptionDetails());
+
+ memory_consumption_map_[process_id] = process_memory_info;
+ }
+
+ void PrintMemoryUsageInfo(const char* test_name) {
+ printf("\n");
+
+ std::string trace_name(test_name);
+
+ ProcessMemoryConsumptionMap::iterator index;
+ size_t total_virtual_size = 0;
+ size_t total_working_set_size = 0;
+
+ for (index = memory_consumption_map_.begin();
+ index != memory_consumption_map_.end();
+ ++index) {
+ ProcessMemoryInfo& memory_info = (*index).second;
+ memory_info.Print(test_name);
+
+ total_virtual_size += memory_info.virtual_size_;
+ total_working_set_size += memory_info.working_set_size_;
+ }
+
+ printf("\n");
+
+ PrintResult("vm_final_total", "", trace_name + "_vm",
+ total_virtual_size / 1024, "KB",
+ false /* not important */);
+ PrintResult("ws_final_total", "", trace_name + "_ws",
+ total_working_set_size / 1024, "KB",
+ true /* important */);
+ }
+
+ // Should never get called.
+ virtual void StartTest(const std::string& url,
+ const std::string& test_name) = 0 {
+ ASSERT_FALSE(false);
+ }
+
+ // Should never get called.
+ virtual void NavigateImpl(const std::string& url) = 0 {
+ ASSERT_FALSE(false);
+ }
+
+ virtual void TestCompleted() = 0 {
+ ASSERT_FALSE(false);
+ }
+
+ // Holds the commit charge at the start of the memory test run.
+ size_t start_commit_charge_;
+
+ // The index of the URL being tested.
+ size_t current_url_index_;
+
+ // The chrome browser pid.
+ base::ProcessId browser_pid_;
+
+ // Contains the list of urls against which the tests are run.
+ std::vector<std::string> urls_;
+
+ ProcessMemoryConsumptionMap memory_consumption_map_;
+};
+
+// This class provides functionality to run the memory test against a reference
+// chrome frame build.
+class ChromeFrameMemoryTestReference : public ChromeFrameMemoryTest {
+ public:
+ virtual void SetUp() {
+ chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
+ chrome_frame_registrar_->RegisterReferenceChromeFrameBuild();
+ }
+
+ virtual void TearDown() {
+ // Reregisters the chrome frame DLL in the build directory.
+ chrome_frame_registrar_.reset(NULL);
+ }
+};
+
+// This class overrides the hooks provided by the ChromeFrameActiveXContainer
+// class and calls back into the ChromeFrameMemoryTest object instance,
+// which measures ChromeFrame memory usage.
+class ChromeFrameActiveXContainerMemory : public ChromeFrameActiveXContainer {
+ public:
+ ChromeFrameActiveXContainerMemory()
+ : delegate_(NULL) {}
+
+ ~ChromeFrameActiveXContainerMemory() {}
+
+ void Initialize(ChromeFrameMemoryTest* delegate) {
+ ASSERT_TRUE(delegate != NULL);
+ delegate_ = delegate;
+ }
+
+ protected:
+ virtual void OnLoadCallbackImpl(VARIANT* param) {
+ delegate_->OnNavigationSuccess(param);
+ }
+
+ virtual void OnLoadErrorCallbackImpl(VARIANT* param) {
+ delegate_->OnNavigationFailure(param);
+ }
+
+ ChromeFrameMemoryTest* delegate_;
+};
+
+// This class runs memory tests against the ChromeFrame ActiveX.
+template<class MemoryTestBase>
+class ChromeFrameActiveXMemoryTest : public MemoryTestBase {
+ public:
+ ChromeFrameActiveXMemoryTest()
+ : chrome_frame_container_(NULL),
+ test_completed_(false) {}
+
+ ~ChromeFrameActiveXMemoryTest() {
+ }
+
+ void StartTest(const std::string& url, const std::string& test_name) {
+ ASSERT_TRUE(chrome_frame_container_ == NULL);
+
+ test_name_ = test_name;
+
+ SimpleModule module;
+ AtlAxWinInit();
+
+ CComObject<ChromeFrameActiveXContainerMemory>::CreateInstance(
+ &chrome_frame_container_);
+ chrome_frame_container_->AddRef();
+
+ chrome_frame_container_->Initialize(this);
+
+ chrome_frame_container_->CreateChromeFrameWindow(url.c_str());
+ chrome_frame_container_->CreateControl(true);
+
+ module.RunMessageLoop();
+
+ chrome_frame_container_->Release();
+
+ PrintResults(test_name_.c_str());
+
+ CoFreeUnusedLibraries();
+ //ASSERT_TRUE(GetModuleHandle(L"npchrome_tab.dll") == NULL);
+ }
+
+ void NavigateImpl(const std::string& url) {
+ ASSERT_TRUE(chrome_frame_container_ != NULL);
+ ASSERT_TRUE(!url.empty());
+ chrome_frame_container_->Navigate(url.c_str());
+ }
+
+ void TestCompleted() {
+ // This can get called multiple times if the last url results in a
+ // redirect.
+ if (!test_completed_) {
+ ASSERT_NE(browser_pid_, 0);
+
+ // Measure memory usage for the browser process.
+ AccountProcessMemoryUsage(browser_pid_);
+ // Measure memory usage for the current process.
+ AccountProcessMemoryUsage(GetCurrentProcessId());
+
+ test_completed_ = true;
+ EXPECT_TRUE(PostMessage(static_cast<HWND>(*chrome_frame_container_),
+ WM_CLOSE, 0, 0));
+ }
+ }
+
+ protected:
+ CComObject<ChromeFrameActiveXContainerMemory>* chrome_frame_container_;
+ std::string test_name_;
+ bool test_completed_;
+};
+
+// This class runs tests to measure chrome frame creation only. This will help
+// track overall page load performance with chrome frame instances.
+class ChromeFrameCreationTest : public ChromeFrameStartupTest {
+ protected:
+ virtual void RunStartupTestImpl(TimeTicks* start_time,
+ TimeTicks* end_time) {
+ SimpleModule module;
+ AtlAxWinInit();
+ CComObjectStackEx<ChromeFrameActiveXContainer> wnd;
+ wnd.CreateChromeFrameWindow(startup_url_);
+ *start_time = TimeTicks::Now();
+ wnd.CreateControl(false);
+ *end_time = TimeTicks::Now();
+ }
+};
+
+// This class provides functionality to run the chrome frame
+// performance test against a reference build.
+class ChromeFrameCreationTestReference : public ChromeFrameCreationTest {
+ public:
+ // override the browser directory to use the reference build instead.
+ virtual void SetUp() {
+ chrome_frame_registrar_.reset(new ScopedChromeFrameRegistrar);
+ chrome_frame_registrar_->RegisterReferenceChromeFrameBuild();
+ ChromeFrameStartupTest::SetUp();
+ }
+
+ virtual void TearDown() {
+ chrome_frame_registrar_.reset(NULL);
+ }
+};
+
+// This class measures the creation time for Flash, which would be used
+// as a baseline to measure chrome frame creation performance.
+class FlashCreationTest : public ChromeFrameStartupTest {
+ protected:
+ virtual void RunStartupTestImpl(TimeTicks* start_time,
+ TimeTicks* end_time) {
+ SimpleModule module;
+ AtlAxWinInit();
+ CAxWindow host;
+ RECT rc = {0, 0, 800, 600};
+ host.Create(NULL, rc, NULL,
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
+ EXPECT_TRUE(host.m_hWnd != NULL);
+
+ *start_time = TimeTicks::Now();
+ HRESULT hr = host.CreateControl(L"ShockwaveFlash.ShockwaveFlash");
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ *end_time = TimeTicks::Now();
+
+ ReleaseHostComReferences(host);
+ }
+};
+
+// This class measures the creation time for Silverlight, which would be used
+// as a baseline to measure chrome frame creation performance.
+class SilverlightCreationTest : public ChromeFrameStartupTest {
+ protected:
+ virtual void RunStartupTestImpl(TimeTicks* start_time,
+ TimeTicks* end_time) {
+ SimpleModule module;
+ AtlAxWinInit();
+ CAxWindow host;
+ RECT rc = {0, 0, 800, 600};
+ host.Create(NULL, rc, NULL,
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
+ EXPECT_TRUE(host.m_hWnd != NULL);
+
+ *start_time = TimeTicks::Now();
+ HRESULT hr = host.CreateControl(L"AgControl.AgControl");
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ *end_time = TimeTicks::Now();
+
+ ReleaseHostComReferences(host);
+ }
+};
+
+TEST(ChromeFramePerf, DISABLED_HostActiveX) {
+ // TODO(stoyan): Create a low integrity level thread && perform the test there
+ SimpleModule module;
+ AtlAxWinInit();
+ CComObjectStackEx<ChromeFrameActiveXContainerPerf> wnd;
+ wnd.CreateChromeFrameWindow("http://www.google.com");
+ wnd.CreateControl(true);
+ module.RunMessageLoop();
+}
+
+TEST(ChromeFramePerf, DISABLED_HostActiveXInvalidURL) {
+ // TODO(stoyan): Create a low integrity level thread && perform the test there
+ SimpleModule module;
+ AtlAxWinInit();
+ CComObjectStackEx<ChromeFrameActiveXContainerPerf> wnd;
+ wnd.CreateChromeFrameWindow("http://non-existent-domain.org/");
+ wnd.CreateControl(true);
+ module.RunMessageLoop();
+}
+
+TEST_F(ChromeFrameStartupTestActiveX, PerfWarm) {
+ RunStartupTest("warm", "t", "about:blank", false /* cold */, 0, NULL,
+ true /* important */, false);
+}
+
+TEST_F(ChromeFrameBinariesLoadTest, PerfWarm) {
+ RunStartupTest("binary_load_warm", "t", "", false /* cold */, 0, NULL,
+ true /* important */, false);
+}
+
+TEST_F(ChromeFrameStartupTestActiveX, PerfCold) {
+ FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_, chrome_frame_dll_};
+ RunStartupTest("cold", "t", "about:blank", true /* cold */,
+ arraysize(binaries_to_evict), binaries_to_evict,
+ false /* not important */, false);
+}
+
+TEST_F(ChromeFrameBinariesLoadTest, PerfCold) {
+ FilePath binaries_to_evict[] = {chrome_exe_, chrome_dll_, chrome_frame_dll_};
+ RunStartupTest("binary_load_cold", "t", "", true /* cold */,
+ arraysize(binaries_to_evict), binaries_to_evict,
+ false /* not important */, false);
+}
+
+TEST_F(ChromeFrameStartupTestActiveXReference, PerfWarm) {
+ RunStartupTest("warm", "t_ref", "about:blank", false /* cold */, 0, NULL,
+ true /* important */, false);
+}
+
+TEST_F(ChromeFrameStartupTestActiveX, PerfChromeFrameInitializationWarm) {
+ RunStartupTest("ChromeFrame_init_warm", "t", "", false /* cold */, 0,
+ NULL, true /* important */, false);
+}
+
+TEST_F(ChromeFrameStartupTestActiveX, PerfChromeFrameInitializationCold) {
+ FilePath binaries_to_evict[] = {chrome_frame_dll_};
+ RunStartupTest("ChromeFrame_init_cold", "t", "", true /* cold */,
+ arraysize(binaries_to_evict), binaries_to_evict,
+ false /* not important */, false);
+}
+
+TEST_F(ChromeFrameStartupTestActiveXReference,
+ PerfChromeFrameInitializationWarm) {
+ RunStartupTest("ChromeFrame_init_warm", "t_ref", "", false /* cold */, 0,
+ NULL, true /* important */, false);
+}
+
+typedef ChromeFrameActiveXMemoryTest<ChromeFrameMemoryTest>
+ RegularChromeFrameActiveXMemoryTest;
+
+TEST_F(RegularChromeFrameActiveXMemoryTest, MemoryTestAboutBlank) {
+ char *urls[] = {"about:blank"};
+ RunTest("memory_about_blank", urls, arraysize(urls));
+}
+
+// TODO(iyengar)
+// Revisit why the chrome frame dll does not unload correctly when this test is
+// run.
+TEST_F(RegularChromeFrameActiveXMemoryTest, DISABLED_MemoryTestUrls) {
+ // TODO(iyengar)
+ // We should use static pages to measure memory usage.
+ char *urls[] = {
+ "http://www.youtube.com/watch?v=PN2HAroA12w",
+ "http://www.youtube.com/watch?v=KmLJDrsaJmk&feature=channel"
+ };
+
+ RunTest("memory", urls, arraysize(urls));
+}
+
+typedef ChromeFrameActiveXMemoryTest<ChromeFrameMemoryTestReference>
+ ReferenceBuildChromeFrameActiveXMemoryTest;
+
+TEST_F(ReferenceBuildChromeFrameActiveXMemoryTest, MemoryTestAboutBlank) {
+ char *urls[] = {"about:blank"};
+ RunTest("memory_about_blank_reference", urls, arraysize(urls));
+}
+
+// TODO(iyengar)
+// Revisit why the chrome frame dll does not unload correctly when this test is
+// run.
+TEST_F(ReferenceBuildChromeFrameActiveXMemoryTest, DISABLED_MemoryTestUrls) {
+ // TODO(iyengar)
+ // We should use static pages to measure memory usage.
+ char *urls[] = {
+ "http://www.youtube.com/watch?v=PN2HAroA12w",
+ "http://www.youtube.com/watch?v=KmLJDrsaJmk&feature=channel"
+ };
+
+ RunTest("memory_reference", urls, arraysize(urls));
+}
+
+TEST_F(ChromeFrameCreationTest, PerfWarm) {
+ RunStartupTest("creation_warm", "t", "", false /* cold */, 0,
+ NULL, true /* important */, false);
+}
+
+TEST_F(ChromeFrameCreationTestReference, PerfWarm) {
+ RunStartupTest("creation_warm", "t_ref", "about:blank", false /* cold */, 0,
+ NULL, true /* not important */, false);
+}
+
+TEST_F(FlashCreationTest, PerfWarm) {
+ RunStartupTest("creation_warm", "t_flash", "", false /* cold */, 0, NULL,
+ true /* not important */, false);
+}
+
+TEST_F(SilverlightCreationTest, DISABLED_PerfWarm) {
+ RunStartupTest("creation_warm", "t_silverlight", "", false /* cold */, 0,
+ NULL, false /* not important */, false);
+}
+
+TEST_F(ChromeFrameCreationTest, PerfCold) {
+ FilePath binaries_to_evict[] = {chrome_frame_dll_};
+
+ RunStartupTest("creation_cold", "t", "", true /* cold */,
+ arraysize(binaries_to_evict), binaries_to_evict,
+ true /* important */, false);
+}
+
+// Attempt to evict the Flash control can fail on the buildbot as the dll
+// is marked read only. The test run is aborted if we fail to evict the file
+// from the cache. This could also fail if the Flash control is in use.
+// On Vista this could fail because of UAC
+TEST_F(FlashCreationTest, PerfCold) {
+ RegKey flash_key(HKEY_CLASSES_ROOT, kFlashControlKey);
+
+ std::wstring plugin_path;
+ ASSERT_TRUE(flash_key.ReadValue(L"", &plugin_path));
+ ASSERT_FALSE(plugin_path.empty());
+
+ FilePath flash_path = FilePath::FromWStringHack(plugin_path);
+ FilePath binaries_to_evict[] = {flash_path};
+
+ RunStartupTest("creation_cold", "t_flash", "", true /* cold */,
+ arraysize(binaries_to_evict), binaries_to_evict,
+ false/* important */, true);
+}
+
+// This test would fail on Vista due to UAC or if the Silverlight control is
+// in use. The test run is aborted if we fail to evict the file from the cache.
+// Disabling this test as the Silverlight dll does not seem to get unloaded
+// correctly causing the attempt to evict the dll from the system cache to
+// fail.
+TEST_F(SilverlightCreationTest, DISABLED_PerfCold) {
+ RegKey silverlight_key(HKEY_CLASSES_ROOT, kSilverlightControlKey);
+
+ std::wstring plugin_path;
+ ASSERT_TRUE(silverlight_key.ReadValue(L"", &plugin_path));
+ ASSERT_FALSE(plugin_path.empty());
+
+ FilePath silverlight_path = FilePath::FromWStringHack(plugin_path);
+ FilePath binaries_to_evict[] = {silverlight_path};
+
+ RunStartupTest("creation_cold", "t_silverlight", "", true /* cold */,
+ arraysize(binaries_to_evict), binaries_to_evict,
+ false /* important */, true);
+}
diff --git a/chrome_frame/test/perf/chrome_frame_perftest.h b/chrome_frame/test/perf/chrome_frame_perftest.h
new file mode 100644
index 0000000..5b895f3
--- /dev/null
+++ b/chrome_frame/test/perf/chrome_frame_perftest.h
@@ -0,0 +1,21 @@
+// Copyright (c) 2006-2009 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.
+#ifndef CHROME_FRAME_TEST_PERF_CHROME_FRAME_PERFTEST_H_
+#define CHROME_FRAME_TEST_PERF_CHROME_FRAME_PERFTEST_H_
+#include <atlbase.h>
+#include "base/logging.h"
+#include "base/perftimer.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class SimpleModule : public CAtlExeModuleT<SimpleModule> {
+ public:
+ // The ATL code does not set _pAtlModule to NULL on destruction, and therefore
+ // creating new module (for another test) will ASSERT in constructor.
+ ~SimpleModule() {
+ Term();
+ _pAtlModule = NULL;
+ }
+};
+#endif // CHROME_FRAME_TEST_PERF_CHROME_FRAME_PERFTEST_H_
+
diff --git a/chrome_frame/test/perf/chrometab_perftests.vcproj b/chrome_frame/test/perf/chrometab_perftests.vcproj
new file mode 100644
index 0000000..b894e6a
--- /dev/null
+++ b/chrome_frame/test/perf/chrometab_perftests.vcproj
@@ -0,0 +1,247 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="chrometab_perftests"
+ ProjectGUID="{760ABE9D-2B3E-48C5-A571-6CD221A04BD6}"
+ RootNamespace="chrometab_perftests"
+ Keyword="Win32Proj"
+ >
+ <Platforms>
+ <Platform
+ Name="Win32"
+ />
+ </Platforms>
+ <ToolFiles>
+ </ToolFiles>
+ <Configurations>
+ <Configuration
+ Name="Debug|Win32"
+ OutputDirectory="..\..\chrome\$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets=".\chrometab_perftests.vsprops;$(SolutionDir)..\build\debug.vsprops"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ Optimization="0"
+ AdditionalIncludeDirectories=".."
+ PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE"
+ MinimalRebuild="true"
+ BasicRuntimeChecks="3"
+ RuntimeLibrary="1"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="4"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="2"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ <Configuration
+ Name="Release|Win32"
+ OutputDirectory="..\..\chrome\$(ConfigurationName)"
+ IntermediateDirectory="$(ConfigurationName)"
+ ConfigurationType="1"
+ InheritedPropertySheets=".\chrometab_perftests.vsprops;$(SolutionDir)..\build\release.vsprops"
+ WholeProgramOptimization="1"
+ >
+ <Tool
+ Name="VCPreBuildEventTool"
+ />
+ <Tool
+ Name="VCCustomBuildTool"
+ />
+ <Tool
+ Name="VCXMLDataGeneratorTool"
+ />
+ <Tool
+ Name="VCWebServiceProxyGeneratorTool"
+ />
+ <Tool
+ Name="VCMIDLTool"
+ />
+ <Tool
+ Name="VCCLCompilerTool"
+ AdditionalIncludeDirectories=".."
+ PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE"
+ RuntimeLibrary="0"
+ UsePrecompiledHeader="0"
+ WarningLevel="3"
+ Detect64BitPortabilityProblems="false"
+ DebugInformationFormat="3"
+ />
+ <Tool
+ Name="VCManagedResourceCompilerTool"
+ />
+ <Tool
+ Name="VCResourceCompilerTool"
+ />
+ <Tool
+ Name="VCPreLinkEventTool"
+ />
+ <Tool
+ Name="VCLinkerTool"
+ LinkIncremental="1"
+ GenerateDebugInformation="true"
+ SubSystem="1"
+ OptimizeReferences="2"
+ EnableCOMDATFolding="2"
+ TargetMachine="1"
+ />
+ <Tool
+ Name="VCALinkTool"
+ />
+ <Tool
+ Name="VCManifestTool"
+ />
+ <Tool
+ Name="VCXDCMakeTool"
+ />
+ <Tool
+ Name="VCBscMakeTool"
+ />
+ <Tool
+ Name="VCFxCopTool"
+ />
+ <Tool
+ Name="VCAppVerifierTool"
+ />
+ <Tool
+ Name="VCWebDeploymentTool"
+ />
+ <Tool
+ Name="VCPostBuildEventTool"
+ />
+ </Configuration>
+ </Configurations>
+ <References>
+ </References>
+ <Files>
+ <Filter
+ Name="Source Files"
+ Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"
+ UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"
+ >
+ <File
+ RelativePath=".\chrometab_perftest.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\chrometab_perftest.h"
+ >
+ </File>
+ <File
+ RelativePath=".\silverlight.cc"
+ >
+ </File>
+ <Filter
+ Name="Common"
+ >
+ <File
+ RelativePath="..\..\chrome\test\chrome_process_util.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\chrome\test\chrome_process_util.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\chrome\test\chrome_process_util_win.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\html_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\chrome\test\perf\mem_usage.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\..\base\perf_test_suite.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\base\perftimer.cc"
+ >
+ </File>
+ <File
+ RelativePath="run_all.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\test_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\test_utils.h"
+ >
+ </File>
+ <File
+ RelativePath="..\..\chrome\test\ui\ui_test.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\utils.cc"
+ >
+ </File>
+ </Filter>
+ </Filter>
+ </Files>
+ <Globals>
+ </Globals>
+</VisualStudioProject>
diff --git a/chrome_frame/test/perf/chrometab_perftests.vsprops b/chrome_frame/test/perf/chrometab_perftests.vsprops
new file mode 100644
index 0000000..3891381
--- /dev/null
+++ b/chrome_frame/test/perf/chrometab_perftests.vsprops
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioPropertySheet
+ ProjectType="Visual C++"
+ Version="8.00"
+ Name="chrometeb_perftests"
+ InheritedPropertySheets="$(SolutionDir)..\build\common.vsprops;$(SolutionDir)..\skia\using_skia.vsprops;$(SolutionDir)..\third_party\libxml\build\using_libxml.vsprops;$(SolutionDir)..\third_party\libxslt\build\using_libxslt.vsprops;..\..\testing\using_gtest.vsprops;$(SolutionDir)..\chrome\third_party\wtl\using_wtl.vsprops;$(SolutionDir)..\tools\grit\build\using_generated_resources.vsprops"
+ >
+ <Tool
+ Name="VCCLCompilerTool"
+ PreprocessorDefinitions="_ATL_APARTMENT_THREADED;_ATL_CSTRING_EXPLICIT_CONSTRUCTORS"
+ AdditionalIncludeDirectories=""
+ />
+
+</VisualStudioPropertySheet>
diff --git a/chrome_frame/test/perf/run_all.cc b/chrome_frame/test/perf/run_all.cc
new file mode 100644
index 0000000..93a5a67
--- /dev/null
+++ b/chrome_frame/test/perf/run_all.cc
@@ -0,0 +1,17 @@
+// Copyright (c) 2006-2009 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 "base/platform_thread.h"
+#include "base/perf_test_suite.h"
+#include "base/scoped_ptr.h"
+#include "chrome/common/chrome_paths.h"
+#include "chrome_frame/test_utils.h"
+
+int main(int argc, char **argv) {
+ PerfTestSuite perf_suite(argc, argv);
+ chrome::RegisterPathProvider();
+ PlatformThread::SetName("ChromeFrame perf tests");
+ // Use ctor/raii to register the local Chrome Frame dll.
+ scoped_ptr<ScopedChromeFrameRegistrar> registrar(new ScopedChromeFrameRegistrar);
+ return perf_suite.Run();
+}
diff --git a/chrome_frame/test/perf/silverlight.cc b/chrome_frame/test/perf/silverlight.cc
new file mode 100644
index 0000000..3016c2b
--- /dev/null
+++ b/chrome_frame/test/perf/silverlight.cc
@@ -0,0 +1,165 @@
+// Copyright (c) 2009 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 <atlbase.h>
+#include <atlwin.h>
+#include <atlhost.h>
+#include "base/scoped_comptr_win.h"
+#include "chrome_frame/test/perf/chrome_frame_perftest.h"
+
+interface IXcpControlDownloadCallback;
+interface __declspec(uuid("1B36028E-B491-4bb2-8584-8A9E0A677D6E"))
+IXcpControlHost : public IUnknown {
+ typedef enum {
+ XcpHostOption_FreezeOnInitialFrame = 0x001,
+ XcpHostOption_DisableFullScreen = 0x002,
+ XcpHostOption_DisableManagedExecution = 0x008,
+ XcpHostOption_EnableCrossDomainDownloads = 0x010,
+ XcpHostOption_UseCustomAppDomain = 0x020,
+ XcpHostOption_DisableNetworking = 0x040,
+ XcpHostOption_DisableScriptCallouts = 0x080,
+ XcpHostOption_EnableHtmlDomAccess = 0x100,
+ XcpHostOption_EnableScriptableObjectAccess = 0x200,
+ } XcpHostOptions;
+
+ STDMETHOD(GetHostOptions)(DWORD* pdwOptions) PURE;
+ STDMETHOD(NotifyLoaded()) PURE;
+ STDMETHOD(NotifyError)(BSTR bstrError, BSTR bstrSource,
+ long nLine, long nColumn) PURE;
+ STDMETHOD(InvokeHandler)(BSTR bstrName, VARIANT varArg1, VARIANT varArg2,
+ VARIANT* pvarResult) PURE;
+ STDMETHOD(GetBaseUrl)(BSTR* pbstrUrl) PURE;
+ STDMETHOD(GetNamedSource)(BSTR bstrSourceName, BSTR* pbstrSource) PURE;
+ STDMETHOD(DownloadUrl)(BSTR bstrUrl, IXcpControlDownloadCallback* pCallback,
+ IStream** ppStream) PURE;
+};
+
+// Not templatized, to trade execution speed vs typing
+class IXcpControlHostImpl : public IXcpControlHost {
+ public:
+ STDMETHOD(GetHostOptions)(DWORD* pdwOptions) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(NotifyLoaded()) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(NotifyError)(BSTR bstrError, BSTR bstrSource,
+ long nLine, long nColumn) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(InvokeHandler)(BSTR bstrName, VARIANT varArg1, VARIANT varArg2,
+ VARIANT* pvarResult) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetBaseUrl)(BSTR* pbstrUrl) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(GetNamedSource)(BSTR bstrSourceName, BSTR* pbstrSource) {
+ return E_NOTIMPL;
+ }
+
+ STDMETHOD(DownloadUrl)(BSTR bstrUrl, IXcpControlDownloadCallback* pCallback,
+ IStream** ppStream) {
+ return E_NOTIMPL;
+ }
+};
+
+// Silverlight container. Supports do-nothing implementation of IXcpControlHost.
+// Should be extended to do some real movie-or-something download.
+class SilverlightContainer :
+ public IServiceProviderImpl<SilverlightContainer>,
+ public IXcpControlHostImpl,
+ public CWindowImpl<SilverlightContainer, CWindow, CWinTraits<
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ WS_EX_APPWINDOW | WS_EX_WINDOWEDGE> >,
+ public CComObjectRootEx<CComSingleThreadModel> {
+ public:
+ DECLARE_WND_CLASS_EX(L"Silverlight_container", 0, 0)
+ BEGIN_COM_MAP(SilverlightContainer)
+ COM_INTERFACE_ENTRY(IServiceProvider)
+ COM_INTERFACE_ENTRY(IXcpControlHost)
+ END_COM_MAP()
+
+ BEGIN_SERVICE_MAP(SilverlightContainer)
+ SERVICE_ENTRY(__uuidof(IXcpControlHost))
+ END_SERVICE_MAP()
+
+ BEGIN_MSG_MAP(ChromeFrameActiveXContainer)
+ MESSAGE_HANDLER(WM_DESTROY, OnDestroy)
+ END_MSG_MAP()
+
+ LRESULT OnDestroy(UINT, WPARAM, LPARAM, BOOL& handled) {
+ host_.Release();
+ return 0;
+ }
+
+ virtual void OnFinalMessage(HWND ) {
+ }
+
+ static const wchar_t* GetWndCaption() {
+ return L"Silverlight Container";
+ }
+
+ HRESULT CreateWndAndHost(RECT* r) {
+ Create(NULL, r);
+ ShowWindow(SW_SHOWDEFAULT);
+
+ CComPtr<IUnknown> spUnkContainer;
+ HRESULT hr = CAxHostWindow::_CreatorClass::CreateInstance(NULL,
+ __uuidof(IAxWinHostWindow), reinterpret_cast<void**>(&host_));
+ if (SUCCEEDED(hr)) {
+ CComPtr<IObjectWithSite> p;
+ hr = host_.QueryInterface(&p);
+ if (SUCCEEDED(hr)) {
+ p->SetSite(GetUnknown());
+ }
+ }
+ return hr;
+ }
+
+ HRESULT CreateControl() {
+ HRESULT hr = host_->CreateControl(L"AgControl.AgControl", m_hWnd, NULL);
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ return hr;
+ }
+
+ ScopedComPtr<IAxWinHostWindow> host_;
+};
+
+// Create and in-place Silverlight control. Should be extended to do something
+// more meaningful.
+TEST(ChromeFramePerf, DISABLED_HostSilverlight2) {
+ SimpleModule module;
+ AtlAxWinInit();
+ CComObjectStackEx<SilverlightContainer> wnd;
+ RECT rc = {0, 0, 800, 600};
+ wnd.CreateWndAndHost(&rc);
+ PerfTimeLogger perf_create("Create Silverlight Control2");
+ wnd.CreateControl();
+ perf_create.Done();
+ wnd.DestroyWindow();
+}
+
+// Simplest test - creates in-place Silverlight control.
+TEST(ChromeFramePerf, DISABLED_HostSilverlight) {
+ SimpleModule module;
+ AtlAxWinInit();
+ CAxWindow host;
+ RECT rc = {0, 0, 800, 600};
+ PerfTimeLogger perf_create("Create Silverlight Control");
+ host.Create(NULL, rc, L"AgControl.AgControl",
+ WS_OVERLAPPEDWINDOW | WS_VISIBLE | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
+ WS_EX_APPWINDOW | WS_EX_WINDOWEDGE);
+ EXPECT_TRUE(host.m_hWnd != NULL);
+ ScopedComPtr<IDispatch> disp;
+ HRESULT hr = host.QueryControl(disp.Receive());
+ EXPECT_HRESULT_SUCCEEDED(hr);
+ disp.Release();
+ perf_create.Done();
+}
+
diff --git a/chrome_frame/test/run_all_unittests.cc b/chrome_frame/test/run_all_unittests.cc
new file mode 100644
index 0000000..787d765
--- /dev/null
+++ b/chrome_frame/test/run_all_unittests.cc
@@ -0,0 +1,49 @@
+// 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 <atlbase.h>
+
+#include "base/at_exit.h"
+#include "base/platform_thread.h"
+#include "base/process_util.h"
+#include "base/test_suite.h"
+#include "base/command_line.h"
+#include "chrome/common/chrome_paths.h"
+
+#include "chrome_frame/test_utils.h"
+
+// To enable ATL-based code to run in this module
+class ChromeFrameUnittestsModule
+ : public CAtlExeModuleT<ChromeFrameUnittestsModule> {
+};
+
+ChromeFrameUnittestsModule _AtlModule;
+
+const wchar_t kNoRegistrationSwitch[] = L"no-registration";
+
+int main(int argc, char **argv) {
+ base::EnableTerminationOnHeapCorruption();
+ PlatformThread::SetName("ChromeFrame tests");
+
+ TestSuite test_suite(argc, argv);
+
+ // If mini_installer is used to register CF, we use the switch
+ // --no-registration to avoid repetitive registration.
+ if (CommandLine::ForCurrentProcess()->HasSwitch(kNoRegistrationSwitch)) {
+ return test_suite.Run();
+ } else {
+ // Register paths needed by the ScopedChromeFrameRegistrar.
+ chrome::RegisterPathProvider();
+
+ // This will register the chrome frame in the build directory. It currently
+ // leaves that chrome frame registered once the tests are done. It must be
+ // constructed AFTER the TestSuite is created since TestSuites create THE
+ // AtExitManager.
+ // TODO(robertshield): Make these tests restore the original registration
+ // once done.
+ ScopedChromeFrameRegistrar registrar;
+
+ return test_suite.Run();
+ }
+}
diff --git a/chrome_frame/test/test_server.cc b/chrome_frame/test/test_server.cc
new file mode 100644
index 0000000..79ea2cf
--- /dev/null
+++ b/chrome_frame/test/test_server.cc
@@ -0,0 +1,211 @@
+// 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 "base/logging.h"
+#include "base/registry.h"
+#include "base/string_util.h"
+
+#include "chrome_frame/test/test_server.h"
+
+#include "net/base/winsock_init.h"
+#include "net/http/http_util.h"
+
+namespace test_server {
+const char kDefaultHeaderTemplate[] =
+ "HTTP/1.1 %hs\r\n"
+ "Connection: close\r\n"
+ "Content-Type: %hs\r\n"
+ "Content-Length: %i\r\n\r\n";
+const char kStatusOk[] = "200 OK";
+const char kStatusNotFound[] = "404 Not Found";
+const char kDefaultContentType[] = "text/html; charset=UTF-8";
+
+void Request::ParseHeaders(const std::string& headers) {
+ size_t pos = headers.find("\r\n");
+ DCHECK(pos != std::string::npos);
+ if (pos != std::string::npos) {
+ headers_ = headers.substr(pos + 2);
+
+ StringTokenizer tokenizer(headers.begin(), headers.begin() + pos, " ");
+ std::string* parse[] = { &method_, &path_, &version_ };
+ int field = 0;
+ while (tokenizer.GetNext() && field < arraysize(parse)) {
+ parse[field++]->assign(tokenizer.token_begin(),
+ tokenizer.token_end());
+ }
+ }
+
+ // Check for content-length in case we're being sent some data.
+ net::HttpUtil::HeadersIterator it(headers_.begin(), headers_.end(),
+ "\r\n");
+ while (it.GetNext()) {
+ if (LowerCaseEqualsASCII(it.name(), "content-length")) {
+ content_length_ = StringToInt(it.values().c_str());
+ break;
+ }
+ }
+}
+
+bool Connection::CheckRequestReceived() {
+ bool ready = false;
+ if (request_.method().length()) {
+ // Headers have already been parsed. Just check content length.
+ ready = (data_.size() >= request_.content_length());
+ } else {
+ size_t index = data_.find("\r\n\r\n");
+ if (index != std::string::npos) {
+ // Parse the headers before returning and chop them of the
+ // data buffer we've already received.
+ std::string headers(data_.substr(0, index + 2));
+ request_.ParseHeaders(headers);
+ data_.erase(0, index + 4);
+ ready = (data_.size() >= request_.content_length());
+ }
+ }
+
+ return ready;
+}
+
+bool FileResponse::GetContentType(std::string* content_type) const {
+ size_t length = ContentLength();
+ char buffer[4096];
+ void* data = NULL;
+
+ if (length) {
+ // Create a copy of the first few bytes of the file.
+ // If we try and use the mapped file directly, FindMimeFromData will crash
+ // 'cause it cheats and temporarily tries to write to the buffer!
+ length = std::min(arraysize(buffer), length);
+ memcpy(buffer, file_->data(), length);
+ data = buffer;
+ }
+
+ LPOLESTR mime_type = NULL;
+ FindMimeFromData(NULL, file_path_.value().c_str(), data, length, NULL,
+ FMFD_DEFAULT, &mime_type, 0);
+ if (mime_type) {
+ *content_type = WideToASCII(mime_type);
+ ::CoTaskMemFree(mime_type);
+ }
+
+ return content_type->length() > 0;
+}
+
+void FileResponse::WriteContents(ListenSocket* socket) const {
+ DCHECK(file_.get());
+ if (file_.get()) {
+ socket->Send(reinterpret_cast<const char*>(file_->data()),
+ file_->length(), false);
+ }
+}
+
+size_t FileResponse::ContentLength() const {
+ if (file_.get() == NULL) {
+ file_.reset(new file_util::MemoryMappedFile());
+ if (!file_->Initialize(file_path_)) {
+ NOTREACHED();
+ file_.reset();
+ }
+ }
+ return file_.get() ? file_->length() : 0;
+}
+
+bool RedirectResponse::GetCustomHeaders(std::string* headers) const {
+ *headers = StringPrintf("HTTP/1.1 302 Found\r\n"
+ "Connection: close\r\n"
+ "Content-Length: 0\r\n"
+ "Content-Type: text/html\r\n"
+ "Location: %hs\r\n\r\n", redirect_url_.c_str());
+ return true;
+}
+
+SimpleWebServer::SimpleWebServer(int port) {
+ CHECK(MessageLoop::current()) << "SimpleWebServer requires a message loop";
+ net::EnsureWinsockInit();
+ AddResponse(&quit_);
+ server_ = ListenSocket::Listen("127.0.0.1", port, this);
+ DCHECK(server_.get() != NULL);
+}
+
+SimpleWebServer::~SimpleWebServer() {
+ ConnectionList::const_iterator it;
+ for (it = connections_.begin(); it != connections_.end(); it++)
+ delete (*it);
+ connections_.clear();
+}
+
+void SimpleWebServer::AddResponse(Response* response) {
+ responses_.push_back(response);
+}
+
+Response* SimpleWebServer::FindResponse(const Request& request) const {
+ std::list<Response*>::const_iterator it;
+ for (it = responses_.begin(); it != responses_.end(); it++) {
+ Response* response = (*it);
+ if (response->Matches(request)) {
+ return response;
+ }
+ }
+ return NULL;
+}
+
+Connection* SimpleWebServer::FindConnection(const ListenSocket* socket) const {
+ ConnectionList::const_iterator it;
+ for (it = connections_.begin(); it != connections_.end(); it++) {
+ if ((*it)->IsSame(socket)) {
+ return (*it);
+ }
+ }
+ return NULL;
+}
+
+void SimpleWebServer::DidAccept(ListenSocket* server,
+ ListenSocket* connection) {
+ connections_.push_back(new Connection(connection));
+}
+
+void SimpleWebServer::DidRead(ListenSocket* connection,
+ const std::string& data) {
+ Connection* c = FindConnection(connection);
+ DCHECK(c);
+ c->AddData(data);
+ if (c->CheckRequestReceived()) {
+ const Request& request = c->request();
+ Response* response = FindResponse(request);
+ if (response) {
+ std::string headers;
+ if (!response->GetCustomHeaders(&headers)) {
+ std::string content_type;
+ if (!response->GetContentType(&content_type))
+ content_type = kDefaultContentType;
+ headers = StringPrintf(kDefaultHeaderTemplate, kStatusOk,
+ content_type.c_str(), response->ContentLength());
+ }
+
+ connection->Send(headers, false);
+ response->WriteContents(connection);
+ response->IncrementAccessCounter();
+ } else {
+ std::string payload = "sorry, I can't find " + request.path();
+ std::string headers(StringPrintf(kDefaultHeaderTemplate, kStatusNotFound,
+ kDefaultContentType, payload.length()));
+ connection->Send(headers, false);
+ connection->Send(payload, false);
+ }
+ }
+}
+
+void SimpleWebServer::DidClose(ListenSocket* sock) {
+ // To keep the historical list of connections reasonably tidy, we delete
+ // 404's when the connection ends.
+ Connection* c = FindConnection(sock);
+ DCHECK(c);
+ if (!FindResponse(c->request())) {
+ // extremely inefficient, but in one line and not that common... :)
+ connections_.erase(std::find(connections_.begin(), connections_.end(), c));
+ delete c;
+ }
+}
+
+} // namespace test_server
diff --git a/chrome_frame/test/test_server.h b/chrome_frame/test/test_server.h
new file mode 100644
index 0000000..896b2b3
--- /dev/null
+++ b/chrome_frame/test/test_server.h
@@ -0,0 +1,296 @@
+// 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.
+
+#ifndef CHROME_FRAME_TEST_TEST_SERVER_H_
+#define CHROME_FRAME_TEST_TEST_SERVER_H_
+
+// Implementation of an HTTP server for tests.
+// To instantiate the server, make sure you have a message loop on the
+// current thread and then create an instance of the SimpleWebServer class.
+// The server uses two basic concepts, a request and a response.
+// The Response interface represents an item (e.g. a document) available from
+// the server. A Request object represents a request from a client (e.g. a
+// browser). There are several basic Response classes implemented in this file,
+// all derived from the Response interface.
+//
+// Here's a simple example that starts a web server that can serve up
+// a single document (http://localhost:1337/foo).
+// All other requests will get a 404.
+//
+// MessageLoopForUI loop;
+// test_server::SimpleWebServer server(1337);
+// test_server::SimpleResponse document("/foo", "Hello World!");
+// test_server.AddResponse(&document);
+// loop.MessageLoop::Run();
+//
+// To close the web server, just go to http://localhost:1337/quit.
+//
+// All Response classes count how many times they have been accessed. Just
+// call Response::accessed().
+//
+// To implement a custom response object (e.g. to match against a request
+// based on some data, serve up dynamic content or take some action on the
+// server), just inherit from one of the response classes or directly from the
+// Response interface and add your response object to the server's list of
+// response objects.
+
+#include <list>
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/file_util.h"
+#include "net/base/listen_socket.h"
+
+namespace test_server {
+
+class Request {
+ public:
+ Request() : content_length_(0) {
+ }
+
+ void ParseHeaders(const std::string& headers);
+
+ const std::string& method() const {
+ return method_;
+ }
+
+ const std::string& path() const {
+ return path_;
+ }
+
+ const std::string& headers() const {
+ return headers_;
+ }
+
+ size_t content_length() const {
+ return content_length_;
+ }
+
+ protected:
+ std::string method_;
+ std::string path_;
+ std::string version_;
+ std::string headers_;
+ size_t content_length_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Request);
+};
+
+// Manages request headers for a single request.
+// For each successful request that's made, the server will keep an instance
+// of this class so that they can be checked even after the server has been
+// shut down.
+class Connection {
+ public:
+ explicit Connection(ListenSocket* sock) : socket_(sock) {
+ }
+
+ ~Connection() {
+ }
+
+ bool IsSame(const ListenSocket* socket) const {
+ return socket_ == socket;
+ }
+
+ void AddData(const std::string& data) {
+ data_ += data;
+ }
+
+ bool CheckRequestReceived();
+
+ const Request& request() const {
+ return request_;
+ }
+
+ protected:
+ scoped_refptr<ListenSocket> socket_;
+ std::string data_;
+ Request request_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Connection);
+};
+
+// Abstract interface with default implementations for some of the methods and
+// a counter for how many times the response object has served requests.
+class Response {
+ public:
+ Response() : accessed_(0) {
+ }
+
+ virtual ~Response() {
+ }
+
+ // Returns true if this response object should be used for a given request.
+ virtual bool Matches(const Request& r) const = 0;
+
+ // Response objects can optionally supply their own HTTP headers, completely
+ // bypassing the default ones.
+ virtual bool GetCustomHeaders(std::string* headers) const {
+ return false;
+ }
+
+ // Optionally provide a content type. Return false if you don't specify
+ // a content type.
+ virtual bool GetContentType(std::string* content_type) const {
+ return false;
+ }
+
+ virtual size_t ContentLength() const {
+ return 0;
+ }
+
+ virtual void WriteContents(ListenSocket* socket) const {
+ }
+
+ void IncrementAccessCounter() {
+ accessed_++;
+ }
+
+ size_t accessed() const {
+ return accessed_;
+ }
+
+ protected:
+ size_t accessed_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(Response);
+};
+
+// Partial implementation of Response that matches a request's path.
+// This is just a convenience implementation for the boilerplate implementation
+// of Matches(). Don't instantiate directly.
+class ResponseForPath : public Response {
+ public:
+ explicit ResponseForPath(const char* request_path)
+ : request_path_(request_path) {
+ }
+
+ virtual bool Matches(const Request& r) const {
+ return r.path().compare(request_path_) == 0;
+ }
+
+ protected:
+ std::string request_path_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ResponseForPath);
+};
+
+// A very basic implementation of a response.
+// A simple response matches a single document path on the server
+// (e.g. "/foo") and returns a document in the form of a string.
+class SimpleResponse : public ResponseForPath {
+ public:
+ SimpleResponse(const char* request_path, const std::string& contents)
+ : ResponseForPath(request_path), contents_(contents) {
+ }
+
+ virtual void WriteContents(ListenSocket* socket) const {
+ socket->Send(contents_.c_str(), contents_.length(), false);
+ }
+
+ virtual size_t ContentLength() const {
+ return contents_.length();
+ }
+
+ protected:
+ std::string contents_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimpleResponse);
+};
+
+// To serve up files from the web server, create an instance of FileResponse
+// and add it to the server's list of responses. The content type of the
+// file will be determined by calling FindMimeFromData which examines the
+// contents of the file and performs registry lookups.
+class FileResponse : public ResponseForPath {
+ public:
+ FileResponse(const char* request_path, const FilePath& file_path)
+ : ResponseForPath(request_path), file_path_(file_path) {
+ }
+
+ virtual bool GetContentType(std::string* content_type) const;
+ virtual void WriteContents(ListenSocket* socket) const;
+ virtual size_t ContentLength() const;
+
+ protected:
+ FilePath file_path_;
+ mutable scoped_ptr<file_util::MemoryMappedFile> file_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileResponse);
+};
+
+// Returns a 302 (temporary redirect) to redirect the client from a path
+// on the test server to a different URL.
+class RedirectResponse : public ResponseForPath {
+ public:
+ RedirectResponse(const char* request_path, const std::string& redirect_url)
+ : ResponseForPath(request_path), redirect_url_(redirect_url) {
+ }
+
+ virtual bool GetCustomHeaders(std::string* headers) const;
+
+ protected:
+ std::string redirect_url_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(RedirectResponse);
+};
+
+// typedef for a list of connections. Used by SimpleWebServer.
+typedef std::list<Connection*> ConnectionList;
+
+// Implementation of a simple http server.
+// Before creating an instance of the server, make sure the current thread
+// has a message loop.
+class SimpleWebServer : public ListenSocket::ListenSocketDelegate {
+ public:
+ explicit SimpleWebServer(int port);
+ virtual ~SimpleWebServer();
+
+ void AddResponse(Response* response);
+
+ // ListenSocketDelegate overrides.
+ virtual void DidAccept(ListenSocket* server, ListenSocket* connection);
+ virtual void DidRead(ListenSocket* connection, const std::string& data);
+ virtual void DidClose(ListenSocket* sock);
+
+ const ConnectionList& connections() {
+ return connections_;
+ }
+
+ protected:
+ class QuitResponse : public SimpleResponse {
+ public:
+ QuitResponse()
+ : SimpleResponse("/quit", "So long and thanks for all the fish.") {
+ }
+
+ virtual void QuitResponse::WriteContents(ListenSocket* socket) const {
+ SimpleResponse::WriteContents(socket);
+ MessageLoop::current()->Quit();
+ }
+ };
+
+ Response* FindResponse(const Request& request) const;
+ Connection* FindConnection(const ListenSocket* socket) const;
+
+ protected:
+ scoped_refptr<ListenSocket> server_;
+ ConnectionList connections_;
+ std::list<Response*> responses_;
+ QuitResponse quit_;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(SimpleWebServer);
+};
+
+} // namespace test_server
+
+#endif // CHROME_FRAME_TEST_TEST_SERVER_H_
diff --git a/chrome_frame/test/test_server_test.cc b/chrome_frame/test/test_server_test.cc
new file mode 100644
index 0000000..4bd139e
--- /dev/null
+++ b/chrome_frame/test/test_server_test.cc
@@ -0,0 +1,194 @@
+// 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 <wininet.h>
+
+#include "base/basictypes.h"
+#include "base/path_service.h"
+#include "base/scoped_handle_win.h"
+#include "chrome_frame/test/test_server.h"
+#include "net/base/cookie_monster.h"
+#include "net/base/host_resolver_proc.h"
+#include "net/disk_cache/disk_cache.h"
+#include "net/http/http_cache.h"
+#include "net/proxy/proxy_service.h"
+#include "net/url_request/url_request.h"
+#include "net/url_request/url_request_unittest.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+class TestServerTest: public testing::Test {
+ protected:
+ virtual void SetUp() {
+ PathService::Get(base::DIR_SOURCE_ROOT, &source_path_);
+ source_path_ = source_path_.Append(FILE_PATH_LITERAL("chrome_frame"));
+ }
+ virtual void TearDown() {
+ }
+
+ public:
+ const FilePath& source_path() const {
+ return source_path_;
+ }
+
+ protected:
+ FilePath source_path_;
+};
+
+namespace {
+
+class ScopedInternet {
+ public:
+ explicit ScopedInternet(HINTERNET handle)
+ : h_(handle) {
+ }
+ ~ScopedInternet() {
+ if (h_) {
+ InternetCloseHandle(h_);
+ }
+ }
+
+ operator HINTERNET() {
+ return h_;
+ }
+
+ protected:
+ HINTERNET h_;
+};
+
+class URLRequestTestContext : public URLRequestContext {
+ public:
+ URLRequestTestContext() {
+ host_resolver_ = net::CreateSystemHostResolver();
+ proxy_service_ = net::ProxyService::CreateNull();
+ ssl_config_service_ = new net::SSLConfigServiceDefaults;
+ http_transaction_factory_ =
+ new net::HttpCache(
+ net::HttpNetworkLayer::CreateFactory(host_resolver_, proxy_service_,
+ ssl_config_service_),
+ disk_cache::CreateInMemoryCacheBackend(0));
+ // In-memory cookie store.
+ cookie_store_ = new net::CookieMonster();
+ }
+
+ virtual ~URLRequestTestContext() {
+ delete http_transaction_factory_;
+ }
+};
+
+class TestURLRequest : public URLRequest {
+ public:
+ TestURLRequest(const GURL& url, Delegate* delegate)
+ : URLRequest(url, delegate) {
+ set_context(new URLRequestTestContext());
+ }
+};
+
+class UrlTaskChain {
+ public:
+ UrlTaskChain(const char* url, UrlTaskChain* next)
+ : url_(url), next_(next) {
+ }
+
+ void Run() {
+ EXPECT_EQ(0, delegate_.response_started_count());
+
+ MessageLoopForIO loop;
+
+ TestURLRequest r(GURL(url_), &delegate_);
+ r.Start();
+ EXPECT_TRUE(r.is_pending());
+
+ MessageLoop::current()->Run();
+
+ EXPECT_EQ(1, delegate_.response_started_count());
+ EXPECT_FALSE(delegate_.received_data_before_response());
+ EXPECT_NE(0, delegate_.bytes_received());
+ }
+
+ UrlTaskChain* next() const {
+ return next_;
+ }
+
+ const std::string& response() const {
+ return delegate_.data_received();
+ }
+
+ protected:
+ std::string url_;
+ TestDelegate delegate_;
+ UrlTaskChain* next_;
+};
+
+DWORD WINAPI FetchUrl(void* param) {
+ UrlTaskChain* task = reinterpret_cast<UrlTaskChain*>(param);
+ while (task != NULL) {
+ task->Run();
+ task = task->next();
+ }
+
+ return 0;
+}
+
+struct QuitMessageHit {
+ explicit QuitMessageHit(MessageLoopForUI* loop) : loop_(loop), hit_(false) {
+ }
+
+ MessageLoopForUI* loop_;
+ bool hit_;
+};
+
+void QuitMessageLoop(QuitMessageHit* msg) {
+ msg->hit_ = true;
+ msg->loop_->PostTask(FROM_HERE, new MessageLoop::QuitTask);
+}
+
+} // end namespace
+
+TEST_F(TestServerTest, DISABLED_TestServer) {
+ // The web server needs a loop to exist on this thread during construction
+ // the loop must be created before we construct the server.
+ MessageLoopForUI loop;
+
+ test_server::SimpleWebServer server(1337);
+ test_server::SimpleResponse person("/person", "Guthrie Govan!");
+ server.AddResponse(&person);
+ test_server::FileResponse file("/file", source_path().Append(
+ FILE_PATH_LITERAL("CFInstance.js")));
+ server.AddResponse(&file);
+ test_server::RedirectResponse redir("/goog", "http://www.google.com/");
+ server.AddResponse(&redir);
+
+ // We should never hit this, but it's our way to break out of the test if
+ // things start hanging.
+ QuitMessageHit quit_msg(&loop);
+ loop.PostDelayedTask(FROM_HERE,
+ NewRunnableFunction(QuitMessageLoop, &quit_msg),
+ 10 * 1000);
+
+ UrlTaskChain quit_task("http://localhost:1337/quit", NULL);
+ UrlTaskChain fnf_task("http://localhost:1337/404", &quit_task);
+ UrlTaskChain person_task("http://localhost:1337/person", &fnf_task);
+ UrlTaskChain file_task("http://localhost:1337/file", &person_task);
+ UrlTaskChain goog_task("http://localhost:1337/goog", &file_task);
+
+ DWORD tid = 0;
+ ScopedHandle worker(::CreateThread(NULL, 0, FetchUrl, &goog_task, 0, &tid));
+ loop.MessageLoop::Run();
+
+ EXPECT_FALSE(quit_msg.hit_);
+ if (!quit_msg.hit_) {
+ EXPECT_EQ(::WaitForSingleObject(worker, 10 * 1000), WAIT_OBJECT_0);
+
+ EXPECT_EQ(person.accessed(), 1);
+ EXPECT_EQ(file.accessed(), 1);
+ EXPECT_EQ(redir.accessed(), 1);
+
+ EXPECT_TRUE(person_task.response().find("Guthrie") != std::string::npos);
+ EXPECT_TRUE(file_task.response().find("function") != std::string::npos);
+ EXPECT_TRUE(goog_task.response().find("<title>") != std::string::npos);
+ } else {
+ ::TerminateThread(worker, ~0);
+ }
+}
diff --git a/chrome_frame/test/util_unittests.cc b/chrome_frame/test/util_unittests.cc
new file mode 100644
index 0000000..dc3d72d
--- /dev/null
+++ b/chrome_frame/test/util_unittests.cc
@@ -0,0 +1,120 @@
+// Copyright (c) 2006-2009 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 "base/file_version_info.h"
+#include "chrome_frame/test/chrome_frame_unittests.h"
+#include "chrome_frame/utils.h"
+
+const wchar_t kChannelName[] = L"-dev";
+const wchar_t kSuffix[] = L"-fix";
+
+TEST(UtilTests, AppendSuffixToChannelNameTest) {
+ std::wstring str_base;
+ std::wstring channel_name(kChannelName);
+ std::wstring suffix(kSuffix);
+
+ str_base = L"2.0-dev-bar";
+ EXPECT_TRUE(AppendSuffixToChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-dev-fix-bar", str_base.c_str());
+
+ str_base = L"2.0-dev-fix-bar";
+ EXPECT_FALSE(AppendSuffixToChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-dev-fix-bar", str_base.c_str());
+
+ str_base = L"2.0-dev-bar-dev-bar";
+ EXPECT_TRUE(AppendSuffixToChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-dev-fix-bar-dev-bar", str_base.c_str());
+
+ str_base = L"2.0";
+ EXPECT_FALSE(AppendSuffixToChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0", str_base.c_str());
+
+ str_base = L"2.0-devvvv";
+ EXPECT_TRUE(AppendSuffixToChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-dev-fixvvv", str_base.c_str());
+}
+
+TEST(UtilTests, RemoveSuffixFromStringTest) {
+ std::wstring str_base;
+ std::wstring channel_name(kChannelName);
+ std::wstring suffix(kSuffix);
+
+ str_base = L"2.0-dev-fix";
+ EXPECT_TRUE(RemoveSuffixFromChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-dev", str_base.c_str());
+
+ str_base = L"2.0-dev-fix-full";
+ EXPECT_TRUE(RemoveSuffixFromChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-dev-full", str_base.c_str());
+
+ str_base = L"2.0";
+ EXPECT_FALSE(RemoveSuffixFromChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0", str_base.c_str());
+
+ str_base = L"2.0-dev";
+ EXPECT_FALSE(RemoveSuffixFromChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-dev", str_base.c_str());
+
+ str_base = L"2.0-fix";
+ EXPECT_FALSE(RemoveSuffixFromChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-fix", str_base.c_str());
+
+ str_base = L"2.0-full-fix";
+ EXPECT_FALSE(RemoveSuffixFromChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-full-fix", str_base.c_str());
+
+ str_base = L"2.0-dev-dev-fix";
+ EXPECT_TRUE(RemoveSuffixFromChannelName(&str_base, channel_name, suffix));
+ EXPECT_STREQ(L"2.0-dev-dev", str_base.c_str());
+}
+
+TEST(UtilTests, GetModuleVersionTest) {
+ HMODULE mod = GetModuleHandle(L"kernel32.dll");
+ EXPECT_NE(mod, static_cast<HMODULE>(NULL));
+ wchar_t path[MAX_PATH] = {0};
+ GetModuleFileName(mod, path, arraysize(path));
+
+ // Use the method that goes to disk
+ scoped_ptr<FileVersionInfo> base_info(
+ FileVersionInfo::CreateFileVersionInfo(path));
+ EXPECT_TRUE(base_info.get() != NULL);
+
+ // Use the method that doesn't go to disk
+ uint32 low = 0, high = 0;
+ EXPECT_TRUE(GetModuleVersion(mod, &high, &low));
+ EXPECT_NE(high, 0);
+ EXPECT_NE(low, 0);
+
+ // Make sure they give the same results.
+ VS_FIXEDFILEINFO* fixed_info = base_info->fixed_file_info();
+ EXPECT_TRUE(fixed_info != NULL);
+
+ EXPECT_EQ(fixed_info->dwFileVersionMS, static_cast<DWORD>(high));
+ EXPECT_EQ(fixed_info->dwFileVersionLS, static_cast<DWORD>(low));
+}
+
+TEST(UtilTests, HaveSameOrigin) {
+ struct OriginCompare {
+ const char* a;
+ const char* b;
+ bool same_origin;
+ } test_cases[] = {
+ { "", "", true },
+ { "*", "*", true },
+ { "*", "+", false },
+ { "http://www.google.com/", "http://www.google.com/", true },
+ { "http://www.google.com", "http://www.google.com/", true },
+ { "http://www.google.com:80/", "http://www.google.com/", true },
+ { "http://www.google.com:8080/", "http://www.google.com/", false },
+ { "https://www.google.com/", "http://www.google.com/", false },
+ { "http://docs.google.com/", "http://www.google.com/", false },
+ { "https://www.google.com/", "https://www.google.com:443/", true },
+ { "https://www.google.com/", "https://www.google.com:443", true },
+ };
+
+ for (int i = 0; i < arraysize(test_cases); ++i) {
+ const OriginCompare& test = test_cases[i];
+ EXPECT_EQ(test.same_origin, HaveSameOrigin(test.a, test.b));
+ }
+}