diff options
-rw-r--r-- | chrome_frame/chrome_frame.gyp | 3 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_test_utils.cc | 516 | ||||
-rw-r--r-- | chrome_frame/test/chrome_frame_test_utils.h | 215 | ||||
-rw-r--r-- | chrome_frame/test/ie_event_sink.cc | 23 | ||||
-rw-r--r-- | chrome_frame/test/ie_event_sink.h | 20 | ||||
-rw-r--r-- | chrome_frame/test/reliability/page_load_test.cc | 162 |
6 files changed, 113 insertions, 826 deletions
diff --git a/chrome_frame/chrome_frame.gyp b/chrome_frame/chrome_frame.gyp index 4b873f5..bcfcbc0 100644 --- a/chrome_frame/chrome_frame.gyp +++ b/chrome_frame/chrome_frame.gyp @@ -472,6 +472,7 @@ '../chrome/chrome.gyp:debugger', '../chrome/chrome.gyp:renderer', '../chrome/chrome.gyp:test_support_common', + '../testing/gmock.gyp:gmock', '../testing/gtest.gyp:gtest', 'base_noicu', 'chrome_frame_ie', @@ -485,6 +486,8 @@ 'test/reliability/reliability_test_suite.h', 'test/chrome_frame_test_utils.cc', 'test/chrome_frame_test_utils.h', + 'test/ie_event_sink.cc', + 'test/ie_event_sink.h', 'test_utils.cc', 'test_utils.h', 'test/simulate_input.cc', diff --git a/chrome_frame/test/chrome_frame_test_utils.cc b/chrome_frame/test/chrome_frame_test_utils.cc index b9ea02a..3a11937 100644 --- a/chrome_frame/test/chrome_frame_test_utils.cc +++ b/chrome_frame/test/chrome_frame_test_utils.cc @@ -7,33 +7,24 @@ #include <atlbase.h> #include <atlwin.h> #include <iepmapi.h> -#include <oleauto.h> #include <sddl.h> -#include <sstream> - #include "base/command_line.h" +#include "base/file_path.h" #include "base/file_version_info.h" -#include "base/file_util.h" -#include "base/message_loop.h" #include "base/path_service.h" -#include "base/platform_thread.h" #include "base/process_util.h" #include "base/registry.h" // to find IE and firefox -#include "base/scoped_bstr_win.h" #include "base/scoped_handle.h" -#include "base/scoped_variant_win.h" +#include "base/scoped_ptr.h" #include "base/string_util.h" #include "base/utf_string_conversions.h" #include "base/win_util.h" #include "chrome/common/chrome_switches.h" #include "chrome/common/chrome_paths.h" #include "chrome/common/chrome_paths_internal.h" -#include "chrome_frame/test/win_event_receiver.h" #include "chrome_frame/utils.h" -#include "testing/gtest/include/gtest/gtest.h" - namespace chrome_frame_test { const int kDefaultWaitForIEToTerminateMs = 10 * 1000; @@ -248,8 +239,20 @@ int CloseAllIEWindows() { if (folder != NULL) { ScopedComPtr<IWebBrowser2> browser; if (SUCCEEDED(browser.QueryFrom(folder))) { - browser->Quit(); - ++ret; + bool is_ie = true; + HWND window = NULL; + // Check the class of the browser window to make sure we only close + // IE windows. + if (browser->get_HWND(reinterpret_cast<SHANDLE_PTR*>(window))) { + wchar_t class_name[MAX_PATH]; + if (::GetClassName(window, class_name, arraysize(class_name))) { + is_ie = _wcsicmp(class_name, L"IEFrame") == 0; + } + } + if (is_ie) { + browser->Quit(); + ++ret; + } } } } @@ -390,465 +393,6 @@ FilePath GetProfilePath(const std::wstring& profile_name) { return profile_path.Append(profile_name); } -_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 - } -}; - -_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow2Info = { - CC_STDCALL, VT_EMPTY, 2, { - VT_DISPATCH | VT_BYREF, - VT_BOOL | VT_BYREF, - } -}; - -_ATL_FUNC_INFO WebBrowserEventSink::kNewWindow3Info = { - CC_STDCALL, VT_EMPTY, 5, { - VT_DISPATCH | VT_BYREF, - VT_BOOL | VT_BYREF, - VT_UINT, - VT_BSTR, - VT_BSTR - } -}; - -_ATL_FUNC_INFO WebBrowserEventSink::kVoidMethodInfo = { - CC_STDCALL, VT_EMPTY, 0, {NULL}}; - -_ATL_FUNC_INFO WebBrowserEventSink::kDocumentCompleteInfo = { - CC_STDCALL, VT_EMPTY, 2, { - VT_DISPATCH, - VT_VARIANT | VT_BYREF - } -}; - -_ATL_FUNC_INFO WebBrowserEventSink::kFileDownloadInfo = { - CC_STDCALL, VT_EMPTY, 2, { - VT_BOOL, - VT_BOOL | VT_BYREF - } -}; - -// WebBrowserEventSink member defines -void WebBrowserEventSink::Attach(IDispatch* browser_disp) { - EXPECT_TRUE(NULL != browser_disp); - if (browser_disp) { - EXPECT_HRESULT_SUCCEEDED(web_browser2_.QueryFrom(browser_disp)); - EXPECT_TRUE(S_OK == DispEventAdvise(web_browser2_, - &DIID_DWebBrowserEvents2)); - } -} - -void WebBrowserEventSink::Uninitialize() { - DisconnectFromChromeFrame(); - if (web_browser2_.get()) { - if (m_dwEventCookie != 0xFEFEFEFE) { - CoDisconnectObject(this, 0); - DispEventUnadvise(web_browser2_); - } - - ScopedHandle process; - // process_id_to_wait_for_ is set when we receive OnQuit. - // So, we should only attempt to wait for the browser if we know that - // the browser is truly quitting and if this instance actually launched - // the browser. - if (process_id_to_wait_for_) { - if (is_main_browser_object_) { - process.Set(OpenProcess(SYNCHRONIZE, FALSE, process_id_to_wait_for_)); - DLOG_IF(ERROR, !process.IsValid()) - << StringPrintf("OpenProcess failed: %i", ::GetLastError()); - } - process_id_to_wait_for_ = 0; - } else { - DLOG_IF(ERROR, is_main_browser_object_) - << "Main browser event object did not have a valid the process id."; - web_browser2_->Quit(); - } - - web_browser2_.Release(); - - if (process) { - DWORD max_wait = kDefaultWaitForIEToTerminateMs; - while (true) { - base::Time start = base::Time::Now(); - HANDLE wait_for = process; - DWORD wait = MsgWaitForMultipleObjects(1, &wait_for, FALSE, max_wait, - QS_ALLINPUT); - if (wait == WAIT_OBJECT_0 + 1) { - MSG msg; - while (PeekMessage(&msg, NULL, 0, 0, TRUE) > 0) { - TranslateMessage(&msg); - DispatchMessage(&msg); - } - } else if (wait == WAIT_OBJECT_0) { - break; - } else { - DCHECK(wait == WAIT_TIMEOUT); - DLOG(ERROR) << "Wait for IE timed out"; - break; - } - - base::TimeDelta elapsed = base::Time::Now() - start; - ULARGE_INTEGER ms; - ms.QuadPart = elapsed.InMilliseconds(); - DCHECK_EQ(ms.HighPart, 0U); - if (ms.LowPart > max_wait) { - DLOG(ERROR) << "Wait for IE timed out (2)"; - break; - } else { - max_wait -= ms.LowPart; - } - } - } - } -} - -STDMETHODIMP WebBrowserEventSink::OnBeforeNavigate2Internal( - IDispatch* dispatch, VARIANT* url, VARIANT* flags, - VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers, - VARIANT_BOOL* cancel) { - DLOG(INFO) << __FUNCTION__ - << StringPrintf("%ls - 0x%08X", url->bstrVal, this); - // Reset any existing reference to chrome frame since this is a new - // navigation. - DisconnectFromChromeFrame(); - OnBeforeNavigate2(dispatch, url, flags, target_frame_name, post_data, - headers, cancel); - return S_OK; -} - -STDMETHODIMP_(void) WebBrowserEventSink::OnNavigateComplete2Internal( - IDispatch* dispatch, VARIANT* url) { - DLOG(INFO) << __FUNCTION__; - ConnectToChromeFrame(); - OnNavigateComplete2(dispatch, url); -} - -STDMETHODIMP_(void) WebBrowserEventSink::OnDocumentCompleteInternal( - IDispatch* dispatch, VARIANT* url) { - DLOG(INFO) << __FUNCTION__; - OnDocumentComplete(dispatch, url); -} - -STDMETHODIMP_(void) WebBrowserEventSink::OnFileDownloadInternal( - VARIANT_BOOL active_doc, VARIANT_BOOL* cancel) { - DLOG(INFO) << __FUNCTION__ << StringPrintf(" 0x%08X ad=%i", this, active_doc); - OnFileDownload(active_doc, cancel); - // Always cancel file downloads in tests. - *cancel = VARIANT_TRUE; -} - -STDMETHODIMP_(void) WebBrowserEventSink::OnNewWindow3Internal( - IDispatch** dispatch, VARIANT_BOOL* cancel, DWORD flags, BSTR url_context, - BSTR url) { - DLOG(INFO) << __FUNCTION__; - if (!dispatch) { - NOTREACHED() << "Invalid argument - dispatch"; - return; - } - - // Call the OnNewWindow3 with original args - OnNewWindow3(dispatch, cancel, flags, url_context, url); - - // Note that |dispatch| is an [in/out] argument. IE is asking listeners if - // they want to use a IWebBrowser2 of their choice for the new window. - // Since we need to listen on events on the new browser, we create one - // if needed. - if (!*dispatch) { - ScopedComPtr<IDispatch> new_browser; - HRESULT hr = new_browser.CreateInstance(CLSID_InternetExplorer, NULL, - CLSCTX_LOCAL_SERVER); - DCHECK(SUCCEEDED(hr) && new_browser); - *dispatch = new_browser.Detach(); - } - - if (*dispatch) - OnNewBrowserWindow(*dispatch, url); -} - -HRESULT WebBrowserEventSink::OnLoadInternal(const VARIANT* param) { - DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal; - if (chrome_frame_) { - OnLoad(param->bstrVal); - } else { - DLOG(WARNING) << "Invalid chrome frame pointer"; - } - return S_OK; -} - -HRESULT WebBrowserEventSink::OnLoadErrorInternal(const VARIANT* param) { - DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal; - if (chrome_frame_) { - OnLoadError(param->bstrVal); - } else { - DLOG(WARNING) << "Invalid chrome frame pointer"; - } - return S_OK; -} - -HRESULT WebBrowserEventSink::OnMessageInternal(const VARIANT* param) { - DLOG(INFO) << __FUNCTION__ << " " << param; - if (!chrome_frame_.get()) { - DLOG(WARNING) << "Invalid chrome frame pointer"; - return S_OK; - } - - ScopedVariant data, origin, source; - if (param && (V_VT(param) == VT_DISPATCH)) { - wchar_t* properties[] = { L"data", L"origin", L"source" }; - const int prop_count = arraysize(properties); - DISPID ids[prop_count] = {0}; - - HRESULT hr = param->pdispVal->GetIDsOfNames(IID_NULL, properties, - prop_count, LOCALE_SYSTEM_DEFAULT, ids); - if (SUCCEEDED(hr)) { - DISPPARAMS params = { 0 }; - EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[0], IID_NULL, - LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, - data.Receive(), NULL, NULL)); - EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[1], IID_NULL, - LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, - origin.Receive(), NULL, NULL)); - EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[2], IID_NULL, - LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, ¶ms, - source.Receive(), NULL, NULL)); - } - } - - OnMessage(V_BSTR(&data), V_BSTR(&origin), V_BSTR(&source)); - return S_OK; -} - -HRESULT WebBrowserEventSink::LaunchIEAndNavigate( - const std::wstring& navigate_url) { - is_main_browser_object_ = true; - HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive()); - EXPECT_EQ(S_OK, hr); - if (hr == S_OK) { - web_browser2_->put_Visible(VARIANT_TRUE); - hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2); - EXPECT_TRUE(hr == S_OK); - hr = Navigate(navigate_url); - } - - DLOG_IF(WARNING, FAILED(hr)) << "Failed to launch IE. Error:" << hr; - return hr; -} - -HRESULT WebBrowserEventSink::Navigate(const std::wstring& navigate_url) { - VARIANT empty = ScopedVariant::kEmptyVariant; - ScopedVariant url; - url.Set(navigate_url.c_str()); - - HRESULT hr = S_OK; - hr = web_browser2_->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty); - EXPECT_TRUE(hr == S_OK); - return hr; -} - -void WebBrowserEventSink::SetFocusToChrome() { - simulate_input::SetKeyboardFocusToWindow(GetRendererWindow()); -} - -void WebBrowserEventSink::SendKeys(const wchar_t* input_string) { - SetFocusToChrome(); - simulate_input::SendStringW(input_string); -} - -void WebBrowserEventSink::SendMouseClick(int x, int y, - simulate_input::MouseButton button) { - 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()) - return; - ScopedComPtr<IShellBrowser> shell_browser; - DoQueryService(SID_STopLevelBrowser, web_browser2_, - shell_browser.Receive()); - - if (shell_browser) { - ScopedComPtr<IShellView> shell_view; - shell_browser->QueryActiveShellView(shell_view.Receive()); - if (shell_view) { - shell_view->GetItemObject(SVGIO_BACKGROUND, __uuidof(IChromeFrame), - reinterpret_cast<void**>(chrome_frame_.Receive())); - } - - if (chrome_frame_) { - ScopedVariant onmessage(onmessage_.ToDispatch()); - ScopedVariant onloaderror(onloaderror_.ToDispatch()); - ScopedVariant onload(onload_.ToDispatch()); - EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onmessage(onmessage)); - EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onloaderror(onloaderror)); - EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onload(onload)); - } - } -} - -void WebBrowserEventSink::DisconnectFromChromeFrame() { - if (chrome_frame_) { - // Use a local ref counted copy of the IChromeFrame interface as the - // outgoing calls could cause the interface to be deleted due to a message - // pump running in the context of the outgoing call. - ScopedComPtr<IChromeFrame> chrome_frame(chrome_frame_); - chrome_frame_.Release(); - ScopedVariant dummy(static_cast<IDispatch*>(NULL)); - chrome_frame->put_onmessage(dummy); - chrome_frame->put_onload(dummy); - chrome_frame->put_onloaderror(dummy); - } -} - -HWND WebBrowserEventSink::GetRendererWindow() { - DCHECK(chrome_frame_); - HWND renderer_window = NULL; - ScopedComPtr<IOleWindow> ole_window; - ole_window.QueryFrom(chrome_frame_); - EXPECT_TRUE(ole_window.get()); - - if (ole_window) { - HWND activex_window = NULL; - ole_window->GetWindow(&activex_window); - EXPECT_TRUE(IsWindow(activex_window)); - - // chrome tab window is the first (and the only) child of activex - HWND chrome_tab_window = GetWindow(activex_window, GW_CHILD); - EXPECT_TRUE(IsWindow(chrome_tab_window)); - renderer_window = GetWindow(chrome_tab_window, GW_CHILD); - } - - EXPECT_TRUE(IsWindow(renderer_window)); - 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_); - web_browser2_ = web_browser2; - web_browser2_->put_Visible(VARIANT_TRUE); - HRESULT hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2); - return hr; -} - -HRESULT WebBrowserEventSink::CloseWebBrowser() { - DCHECK_EQ(process_id_to_wait_for_, 0u); - if (!web_browser2_) - return E_FAIL; - - DisconnectFromChromeFrame(); - web_browser2_->Quit(); - return S_OK; -} - -void WebBrowserEventSink::ExpectRendererWindowHasFocus() { - HWND renderer_window = GetRendererWindow(); - EXPECT_TRUE(IsWindow(renderer_window)); - - for (HWND first_child = renderer_window; - IsWindow(first_child); first_child = GetWindow(first_child, GW_CHILD)) { - renderer_window = first_child; - } - - wchar_t class_name[MAX_PATH] = {0}; - GetClassName(renderer_window, class_name, arraysize(class_name)); - EXPECT_EQ(0, _wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND")); - - 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::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) { - ScopedComPtr<IOleCommandTarget> shell_browser_cmd_target; - DoQueryService(SID_STopLevelBrowser, web_browser2_, - shell_browser_cmd_target.Receive()); - ASSERT_TRUE(NULL != shell_browser_cmd_target); - EXPECT_HRESULT_SUCCEEDED(shell_browser_cmd_target->Exec(cmd_group_guid, - command_id, cmd_exec_opt, in_args, out_args)); -} - std::wstring GetExeVersion(const std::wstring& exe_path) { scoped_ptr<FileVersionInfo> ie_version_info( FileVersionInfo::CreateFileVersionInfo(FilePath(exe_path))); @@ -919,15 +463,17 @@ bool AddCFMetaTag(std::string* html_data) { NOTREACHED(); return false; } - size_t head = html_data->find("<head>"); + std::string lower = StringToLowerASCII(*html_data); + size_t head = lower.find("<head>"); if (head == std::string::npos) { // Add missing head section. - size_t html = html_data->find("<html>"); - EXPECT_NE(std::string::npos, html) << "Meta tag will not be injected " - << "because the html tag could not be found"; + size_t html = lower.find("<html>"); if (html != std::string::npos) { - html_data->insert(html + strlen("<html>"), "<head></head>"); - head = html_data->find("<head>"); + head = html + strlen("<html>"); + html_data->insert(head, "<head></head>"); + } else { + DLOG(ERROR) << "Meta tag will not be injected " + << "because the html tag could not be found"; } } if (head != std::string::npos) { @@ -938,20 +484,6 @@ bool AddCFMetaTag(std::string* html_data) { return head != std::string::npos; } -void DelaySendExtendedKeysEnter(TimedMsgLoop* loop, int delay, char c, - int repeat, simulate_input::Modifier mod) { - const unsigned int kInterval = 25; - unsigned int next_delay = delay; - for (int i = 0; i < repeat; i++) { - loop->PostDelayedTask(FROM_HERE, NewRunnableFunction( - simulate_input::SendExtendedKey, c, mod), next_delay); - next_delay += kInterval; - } - - loop->PostDelayedTask(FROM_HERE, NewRunnableFunction( - simulate_input::SendCharA, VK_RETURN, simulate_input::NONE), next_delay); -} - CloseIeAtEndOfScope::~CloseIeAtEndOfScope() { int closed = CloseAllIEWindows(); DLOG_IF(ERROR, closed != 0) << "Closed " << closed << " windows forcefully"; diff --git a/chrome_frame/test/chrome_frame_test_utils.h b/chrome_frame/test/chrome_frame_test_utils.h index 199be72..6bd787f 100644 --- a/chrome_frame/test/chrome_frame_test_utils.h +++ b/chrome_frame/test/chrome_frame_test_utils.h @@ -29,6 +29,10 @@ // Include without path to make GYP build see it. #include "chrome_tab.h" // NOLINT +// Needed for CreateFunctor. +#define GMOCK_MUTANT_INCLUDE_LATE_OBJECT_BINDING +#include "testing/gmock_mutant.h" + namespace chrome_frame_test { int CloseVisibleWindowsOnAllThreads(HANDLE process); @@ -120,212 +124,6 @@ HRESULT LaunchIEAsComServer(IWebBrowser2** web_browser); FilePath GetProfilePath(const std::wstring& suffix); -#ifndef DISPID_NEWPROCESS -#define DISPID_NEWPROCESS 284 -#endif // DISPID_NEWPROCESS - -// 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 -// 4. DISPID_NEWWINDOW3 -// 5. DISPID_DOCUMENTCOMPLETE -// Other events can be subscribed to on an if needed basis. -class WebBrowserEventSink - : public CComObjectRootEx<CComMultiThreadModel>, - public IDispEventSimpleImpl<0, WebBrowserEventSink, - &DIID_DWebBrowserEvents2>, - public IUnknown { - public: - typedef IDispEventSimpleImpl<0, WebBrowserEventSink, - &DIID_DWebBrowserEvents2> DispEventsImpl; - WebBrowserEventSink() - : ALLOW_THIS_IN_INITIALIZER_LIST( - onmessage_(this, &WebBrowserEventSink::OnMessageInternal)), - ALLOW_THIS_IN_INITIALIZER_LIST( - onloaderror_(this, &WebBrowserEventSink::OnLoadErrorInternal)), - ALLOW_THIS_IN_INITIALIZER_LIST( - onload_(this, &WebBrowserEventSink::OnLoadInternal)), - process_id_to_wait_for_(0), - is_main_browser_object_(false) { - } - - ~WebBrowserEventSink() { - Uninitialize(); - } - -BEGIN_COM_MAP(WebBrowserEventSink) - COM_INTERFACE_ENTRY(IUnknown) -END_COM_MAP() - -BEGIN_SINK_MAP(WebBrowserEventSink) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2, - OnBeforeNavigate2Internal, &kBeforeNavigate2Info) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN, - OnDownloadBegin, &kVoidMethodInfo) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2, - OnNavigateComplete2Internal, &kNavigateComplete2Info) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATEERROR, - OnNavigateError, &kNavigateErrorInfo) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW2, - OnNewWindow2, &kNewWindow2Info) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW3, - OnNewWindow3Internal, &kNewWindow3Info) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE, - OnDocumentCompleteInternal, &kDocumentCompleteInfo) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_FILEDOWNLOAD, - OnFileDownloadInternal, &kFileDownloadInfo) - SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_ONQUIT, - OnQuitInternal, &kVoidMethodInfo) -END_SINK_MAP() - - void Attach(IDispatch* browser_disp); - void Uninitialize(); - - // Helper function to launch IE and navigate to a URL. - // Returns S_OK on success, S_FALSE if the test was not run, other - // errors on failure. - HRESULT LaunchIEAndNavigate(const std::wstring& navigate_url); - - virtual HRESULT Navigate(const std::wstring& navigate_url); - - // Set input focus to chrome frame window. - void SetFocusToChrome(); - - // Send keyboard input to the renderer window hosted in chrome using - // SendInput API. - void SendKeys(const wchar_t* input_string); - - // Send mouse click to the renderer window hosted in chrome using - // 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); - - // Overridable methods for the mock. - STDMETHOD_(void, OnNavigateError)(IDispatch* dispatch, VARIANT* url, - VARIANT* frame_name, VARIANT* status_code, - VARIANT* cancel) { - DLOG(INFO) << __FUNCTION__; - } - - STDMETHOD_(void, OnBeforeNavigate2)(IDispatch* dispatch, VARIANT* url, - VARIANT* flags, VARIANT* target_frame_name, - VARIANT* post_data, VARIANT* headers, - VARIANT_BOOL* cancel) {} - STDMETHOD_(void, OnDownloadBegin)() {} - STDMETHOD_(void, OnNavigateComplete2)(IDispatch* dispatch, VARIANT* url) {} - STDMETHOD_(void, OnNewWindow2)(IDispatch** dispatch, VARIANT_BOOL* cancel) {} - STDMETHOD_(void, OnNewWindow3)(IDispatch** dispatch, VARIANT_BOOL* cancel, - DWORD flags, BSTR url_context, BSTR url) {} - STDMETHOD_(void, OnDocumentComplete)(IDispatch* dispatch, - VARIANT* url) {} - - STDMETHOD_(void, OnFileDownloadInternal)(VARIANT_BOOL active_doc, - VARIANT_BOOL* cancel); - STDMETHOD_(void, OnFileDownload)(VARIANT_BOOL active_doc, - VARIANT_BOOL* cancel) {} - STDMETHOD_(void, OnQuit)() {} - STDMETHOD_(void, OnQuitInternal)() { - DLOG(INFO) << __FUNCTION__; - // Grab the process id here in case it will be too late to do it - // in Uninitialize. - HWND hwnd = NULL; - web_browser2_->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&hwnd)); - if (::IsWindow(hwnd)) - ::GetWindowThreadProcessId(hwnd, &process_id_to_wait_for_); - - OnQuit(); - CoDisconnectObject(this, 0); - DispEventUnadvise(web_browser2_); - } -#ifdef _DEBUG - STDMETHOD(Invoke)(DISPID dispid, REFIID riid, - LCID lcid, WORD flags, DISPPARAMS* params, VARIANT* result, - EXCEPINFO* except_info, UINT* arg_error) { - DLOG(INFO) << __FUNCTION__ << L" disp id :" << dispid; - return DispEventsImpl::Invoke(dispid, riid, lcid, flags, params, result, - except_info, arg_error); - } -#endif // _DEBUG - - // Chrome frame callbacks - virtual void OnLoad(const wchar_t* url) {} - virtual void OnLoadError(const wchar_t* url) {} - virtual void OnMessage(const wchar_t* message, const wchar_t* origin, - const wchar_t* source) {} - virtual void OnNewBrowserWindow(IDispatch* new_window, const wchar_t* url) {} - - // Window watchdog override - virtual void OnWindowDetected(HWND hwnd, const std::string& caption) {} - - IWebBrowser2* web_browser2() { - return web_browser2_.get(); - } - - HRESULT SetWebBrowser(IWebBrowser2* web_browser2); - 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). - HRESULT CloseWebBrowser(); - - protected: - STDMETHOD(OnBeforeNavigate2Internal)(IDispatch* dispatch, VARIANT* url, - VARIANT* flags, - VARIANT* target_frame_name, - VARIANT* post_data, VARIANT* headers, - VARIANT_BOOL* cancel); - STDMETHOD_(void, OnNavigateComplete2Internal)(IDispatch* dispatch, - VARIANT* url); - STDMETHOD_(void, OnDocumentCompleteInternal)(IDispatch* dispatch, - VARIANT* url); - STDMETHOD_(void, OnNewWindow3Internal)(IDispatch** dispatch, - VARIANT_BOOL* cancel, DWORD flags, - BSTR url_context, BSTR url); - - // IChromeFrame callbacks - HRESULT OnLoadInternal(const VARIANT* param); - HRESULT OnLoadErrorInternal(const VARIANT* param); - HRESULT OnMessageInternal(const VARIANT* param); - - void ConnectToChromeFrame(); - void DisconnectFromChromeFrame(); - HWND GetRendererWindow(); - HWND GetIERendererWindow(); - - public: - ScopedComPtr<IWebBrowser2> web_browser2_; - ScopedComPtr<IChromeFrame> chrome_frame_; - DispCallback<WebBrowserEventSink> onmessage_; - DispCallback<WebBrowserEventSink> onloaderror_; - DispCallback<WebBrowserEventSink> onload_; - base::ProcessId process_id_to_wait_for_; - - // Set to true if this instance was used to launch the browser process. - // For instances used to connect to popup windows etc, this will be - // set to false. - bool is_main_browser_object_; - - protected: - static _ATL_FUNC_INFO kBeforeNavigate2Info; - static _ATL_FUNC_INFO kNavigateComplete2Info; - static _ATL_FUNC_INFO kNavigateErrorInfo; - static _ATL_FUNC_INFO kNewWindow2Info; - static _ATL_FUNC_INFO kNewWindow3Info; - static _ATL_FUNC_INFO kVoidMethodInfo; - static _ATL_FUNC_INFO kDocumentCompleteInfo; - static _ATL_FUNC_INFO kFileDownloadInfo; -}; - // Returns the path of the exe passed in. std::wstring GetExecutableAppPath(const std::wstring& file); @@ -350,11 +148,6 @@ std::wstring GetPathAndQueryFromUrl(const std::wstring& url); // Adds the CF meta tag to the html page. Returns true if successful. bool AddCFMetaTag(std::string* html_data); -// Posts a delayed task to send an extended keystroke |repeat| times with an -// optional modifier via SendInput. Following this, the enter key is sent. -void DelaySendExtendedKeysEnter(TimedMsgLoop* loop, int delay, char c, - int repeat, simulate_input::Modifier mod); - // 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 diff --git a/chrome_frame/test/ie_event_sink.cc b/chrome_frame/test/ie_event_sink.cc index 7da31dc..0e5ddcb 100644 --- a/chrome_frame/test/ie_event_sink.cc +++ b/chrome_frame/test/ie_event_sink.cc @@ -100,10 +100,19 @@ void IEEventSink::Attach(IDispatch* browser_disp) { EXPECT_TRUE(NULL != browser_disp); if (browser_disp) { EXPECT_HRESULT_SUCCEEDED(web_browser2_.QueryFrom(browser_disp)); - EXPECT_TRUE(S_OK == DispEventAdvise(web_browser2_, - &DIID_DWebBrowserEvents2)); + EXPECT_HRESULT_SUCCEEDED(Attach(web_browser2_.get())); + } +} + +HRESULT IEEventSink::Attach(IWebBrowser2* browser) { + DCHECK(browser); + HRESULT result; + if (browser) { + web_browser2_ = browser; FindIEProcessId(); + result = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2); } + return result; } void IEEventSink::Uninitialize() { @@ -339,16 +348,11 @@ HRESULT IEEventSink::LaunchIEAndNavigate( const std::wstring& navigate_url, IEEventListener* listener) { listener_ = listener; HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive()); - EXPECT_EQ(S_OK, hr); - if (hr == S_OK) { - FindIEProcessId(); + if (SUCCEEDED(hr)) { web_browser2_->put_Visible(VARIANT_TRUE); - hr = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2); - EXPECT_TRUE(hr == S_OK); + Attach(web_browser2_); hr = Navigate(navigate_url); } - - DLOG_IF(WARNING, FAILED(hr)) << "Failed to launch IE. Error:" << hr; return hr; } @@ -425,6 +429,7 @@ void IEEventSink::FindIEProcessId() { EXPECT_TRUE(::IsWindow(hwnd)); if (::IsWindow(hwnd)) ::GetWindowThreadProcessId(hwnd, &ie_process_id_); + EXPECT_NE(static_cast<DWORD>(0), ie_process_id_); } // Event callbacks diff --git a/chrome_frame/test/ie_event_sink.h b/chrome_frame/test/ie_event_sink.h index 189d98e..fd21317 100644 --- a/chrome_frame/test/ie_event_sink.h +++ b/chrome_frame/test/ie_event_sink.h @@ -67,8 +67,6 @@ class PropertyNotifySinkListener { // This class sets up event sinks to the IWebBrowser interface. It forwards // all events to its listener. -// TODO(kkania): Delete WebBrowserEventSink and use this class instead for -// the reliability tests. class IEEventSink : public CComObjectRootEx<CComSingleThreadModel>, public IDispEventSimpleImpl<0, IEEventSink, @@ -85,10 +83,21 @@ class IEEventSink IEEventSink(); ~IEEventSink(); + // Launches IE, sets up the sink to forward events to the listener, and + // navigates to the given page. + HRESULT LaunchIEAndNavigate(const std::wstring& navigate_url, + IEEventListener* listener); + + // Navigate to the given url. + HRESULT Navigate(const std::wstring& navigate_url); + // Listen to events from this |browser_disp|, which should be queryable for // IWebBrowser2. void Attach(IDispatch* browser_disp); + // Listen to events from the given browser. + HRESULT Attach(IWebBrowser2* browser); + // Stop listening to the associated web browser and possibly wait for it to // close, if this browser has its own process. void Uninitialize(); @@ -123,13 +132,6 @@ class IEEventSink // In that case, the returned handle will be NULL. HWND GetRendererWindowSafe(); - // Launch IE, use the given listener, and navigate to the given url. - HRESULT LaunchIEAndNavigate(const std::wstring& navigate_url, - IEEventListener* listener); - - // Navigate to the given url. - HRESULT Navigate(const std::wstring& navigate_url); - // Returns whether CF is rendering the current page. bool IsCFRendering(); diff --git a/chrome_frame/test/reliability/page_load_test.cc b/chrome_frame/test/reliability/page_load_test.cc index f725746..19f8f76 100644 --- a/chrome_frame/test/reliability/page_load_test.cc +++ b/chrome_frame/test/reliability/page_load_test.cc @@ -12,8 +12,6 @@ // Optional Switches: // --iterations=num: goes through the list of URLs constructed in usage 2 or 3 // num times. -// --continuousload: continuously visits the list of URLs without restarting -// browser for each page load. // --memoryusage: prints out memory usage when visiting each page. // --logfile=filepath: saves the visit log to the specified path. // --timeout=seconds: time out as specified in seconds during each @@ -47,6 +45,7 @@ #include "chrome/common/logging_chrome.h" #include "chrome/common/pref_names.h" #include "chrome_frame/test/chrome_frame_test_utils.h" +#include "chrome_frame/test/ie_event_sink.h" #include "chrome/test/automation/automation_messages.h" #include "chrome/test/automation/automation_proxy.h" #include "chrome/test/automation/browser_proxy.h" @@ -56,7 +55,11 @@ #include "chrome/test/reliability/page_load_test.h" #include "chrome_frame/utils.h" #include "net/base/net_util.h" +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +using testing::StrCaseEq; +using testing::StrCaseNe; namespace { @@ -85,8 +88,6 @@ int32 g_start_index = 1; int32 g_end_index = kint32max; int32 g_iterations = 1; bool g_memory_usage = false; -bool g_continuous_load = false; -bool g_browser_existing = false; bool g_page_down = true; bool g_clear_profile = true; std::string g_end_url; @@ -100,86 +101,24 @@ bool g_stand_alone = false; const int kUrlNavigationTimeoutSeconds = 20; int g_timeout_seconds = kUrlNavigationTimeoutSeconds; -// Overrides a number of IWebBrowser2 event sink handlers as provided by the -// base chrome_frame_test::WebBrowserEventSink class and provides functionality -// for reliability testing. -class WebBrowserEventSinkImpl : public chrome_frame_test::WebBrowserEventSink { +// Mocks document complete and load events. +class MockLoadListener : public chrome_frame_test::IEEventListener { public: - typedef chrome_frame_test::WebBrowserEventSink Base; - - WebBrowserEventSinkImpl() - : navigation_started_(false), - navigation_completed_(false), - message_loop_(NULL), - is_chrome_frame_navigation_(false) {} - - ~WebBrowserEventSinkImpl() {} - - STDMETHOD_(void, OnDocumentComplete)(IDispatch* dispatch, VARIANT* url) { - // Note that we can't compare url directly to this->url_ since this check - // then fails for redirects, e.g. www.google.com -> www.google.ca, since we - // only get a single OnDocumentComplete for the final destination. - // Instead just discard interstitial "about:blank" notifications. - if (url->bstrVal && _wcsicmp(url->bstrVal, L"about:blank") != 0) { - navigation_started_ = false; - // If this is a chrome frame navigation then the OnDocumentComplete event - // does not indicate that we actually finished navigation. For that we - // have to wait for the OnLoad notification from ChromeFrame to arrive. - if (!is_chrome_frame_navigation_) { - navigation_completed_ = true; - DCHECK(message_loop_); - message_loop_->Quit(); - } - } - } - - virtual void OnLoad(const wchar_t* url) { - navigation_completed_ = true; - DCHECK(message_loop_); - message_loop_->Quit(); - } - - virtual HRESULT Navigate(const std::wstring& navigate_url) { - if (StartsWith(navigate_url, kChromeProtocolPrefix, true)) { - is_chrome_frame_navigation_ = true; - url_ = navigate_url.substr(wcslen(kChromeProtocolPrefix)); - } else { - url_ = navigate_url; - } - navigation_started_ = true; - navigation_completed_ = false; - - return Base::Navigate(navigate_url); - } - - bool navigation_started() const { - return navigation_started_; - } - - bool navigation_completed() const { - return navigation_completed_; - } - - const std::wstring& url() const { - return url_; - } - - bool is_chrome_frame_navigation() const { - return is_chrome_frame_navigation_; - } - - void set_message_loop(chrome_frame_test::TimedMsgLoop* message_loop) { - message_loop_ = message_loop; - } + MOCK_METHOD1(OnDocumentComplete, void (const wchar_t* url)); // NOLINT + MOCK_METHOD1(OnLoad, void (const wchar_t* url)); // NOLINT + MOCK_METHOD0(OnQuit, void ()); // NOLINT private: - bool navigation_started_; - bool navigation_completed_; - std::wstring url_; - chrome_frame_test::TimedMsgLoop* message_loop_; - bool is_chrome_frame_navigation_; + virtual void OnDocumentComplete(IDispatch* dispatch, VARIANT* url) { + if (url->bstrVal) + OnDocumentComplete(url->bstrVal); + } }; +ACTION_P(QuitIE, event_sink) { + EXPECT_HRESULT_SUCCEEDED(event_sink->CloseWebBrowser()); +} + class PageLoadTest : public testing::Test { public: enum NavigationResult { @@ -239,30 +178,47 @@ class PageLoadTest : public testing::Test { chrome_frame_test::TimedMsgLoop message_loop; - CComObjectStack<WebBrowserEventSinkImpl> browser_event_sink; - browser_event_sink.set_message_loop(&message_loop); - - if (!g_continuous_load && !g_browser_existing) { - ScopedComPtr<IWebBrowser2> web_browser2; - hr = chrome_frame_test::LaunchIEAsComServer(web_browser2.Receive()); - EXPECT_HRESULT_SUCCEEDED(hr); - EXPECT_TRUE(web_browser2.get() != NULL); - - EXPECT_HRESULT_SUCCEEDED(browser_event_sink.SetWebBrowser(web_browser2)); - g_browser_existing = true; - } + // Launch IE. + ScopedComPtr<IWebBrowser2> web_browser2; + hr = chrome_frame_test::LaunchIEAsComServer(web_browser2.Receive()); + EXPECT_HRESULT_SUCCEEDED(hr); + EXPECT_TRUE(web_browser2.get() != NULL); + web_browser2->put_Visible(VARIANT_TRUE); // Log Browser Launched time. time_now = base::Time::Now(); test_log << "browser_launched_seconds="; test_log << (time_now.ToDoubleT() - time_start) << std::endl; - // This is essentially what NavigateToURL does except we don't fire - // assertion when page loading fails. We log the result instead. - hr = browser_event_sink.Navigate(UTF8ToWide(url.spec())); + bool is_chrome_frame_navigation = + StartsWith(UTF8ToWide(url.spec()), kChromeProtocolPrefix, true); + + CComObjectStack<chrome_frame_test::IEEventSink> ie_event_sink; + MockLoadListener load_listener; + // Disregard any interstitial about:blank loads. + EXPECT_CALL(load_listener, OnDocumentComplete(StrCaseEq(L"about:blank"))) + .Times(testing::AnyNumber()); + + // Note that we can't compare the loaded url directly with the given url + // because the page may have redirected us to a different page, e.g. + // www.google.com -> www.google.ca. + if (is_chrome_frame_navigation) { + EXPECT_CALL(load_listener, OnDocumentComplete(testing::_)); + EXPECT_CALL(load_listener, OnLoad(testing::_)) + .WillOnce(QuitIE(&ie_event_sink)); + } else { + EXPECT_CALL(load_listener, OnDocumentComplete(StrCaseNe(L"about:blank"))) + .WillOnce(QuitIE(&ie_event_sink)); + } + EXPECT_CALL(load_listener, OnQuit()).WillOnce(QUIT_LOOP(message_loop)); + + // Attach the sink and navigate. + ie_event_sink.set_listener(&load_listener); + ie_event_sink.Attach(web_browser2); + hr = ie_event_sink.Navigate(UTF8ToWide(url.spec())); if (SUCCEEDED(hr)) { message_loop.RunFor(g_timeout_seconds); - if (browser_event_sink.navigation_completed()) + if (!message_loop.WasTimedOut()) metrics.result = NAVIGATION_SUCCESS; } @@ -271,11 +227,10 @@ class PageLoadTest : public testing::Test { test_log << "navigate_complete_seconds="; test_log << (time_now.ToDoubleT() - time_start) << std::endl; - if (!g_continuous_load) { - browser_event_sink.Uninitialize(); - chrome_frame_test::CloseAllIEWindows(); - g_browser_existing = false; - } + // Close IE. + ie_event_sink.set_listener(NULL); + ie_event_sink.Uninitialize(); + chrome_frame_test::CloseAllIEWindows(); // Log end of test time. time_now = base::Time::Now(); @@ -302,7 +257,7 @@ class PageLoadTest : public testing::Test { } // Get stability metrics recorded by Chrome itself. - if (browser_event_sink.is_chrome_frame_navigation()) { + if (is_chrome_frame_navigation) { GetStabilityMetrics(&metrics); } @@ -319,7 +274,7 @@ class PageLoadTest : public testing::Test { // Close test log. test_log.close(); - if (log_file.is_open() && g_save_debug_log && !g_continuous_load) + if (log_file.is_open() && g_save_debug_log) SaveDebugLogs(log_file); // Log revision information for Chrome build under test. @@ -595,9 +550,6 @@ void SetPageRange(const CommandLine& parsed_command_line) { if (parsed_command_line.HasSwitch(kMemoryUsageSwitch)) g_memory_usage = true; - if (parsed_command_line.HasSwitch(kContinuousLoadSwitch)) - g_continuous_load = true; - if (parsed_command_line.HasSwitch(kLogFileSwitch)) g_log_file_path = parsed_command_line.GetSwitchValuePath(kLogFileSwitch); |