diff options
-rw-r--r-- | chrome_frame/test/chrome_frame_test_utils.cc | 52 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_test_utils.h | 55 | ||||
-rwxr-xr-x | chrome_frame/test/data/no_interference/empty.html | 0 | ||||
-rw-r--r-- | chrome_frame/test/data/no_interference/window_open.html | 12 | ||||
-rw-r--r-- | chrome_frame/test/no_interference_test.cc | 129 | ||||
-rw-r--r-- | chrome_frame/test/test_mock_with_web_server.cc | 65 | ||||
-rw-r--r-- | chrome_frame/test/test_mock_with_web_server.h | 8 | ||||
-rw-r--r-- | chrome_frame/test/test_with_web_server.cc | 4 |
8 files changed, 256 insertions, 69 deletions
diff --git a/chrome_frame/test/chrome_frame_test_utils.cc b/chrome_frame/test/chrome_frame_test_utils.cc index 1cc1f47..5b60922 100644 --- a/chrome_frame/test/chrome_frame_test_utils.cc +++ b/chrome_frame/test/chrome_frame_test_utils.cc @@ -14,6 +14,7 @@ #include "base/message_loop.h" #include "base/path_service.h" #include "base/registry.h" // to find IE and firefox +#include "base/scoped_bstr_win.h" #include "base/scoped_handle.h" #include "base/scoped_comptr_win.h" #include "base/utf_string_conversions.h" @@ -37,6 +38,7 @@ const wchar_t kSafariImageName[] = L"safari.exe"; const wchar_t kChromeImageName[] = L"chrome.exe"; const wchar_t kIEProfileName[] = L"iexplore"; const wchar_t kChromeLauncher[] = L"chrome_launcher.exe"; +const int kChromeFrameLongNavigationTimeoutInSeconds = 10; // Callback function for EnumThreadWindows. BOOL CALLBACK CloseWindowsThreadCallback(HWND hwnd, LPARAM param) { @@ -667,6 +669,11 @@ void WebBrowserEventSink::SendMouseClick(int x, int y, simulate_input::SendMouseClick(GetRendererWindow(), x, y, button); } +void WebBrowserEventSink::SendMouseClickToIE(int x, int y, + simulate_input::MouseButton button) { + simulate_input::SendMouseClick(GetIERendererWindow(), x, y, button); +} + void WebBrowserEventSink::ConnectToChromeFrame() { DCHECK(web_browser2_); if (chrome_frame_.get()) @@ -730,6 +737,24 @@ HWND WebBrowserEventSink::GetRendererWindow() { return renderer_window; } +HWND WebBrowserEventSink::GetIERendererWindow() { + DCHECK(web_browser2_); + HWND renderer_window = NULL; + ScopedComPtr<IDispatch> doc; + HRESULT hr = web_browser2_->get_Document(doc.Receive()); + EXPECT_HRESULT_SUCCEEDED(hr); + EXPECT_TRUE(doc); + if (doc) { + ScopedComPtr<IOleWindow> ole_window; + ole_window.QueryFrom(doc); + EXPECT_TRUE(ole_window); + if (ole_window) { + ole_window->GetWindow(&renderer_window); + } + } + return renderer_window; +} + HRESULT WebBrowserEventSink::SetWebBrowser(IWebBrowser2* web_browser2) { DCHECK(web_browser2_.get() == NULL); DCHECK(!is_main_browser_object_); @@ -749,7 +774,7 @@ HRESULT WebBrowserEventSink::CloseWebBrowser() { return S_OK; } -void WebBrowserEventSink::ExpectRendererWindowHasfocus() { +void WebBrowserEventSink::ExpectRendererWindowHasFocus() { HWND renderer_window = GetRendererWindow(); EXPECT_TRUE(IsWindow(renderer_window)); @@ -773,6 +798,31 @@ void WebBrowserEventSink::ExpectRendererWindowHasfocus() { EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE)); } +void WebBrowserEventSink::ExpectIERendererWindowHasFocus() { + HWND renderer_window = GetIERendererWindow(); + EXPECT_TRUE(IsWindow(renderer_window)); + + DWORD renderer_thread = 0; + DWORD renderer_process = 0; + renderer_thread = GetWindowThreadProcessId(renderer_window, + &renderer_process); + + ASSERT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, TRUE)); + HWND focus_window = GetFocus(); + EXPECT_TRUE(focus_window == renderer_window); + EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE)); +} + +void WebBrowserEventSink::ExpectAddressBarUrl( + const std::wstring& expected_url) { + DCHECK(web_browser2_); + if (web_browser2_) { + ScopedBstr address_bar_url; + EXPECT_EQ(S_OK, web_browser2_->get_LocationURL(address_bar_url.Receive())); + EXPECT_EQ(expected_url, std::wstring(address_bar_url)); + } +} + void WebBrowserEventSink::Exec(const GUID* cmd_group_guid, DWORD command_id, DWORD cmd_exec_opt, VARIANT* in_args, VARIANT* out_args) { diff --git a/chrome_frame/test/chrome_frame_test_utils.h b/chrome_frame/test/chrome_frame_test_utils.h index 5a4e841..717b204 100644 --- a/chrome_frame/test/chrome_frame_test_utils.h +++ b/chrome_frame/test/chrome_frame_test_utils.h @@ -53,6 +53,7 @@ extern const wchar_t kOperaImageName[]; extern const wchar_t kSafariImageName[]; extern const wchar_t kChromeImageName[]; extern const wchar_t kChromeLauncher[]; +extern const int kChromeFrameLongNavigationTimeoutInSeconds; // Temporarily impersonate the current thread to low integrity for the lifetime // of the object. Destructor will automatically revert integrity level. @@ -106,10 +107,10 @@ class TimedMsgLoop { // testing::InvokeWithoutArgs since it returns a // non-public (testing::internal) type. #define QUIT_LOOP(loop) testing::InvokeWithoutArgs(\ - CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::Quit)) + testing::CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::Quit)) #define QUIT_LOOP_SOON(loop, seconds) testing::InvokeWithoutArgs(\ - CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::QuitAfter, \ + testing::CreateFunctor(&loop, &chrome_frame_test::TimedMsgLoop::QuitAfter, \ seconds)) // Launches IE as a COM server and returns the corresponding IWebBrowser2 @@ -194,21 +195,25 @@ END_SINK_MAP() void SetFocusToChrome(); // Send keyboard input to the renderer window hosted in chrome using - // SendInput API + // SendInput API. void SendKeys(const wchar_t* input_string); // Send mouse click to the renderer window hosted in chrome using - // SendInput API + // SendInput API. void SendMouseClick(int x, int y, simulate_input::MouseButton button); + // Send mouse click to the renderer window hosted in IE using + // SendInput API. + void SendMouseClickToIE(int x, int y, simulate_input::MouseButton button); + void Exec(const GUID* cmd_group_guid, DWORD command_id, DWORD cmd_exec_opt, VARIANT* in_args, VARIANT* out_args); - // Watch for new window created + // Watch for new window created. void WatchChromeWindow(const wchar_t* window_class); void StopWatching(); - // Overridable methods for the mock + // Overridable methods for the mock. STDMETHOD_(void, OnNavigateError)(IDispatch* dispatch, VARIANT* url, VARIANT* frame_name, VARIANT* status_code, VARIANT* cancel) { @@ -270,7 +275,9 @@ END_SINK_MAP() } HRESULT SetWebBrowser(IWebBrowser2* web_browser2); - void ExpectRendererWindowHasfocus(); + void ExpectRendererWindowHasFocus(); + void ExpectIERendererWindowHasFocus(); + void ExpectAddressBarUrl(const std::wstring& url); // Closes the web browser in such a way that the OnQuit notification will // be fired when the window closes (async). @@ -298,6 +305,7 @@ END_SINK_MAP() void ConnectToChromeFrame(); void DisconnectFromChromeFrame(); HWND GetRendererWindow(); + HWND GetIERendererWindow(); public: ScopedComPtr<IWebBrowser2> web_browser2_; @@ -337,6 +345,39 @@ std::wstring GetExeVersion(const std::wstring& exe_path); // Returns the version of Internet Explorer on the machine. IEVersion GetInstalledIEVersion(); +// A convenience class to close all open IE windows at the end +// of a scope. It's more convenient to do it this way than to +// explicitly call chrome_frame_test::CloseAllIEWindows at the +// end of a test since part of the test's cleanup code may be +// in object destructors that would run after CloseAllIEWindows +// would get called. +// Ideally all IE windows should be closed when this happens so +// if the test ran normally, we should not have any windows to +// close at this point. +class CloseIeAtEndOfScope { + public: + CloseIeAtEndOfScope() {} + ~CloseIeAtEndOfScope() { + int closed = CloseAllIEWindows(); + DLOG_IF(ERROR, closed != 0) + << StringPrintf("Closed %i windows forcefully", closed); + } +}; + +// Specialization of CComObjectStackEx that performs object cleanup via +// calling Base::Uninitialize() before we get to CComObjectStackEx' destructor. +// The CComObjectStackEx destructor expects the reference count to be 0 +// or it will throw an assert. To work around that and to avoid having to +// explicitly call Uninitialize() at the end of every test, we override the +// destructor here to perform the cleanup. +template <class Base> +class ComStackObjectWithUninitialize : public CComObjectStackEx<Base> { + public: + virtual ~ComStackObjectWithUninitialize() { + Base::Uninitialize(); + } +}; + } // namespace chrome_frame_test #endif // CHROME_FRAME_TEST_CHROME_FRAME_TEST_UTILS_H_ diff --git a/chrome_frame/test/data/no_interference/empty.html b/chrome_frame/test/data/no_interference/empty.html new file mode 100755 index 0000000..e69de29 --- /dev/null +++ b/chrome_frame/test/data/no_interference/empty.html diff --git a/chrome_frame/test/data/no_interference/window_open.html b/chrome_frame/test/data/no_interference/window_open.html new file mode 100644 index 0000000..b426b17 --- /dev/null +++ b/chrome_frame/test/data/no_interference/window_open.html @@ -0,0 +1,12 @@ +<html> + <head> + <script type="text/javascript"> + window.onload = function() { + window.open("empty.html", "test"); + } + </script> + </head> + <body> + Calling window.open... + </body> +</html>
\ No newline at end of file diff --git a/chrome_frame/test/no_interference_test.cc b/chrome_frame/test/no_interference_test.cc new file mode 100644 index 0000000..acd1d5c --- /dev/null +++ b/chrome_frame/test/no_interference_test.cc @@ -0,0 +1,129 @@ +// Copyright (c) 2010 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/test_mock_with_web_server.h" + +#include "base/scoped_variant_win.h" +#include "base/utf_string_conversions.h" +#include "chrome_frame/test/simulate_input.h" +#include "chrome_frame/test/test_with_web_server.h" + +using chrome_frame_test::CloseIeAtEndOfScope; +using chrome_frame_test::ComStackObjectWithUninitialize; +using chrome_frame_test::kChromeFrameLongNavigationTimeoutInSeconds; +using testing::_; + +namespace { + +// The same as MockWebBrowserEventSink, except OnDocumentComplete is added. +// This class can be merged with its base class, but it would require some +// modification to most of the test cases. +class MockWebBrowserEventSink + : public chrome_frame_test::MockWebBrowserEventSink { + public: + // This method is called for each OnDocumentComplete event in which the + // document is rendered by IE and not CF. + MOCK_METHOD1(OnIELoad, void (const wchar_t* url)); // NOLINT + + // The OnDocumentComplete event is received for every document load, + // regardless of whether IE or CF is hosting. This method will call + // OnIELoad iff the document was loaded in an IE renderer. + STDMETHOD_(void, OnDocumentComplete)(IDispatch* dispatch, + VARIANT* url_variant) { + ScopedVariant url; + if (url_variant) + url.Reset(*url_variant); + // To determine if the document is loaded in IE or CF, check if + // IHTMLDocument2 can be retrieved. If so, call OnIELoad. + ScopedComPtr<IWebBrowser2> web_browser2; + web_browser2.QueryFrom(dispatch); + EXPECT_TRUE(web_browser2); + if (web_browser2) { + ScopedComPtr<IDispatch> doc_dispatch; + HRESULT hr = web_browser2->get_Document(doc_dispatch.Receive()); + EXPECT_HRESULT_SUCCEEDED(hr); + EXPECT_TRUE(doc_dispatch); + if (doc_dispatch) { + ScopedComPtr<IHTMLDocument2> doc; + doc.QueryFrom(doc_dispatch); + if (doc) { + OnIELoad(V_BSTR(&url)); + } + } + } + } +}; + +// Fixture for tests which ensure that Chrome Frame does not interfere with +// normal IE operation. +// TODO(kkania): Move this fixture to test_mock_with_web_server.h and change +// those tests to use this too. +class NoInterferenceTest : public ChromeFrameTestWithWebServer { + protected: + // Launches IE and navigates to |url|, then waits until receiving a quit + // message or the timeout is exceeded. + void LaunchIEAndNavigate(const std::wstring& url) { + CloseIeAtEndOfScope last_resort_close_ie; + + EXPECT_CALL(mock_, OnQuit()) + .Times(testing::AtMost(1)) + .WillOnce(QUIT_LOOP(loop_)); + HRESULT hr = mock_.LaunchIEAndNavigate(url); + ASSERT_HRESULT_SUCCEEDED(hr); + if (hr == S_FALSE) + return; + + ASSERT_TRUE(mock_.web_browser2() != NULL); + loop_.RunFor(kChromeFrameLongNavigationTimeoutInSeconds); + } + + const std::wstring GetTestUrl(const wchar_t* relative_path) { + std::wstring path = std::wstring(L"files/no_interference/") + + relative_path; + return UTF8ToWide(server_.Resolve(path.c_str()).spec()); + } + + chrome_frame_test::TimedMsgLoop loop_; + ComStackObjectWithUninitialize< + testing::StrictMock<MockWebBrowserEventSink> > mock_; +}; + +ACTION_P(ExpectIERendererWindowHasFocus, mock) { + mock->ExpectIERendererWindowHasFocus(); +} + +// This tests that a new IE renderer window has focus. +TEST_F(NoInterferenceTest, SimpleFocus) { + const std::wstring kEmptyFileUrl = GetTestUrl(L"empty.html"); + mock_.ExpectNavigationAndSwitch(kEmptyFileUrl); + + EXPECT_CALL(mock_, OnIELoad(testing::StrCaseEq(kEmptyFileUrl))) + .WillOnce(testing::DoAll( + ExpectIERendererWindowHasFocus(&mock_), + VerifyAddressBarUrl(&mock_), + CloseBrowserMock(&mock_))); + + LaunchIEAndNavigate(kEmptyFileUrl); +} + +// This tests that window.open does not get intercepted by Chrome Frame. +TEST_F(NoInterferenceTest, FLAKY_JavascriptWindowOpen) { + const std::wstring kEmptyFileUrl = GetTestUrl(L"empty.html"); + const std::wstring kWindowOpenUrl = GetTestUrl(L"window_open.html"); + ComStackObjectWithUninitialize< + testing::StrictMock<MockWebBrowserEventSink> > new_window_mock; + + mock_.ExpectNavigationAndSwitch(kWindowOpenUrl); + EXPECT_CALL(mock_, OnIELoad(testing::StrCaseEq(kWindowOpenUrl))); + + mock_.ExpectNewWindow(&new_window_mock); + EXPECT_CALL(new_window_mock, OnIELoad(testing::StrCaseEq(kEmptyFileUrl))) + .WillOnce(CloseBrowserMock(&new_window_mock)); + EXPECT_CALL(new_window_mock, OnQuit()) + .WillOnce(CloseBrowserMock(&mock_)); + + LaunchIEAndNavigate(kWindowOpenUrl); +} + +} // namespace
\ No newline at end of file diff --git a/chrome_frame/test/test_mock_with_web_server.cc b/chrome_frame/test/test_mock_with_web_server.cc index 0b7ce1e..f869bb7 100644 --- a/chrome_frame/test/test_mock_with_web_server.cc +++ b/chrome_frame/test/test_mock_with_web_server.cc @@ -14,51 +14,18 @@ #define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING #include "testing/gmock_mutant.h" -using testing::CreateFunctor; using testing::_; +using testing::CreateFunctor; +using chrome_frame_test::CloseIeAtEndOfScope; +using chrome_frame_test::ComStackObjectWithUninitialize; +using chrome_frame_test::kChromeFrameLongNavigationTimeoutInSeconds; using chrome_frame_test::MockWebBrowserEventSink; -const int kChromeFrameLaunchDelay = 5; -const int kChromeFrameLongNavigationTimeoutInSeconds = 10; - const wchar_t kChromeFrameFileUrl[] = L"gcf:file:///C:/"; const wchar_t enter_key[] = { VK_RETURN, 0 }; const wchar_t escape_key[] = { VK_ESCAPE, 0 }; const wchar_t tab_enter_keys[] = { VK_TAB, VK_RETURN, 0 }; -// A convenience class to close all open IE windows at the end -// of a scope. It's more convenient to do it this way than to -// explicitly call chrome_frame_test::CloseAllIEWindows at the -// end of a test since part of the test's cleanup code may be -// in object destructors that would run after CloseAllIEWindows -// would get called. -// Ideally all IE windows should be closed when this happens so -// if the test ran normally, we should not have any windows to -// close at this point. -class CloseIeAtEndOfScope { - public: - CloseIeAtEndOfScope() {} - ~CloseIeAtEndOfScope() { - int closed = chrome_frame_test::CloseAllIEWindows(); - DLOG_IF(ERROR, closed != 0) - << StringPrintf("Closed %i windows forcefully", closed); - } -}; - -// Specialization of CComObjectStackEx that performs object cleanup via -// calling Base::Uninitialize() before we get to CComObjectStackEx' destructor. -// The CComObjectStackEx destructor expects the reference count to be 0 -// or it will throw an assert. To work around that and to avoid having to -// explicitly call Uninitialize() at the end of every test, we override the -// destructor here to perform the cleanup. -template <class Base> -class ComStackObjectWithUninitialize : public CComObjectStackEx<Base> { - public: - virtual ~ComStackObjectWithUninitialize() { - Base::Uninitialize(); - } -}; - namespace chrome_frame_test { ExpectationSet MockWebBrowserEventSink::ExpectNavigationCardinality( @@ -182,10 +149,6 @@ ExpectationSet MockWebBrowserEventSink::ExpectNewWindow( } // namespace chrome_frame_test -ACTION_P(CloseBrowserMock, mock) { - mock->CloseWebBrowser(); -} - ACTION_P3(DelayCloseBrowserMock, loop, delay, mock) { loop->PostDelayedTask(FROM_HERE, NewRunnableMethod(mock, &MockWebBrowserEventSink::CloseWebBrowser), delay); @@ -269,26 +232,10 @@ ACTION_P3(TypeUrlInAddressBar, loop, url, delay) { next_delay); } -void ExpectAddressBarUrl(IWebBrowser2* web_browser2, - const std::wstring& expected_url) { - EXPECT_NE(static_cast<IWebBrowser2*>(NULL), web_browser2); - if (web_browser2) { - ScopedBstr address_bar_url; - EXPECT_EQ(S_OK, web_browser2->get_LocationURL(address_bar_url.Receive())); - EXPECT_EQ(expected_url, std::wstring(address_bar_url)); - } -} - -// To be tacked on to the EXPECT_CALL of OnLoad. This verifies that -// the address bar URL matches with the URL reported by OnLoad -ACTION_P(VerifyAddressBarUrl, mock) { - ExpectAddressBarUrl(mock->web_browser2(), std::wstring(arg0)); -} - ACTION_P(VerifyAddressBarUrlWithGcf, mock) { std::wstring expected_url = L"gcf:"; expected_url += arg0; - ExpectAddressBarUrl(mock->web_browser2(), expected_url); + mock->ExpectAddressBarUrl(expected_url); } TEST(ChromeFrameTest, FullTabModeIE_DisallowedUrls) { @@ -366,7 +313,7 @@ TEST_F(ChromeFrameTestWithWebServer, FullTabModeIE_FocusTest) { EXPECT_CALL(mock, OnLoad(testing::StrCaseEq(kAboutVersion))) .WillOnce(testing::DoAll( testing::InvokeWithoutArgs(CreateFunctor(&mock, - &MockWebBrowserEventSink::ExpectRendererWindowHasfocus)), + &MockWebBrowserEventSink::ExpectRendererWindowHasFocus)), VerifyAddressBarUrlWithGcf(&mock), CloseBrowserMock(&mock))); diff --git a/chrome_frame/test/test_mock_with_web_server.h b/chrome_frame/test/test_mock_with_web_server.h index c14a03f..53fccb1 100644 --- a/chrome_frame/test/test_mock_with_web_server.h +++ b/chrome_frame/test/test_mock_with_web_server.h @@ -86,6 +86,14 @@ class MockWebBrowserEventSink : public chrome_frame_test::WebBrowserEventSink { const std::wstring& url); }; +ACTION_P(CloseBrowserMock, mock) { + mock->CloseWebBrowser(); +} + +ACTION_P(VerifyAddressBarUrl, mock) { + mock->ExpectAddressBarUrl(std::wstring(arg0)); +} + } // namespace chrome_frame_test #endif // CHROME_FRAME_TEST_MOCK_WITH_WEB_SERVER_H_ diff --git a/chrome_frame/test/test_with_web_server.cc b/chrome_frame/test/test_with_web_server.cc index 815ad86..2c95f13 100644 --- a/chrome_frame/test/test_with_web_server.cc +++ b/chrome_frame/test/test_with_web_server.cc @@ -11,11 +11,11 @@ #include "chrome_frame/utils.h" #include "chrome_frame/test/chrome_frame_test_utils.h" +using chrome_frame_test::kChromeFrameLongNavigationTimeoutInSeconds; + const wchar_t kDocRoot[] = L"chrome_frame\\test\\data"; const int kLongWaitTimeout = 60 * 1000; const int kShortWaitTimeout = 25 * 1000; -const int kChromeFrameLaunchDelay = 5; -const int kChromeFrameLongNavigationTimeoutInSeconds = 10; class ChromeFrameTestEnvironment: public testing::Environment { public: |