summaryrefslogtreecommitdiffstats
path: root/chrome_frame/test/ie_event_sink.cc
diff options
context:
space:
mode:
authorkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-12 22:07:03 +0000
committerkkania@chromium.org <kkania@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-07-12 22:07:03 +0000
commit6448b3abcfa035eaf65b7c0c15bb728513f228fe (patch)
tree3d8ee0bf2f528e93616a1d7721bc721ef6726189 /chrome_frame/test/ie_event_sink.cc
parentbb2eb35a70fcbc47be253bf50c8648c65271a19e (diff)
downloadchromium_src-6448b3abcfa035eaf65b7c0c15bb728513f228fe.zip
chromium_src-6448b3abcfa035eaf65b7c0c15bb728513f228fe.tar.gz
chromium_src-6448b3abcfa035eaf65b7c0c15bb728513f228fe.tar.bz2
[chrome_frame] Refactor/merge IE no interference tests with other mock event sink tests. Have the test mock contain, not inherit the IE event sink. Use the new test server to verify the right requests are being sent.
BUG=none TEST=none Review URL: http://codereview.chromium.org/2822016 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52137 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome_frame/test/ie_event_sink.cc')
-rw-r--r--chrome_frame/test/ie_event_sink.cc545
1 files changed, 545 insertions, 0 deletions
diff --git a/chrome_frame/test/ie_event_sink.cc b/chrome_frame/test/ie_event_sink.cc
new file mode 100644
index 0000000..2f7d32c
--- /dev/null
+++ b/chrome_frame/test/ie_event_sink.cc
@@ -0,0 +1,545 @@
+// 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/ie_event_sink.h"
+
+#include "base/scoped_bstr_win.h"
+#include "base/scoped_handle.h"
+#include "base/utf_string_conversions.h"
+#include "base/win_util.h"
+#include "chrome_frame/test/chrome_frame_test_utils.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+namespace chrome_frame_test {
+
+const int kDefaultWaitForIEToTerminateMs = 10 * 1000;
+
+_ATL_FUNC_INFO IEEventSink::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 IEEventSink::kNavigateComplete2Info = {
+ CC_STDCALL, VT_EMPTY, 2, {
+ VT_DISPATCH,
+ VT_VARIANT | VT_BYREF
+ }
+};
+
+_ATL_FUNC_INFO IEEventSink::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 IEEventSink::kNewWindow2Info = {
+ CC_STDCALL, VT_EMPTY, 2, {
+ VT_DISPATCH | VT_BYREF,
+ VT_BOOL | VT_BYREF,
+ }
+};
+
+_ATL_FUNC_INFO IEEventSink::kNewWindow3Info = {
+ CC_STDCALL, VT_EMPTY, 5, {
+ VT_DISPATCH | VT_BYREF,
+ VT_BOOL | VT_BYREF,
+ VT_UINT,
+ VT_BSTR,
+ VT_BSTR
+ }
+};
+
+_ATL_FUNC_INFO IEEventSink::kVoidMethodInfo = {
+ CC_STDCALL, VT_EMPTY, 0, {NULL}};
+
+_ATL_FUNC_INFO IEEventSink::kDocumentCompleteInfo = {
+ CC_STDCALL, VT_EMPTY, 2, {
+ VT_DISPATCH,
+ VT_VARIANT | VT_BYREF
+ }
+};
+
+_ATL_FUNC_INFO IEEventSink::kFileDownloadInfo = {
+ CC_STDCALL, VT_EMPTY, 2, {
+ VT_BOOL,
+ VT_BOOL | VT_BYREF
+ }
+};
+
+IEEventSink::IEEventSink()
+ : ALLOW_THIS_IN_INITIALIZER_LIST(
+ onmessage_(this, &IEEventSink::OnMessage)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ onloaderror_(this, &IEEventSink::OnLoadError)),
+ ALLOW_THIS_IN_INITIALIZER_LIST(
+ onload_(this, &IEEventSink::OnLoad)),
+ listener_(NULL),
+ ie_process_id_(0),
+ did_receive_on_quit_(false) {
+}
+
+IEEventSink::~IEEventSink() {
+ Uninitialize();
+}
+
+// IEEventSink member defines
+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));
+ FindIEProcessId();
+ }
+}
+
+void IEEventSink::Uninitialize() {
+ DisconnectFromChromeFrame();
+ if (web_browser2_.get()) {
+ if (m_dwEventCookie != 0xFEFEFEFE) {
+ DispEventUnadvise(web_browser2_);
+ CoDisconnectObject(this, 0);
+ }
+
+ if (!did_receive_on_quit_) {
+ // Log the browser window url for debugging purposes.
+ ScopedBstr browser_url;
+ web_browser2_->get_LocationURL(browser_url.Receive());
+ std::wstring browser_url_wstring;
+ browser_url_wstring.assign(browser_url, browser_url.Length());
+ std::string browser_url_string = WideToUTF8(browser_url_wstring);
+ EXPECT_TRUE(did_receive_on_quit_) << "OnQuit was not received for "
+ << "browser with url "
+ << browser_url_string;
+
+ web_browser2_->Quit();
+ }
+
+ ScopedHandle process;
+ process.Set(OpenProcess(SYNCHRONIZE, FALSE, ie_process_id_));
+ DLOG_IF(WARNING, !process.IsValid())
+ << StringPrintf("OpenProcess failed: %i", ::GetLastError());
+
+ web_browser2_.Release();
+
+ // IE may not have closed yet. Wait here for the process to finish.
+ // This is necessary at least on some browser/platform configurations.
+ 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;
+ }
+ }
+ }
+ }
+}
+
+bool IEEventSink::IsCFRendering() {
+ DCHECK(web_browser2_);
+
+ HWND renderer_window;
+ if (web_browser2_) {
+ ScopedComPtr<IDispatch> doc;
+ web_browser2_->get_Document(doc.Receive());
+ if (doc) {
+ ScopedComPtr<IOleWindow> ole_window;
+ ole_window.QueryFrom(doc);
+ if (ole_window) {
+ ole_window->GetWindow(&renderer_window);
+ if (IsWindow(renderer_window)) {
+ wchar_t class_name[MAX_PATH] = {0};
+ GetClassName(renderer_window, class_name, arraysize(class_name));
+ return _wcsicmp(class_name, L"Internet Explorer_Server") != 0;
+ }
+ }
+ }
+ }
+ return false;
+}
+
+void IEEventSink::SetFocusToRenderer() {
+ simulate_input::SetKeyboardFocusToWindow(GetRendererWindow());
+}
+
+void IEEventSink::SendKeys(const wchar_t* input_string) {
+ SetFocusToRenderer();
+ simulate_input::SendStringW(input_string);
+}
+
+void IEEventSink::SendMouseClick(int x, int y,
+ simulate_input::MouseButton button) {
+ simulate_input::SendMouseClick(GetRendererWindow(), x, y, button);
+}
+
+void IEEventSink::ExpectRendererWindowHasFocus() {
+ HWND renderer_window = GetRendererWindow();
+ EXPECT_TRUE(IsWindow(renderer_window));
+
+ if (IsCFRendering()) {
+ 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 IEEventSink::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 IEEventSink::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));
+}
+
+HWND IEEventSink::GetRendererWindow() {
+ HWND renderer_window = NULL;
+ if (IsCFRendering()) {
+ DCHECK(chrome_frame_);
+ 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);
+ }
+ } else {
+ DCHECK(web_browser2_);
+ 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);
+ }
+ }
+ }
+
+ EXPECT_TRUE(IsWindow(renderer_window));
+ return renderer_window;
+}
+
+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();
+ 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 IEEventSink::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;
+}
+
+HRESULT IEEventSink::CloseWebBrowser() {
+ if (!web_browser2_)
+ return E_FAIL;
+
+ DisconnectFromChromeFrame();
+ EXPECT_HRESULT_SUCCEEDED(web_browser2_->Quit());
+ return S_OK;
+}
+
+void IEEventSink::Refresh() {
+ ScopedVariant refresh_level(REFRESH_NORMAL);
+ web_browser2_->Refresh2(refresh_level.AsInput());
+}
+
+void IEEventSink::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 IEEventSink::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);
+ }
+}
+
+void IEEventSink::FindIEProcessId() {
+ HWND hwnd = NULL;
+ web_browser2_->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&hwnd));
+ EXPECT_TRUE(::IsWindow(hwnd));
+ if (::IsWindow(hwnd))
+ ::GetWindowThreadProcessId(hwnd, &ie_process_id_);
+}
+
+// Event callbacks
+STDMETHODIMP_(void) IEEventSink::OnDownloadBegin() {
+ if (listener_)
+ listener_->OnDownloadBegin();
+}
+
+STDMETHODIMP_(void) IEEventSink::OnNewWindow2(IDispatch** disp,
+ VARIANT_BOOL* s) {
+ if (listener_)
+ listener_->OnNewWindow2(disp, s);
+}
+
+STDMETHODIMP_(void) IEEventSink::OnNavigateError(IDispatch* dispatch,
+ VARIANT* url, VARIANT* frame_name, VARIANT* status_code, VARIANT* cancel) {
+ DLOG(INFO) << __FUNCTION__;
+ if (listener_)
+ listener_->OnNavigateError(dispatch, url, frame_name, status_code, cancel);
+}
+
+STDMETHODIMP IEEventSink::OnBeforeNavigate2(
+ 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();
+ if (listener_)
+ listener_->OnBeforeNavigate2(dispatch, url, flags, target_frame_name,
+ post_data, headers, cancel);
+ return S_OK;
+}
+
+STDMETHODIMP_(void) IEEventSink::OnNavigateComplete2(
+ IDispatch* dispatch, VARIANT* url) {
+ DLOG(INFO) << __FUNCTION__;
+ ConnectToChromeFrame();
+ if (listener_)
+ listener_->OnNavigateComplete2(dispatch, url);
+}
+
+STDMETHODIMP_(void) IEEventSink::OnDocumentComplete(
+ IDispatch* dispatch, VARIANT* url) {
+ DLOG(INFO) << __FUNCTION__;
+ EXPECT_TRUE(url);
+ if (!url)
+ return;
+ if (listener_)
+ listener_->OnDocumentComplete(dispatch, url);
+}
+
+STDMETHODIMP_(void) IEEventSink::OnFileDownload(
+ VARIANT_BOOL active_doc, VARIANT_BOOL* cancel) {
+ DLOG(INFO) << __FUNCTION__ << StringPrintf(" 0x%08X ad=%i", this,
+ active_doc);
+ if (listener_)
+ listener_->OnFileDownload(active_doc, cancel);
+ // Always cancel file downloads in tests.
+ *cancel = VARIANT_TRUE;
+}
+
+STDMETHODIMP_(void) IEEventSink::OnNewWindow3(
+ IDispatch** dispatch, VARIANT_BOOL* cancel, DWORD flags, BSTR url_context,
+ BSTR url) {
+ DLOG(INFO) << __FUNCTION__;
+ EXPECT_TRUE(dispatch);
+ if (!dispatch)
+ return;
+
+ if (listener_)
+ listener_->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)
+ listener_->OnNewBrowserWindow(*dispatch, url);
+}
+
+STDMETHODIMP_(void) IEEventSink::OnQuit() {
+ DLOG(INFO) << __FUNCTION__;
+
+ did_receive_on_quit_ = true;
+
+ DispEventUnadvise(web_browser2_);
+ CoDisconnectObject(this, 0);
+
+ if (listener_)
+ listener_->OnQuit();
+}
+
+HRESULT IEEventSink::OnLoad(const VARIANT* param) {
+ DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
+ ScopedVariant stack_object(*param);
+ if (chrome_frame_) {
+ if (listener_)
+ listener_->OnLoad(param->bstrVal);
+ } else {
+ DLOG(WARNING) << "Invalid chrome frame pointer";
+ }
+ return S_OK;
+}
+
+HRESULT IEEventSink::OnLoadError(const VARIANT* param) {
+ DLOG(INFO) << __FUNCTION__ << " " << param->bstrVal;
+ if (chrome_frame_) {
+ if (listener_)
+ listener_->OnLoadError(param->bstrVal);
+ } else {
+ DLOG(WARNING) << "Invalid chrome frame pointer";
+ }
+ return S_OK;
+}
+
+HRESULT IEEventSink::OnMessage(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, &params,
+ data.Receive(), NULL, NULL));
+ EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[1], IID_NULL,
+ LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
+ origin.Receive(), NULL, NULL));
+ EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[2], IID_NULL,
+ LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
+ source.Receive(), NULL, NULL));
+ }
+ }
+
+ if (listener_)
+ listener_->OnMessage(V_BSTR(&data), V_BSTR(&origin), V_BSTR(&source));
+ return S_OK;
+}
+
+} // namespace chrome_frame_test \ No newline at end of file