| // Copyright (c) 2012 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 <shlguid.h> |
| #include <shobjidl.h> |
| |
| #include <map> |
| #include <utility> |
| |
| #include "base/lazy_instance.h" |
| #include "base/string_number_conversions.h" |
| #include "base/string_util.h" |
| #include "base/stringprintf.h" |
| #include "base/utf_string_conversions.h" |
| #include "base/win/scoped_bstr.h" |
| #include "base/win/scoped_handle.h" |
| #include "base/win/scoped_variant.h" |
| #include "chrome_frame/test/chrome_frame_test_utils.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using base::win::ScopedBstr; |
| |
| namespace { |
| |
| // A lookup table from DISPID to DWebBrowserEvents and/or DWebBrowserEvents2 |
| // method name. |
| class DispIdNameTable { |
| public: |
| DispIdNameTable(); |
| ~DispIdNameTable(); |
| |
| // Returns the method name corresponding to |dispid| or, if none is known, |
| // the string "DISPID |dispid|". |
| std::string Lookup(DISPID dispid) const; |
| |
| private: |
| std::map<DISPID,const char*> dispid_to_name_; |
| DISALLOW_COPY_AND_ASSIGN(DispIdNameTable); |
| }; |
| |
| DispIdNameTable::DispIdNameTable() { |
| static const struct { |
| DISPID dispid; |
| const char* name; |
| } kIdToName[] = { |
| // DWebBrowserEvents |
| { 100, "BeforeNavigate" }, |
| { 101, "NavigateComplete" }, |
| { 102, "StatusTextChange" }, |
| { 108, "ProgressChange" }, |
| { 104, "DownloadComplete" }, |
| { 105, "CommandStateChange" }, |
| { 106, "DownloadBegin" }, |
| { 107, "NewWindow" }, |
| { 113, "TitleChange" }, |
| { 200, "FrameBeforeNavigate" }, |
| { 201, "FrameNavigateComplete" }, |
| { 204, "FrameNewWindow" }, |
| { 103, "Quit" }, |
| { 109, "WindowMove" }, |
| { 110, "WindowResize" }, |
| { 111, "WindowActivate" }, |
| { 112, "PropertyChange" }, |
| // DWebBrowserEvents2 |
| { 250, "BeforeNavigate2" }, |
| { 251, "NewWindow2" }, |
| { 252, "NavigateComplete2" }, |
| { 259, "DocumentComplete" }, |
| { 253, "OnQuit" }, |
| { 254, "OnVisible" }, |
| { 255, "OnToolBar" }, |
| { 256, "OnMenuBar" }, |
| { 257, "OnStatusBar" }, |
| { 258, "OnFullScreen" }, |
| { 260, "OnTheaterMode" }, |
| { 262, "WindowSetResizable" }, |
| { 264, "WindowSetLeft" }, |
| { 265, "WindowSetTop" }, |
| { 266, "WindowSetWidth" }, |
| { 267, "WindowSetHeight" }, |
| { 263, "WindowClosing" }, |
| { 268, "ClientToHostWindow" }, |
| { 269, "SetSecureLockIcon" }, |
| { 270, "FileDownload" }, |
| { 271, "NavigateError" }, |
| { 225, "PrintTemplateInstantiation" }, |
| { 226, "PrintTemplateTeardown" }, |
| { 227, "UpdatePageStatus" }, |
| { 272, "PrivacyImpactedStateChange" }, |
| { 273, "NewWindow3" }, |
| { 282, "SetPhishingFilterStatus" }, |
| { 283, "WindowStateChanged" }, |
| { 284, "NewProcess" }, |
| { 285, "ThirdPartyUrlBlocked" }, |
| { 286, "RedirectXDomainBlocked" }, |
| // Present in ExDispid.h but not ExDisp.idl |
| { 114, "TitleIconChange" }, |
| { 261, "OnAddressBar" }, |
| { 281, "ViewUpdate" }, |
| }; |
| size_t index_of_duplicate = 0; |
| DISPID duplicate_dispid = 0; |
| for (size_t i = 0; i < arraysize(kIdToName); ++i) { |
| if (!dispid_to_name_.insert(std::make_pair(kIdToName[i].dispid, |
| kIdToName[i].name)).second && |
| index_of_duplicate == 0) { |
| index_of_duplicate = i; |
| duplicate_dispid = kIdToName[i].dispid; |
| } |
| } |
| DCHECK_EQ(static_cast<size_t>(0), index_of_duplicate) |
| << "Duplicate name for DISPID " << duplicate_dispid |
| << " at kIdToName[" << index_of_duplicate << "]"; |
| } |
| |
| DispIdNameTable::~DispIdNameTable() { |
| } |
| |
| std::string DispIdNameTable::Lookup(DISPID dispid) const { |
| std::map<DISPID,const char*>::const_iterator it = |
| dispid_to_name_.find(dispid); |
| if (it != dispid_to_name_.end()) |
| return it->second; |
| return std::string("DISPID ").append(base::IntToString(dispid)); |
| } |
| |
| base::LazyInstance<DispIdNameTable> g_dispIdToName = LAZY_INSTANCE_INITIALIZER; |
| |
| } // namespace |
| |
| 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 |
| } |
| }; |
| |
| bool IEEventSink::abnormal_shutdown_ = false; |
| |
| 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(); |
| } |
| |
| void IEEventSink::SetAbnormalShutdown(bool abnormal_shutdown) { |
| abnormal_shutdown_ = abnormal_shutdown; |
| } |
| |
| // 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_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() { |
| if (!abnormal_shutdown_) { |
| 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); |
| LOG(ERROR) << "OnQuit was not received for browser with url " |
| << browser_url_string; |
| web_browser2_->Quit(); |
| } |
| |
| base::win::ScopedHandle process; |
| process.Set(OpenProcess(SYNCHRONIZE, FALSE, ie_process_id_)); |
| web_browser2_.Release(); |
| |
| if (!process.IsValid()) { |
| LOG_IF(WARNING, !process.IsValid()) |
| << base::StringPrintf("OpenProcess failed: %i", ::GetLastError()); |
| return; |
| } |
| // IE may not have closed yet. Wait here for the process to finish. |
| // This is necessary at least on some browser/platform configurations. |
| WaitForSingleObject(process, kDefaultWaitForIEToTerminateMs); |
| } |
| } else { |
| LOG(ERROR) << "Terminating hung IE process"; |
| } |
| chrome_frame_test::KillProcesses(chrome_frame_test::kIEImageName, 0, |
| !abnormal_shutdown_); |
| chrome_frame_test::KillProcesses(chrome_frame_test::kIEBrokerImageName, 0, |
| !abnormal_shutdown_); |
| } |
| |
| bool IEEventSink::IsCFRendering() { |
| DCHECK(web_browser2_); |
| |
| if (web_browser2_) { |
| base::win::ScopedComPtr<IDispatch> doc; |
| web_browser2_->get_Document(doc.Receive()); |
| if (doc) { |
| // Detect if CF is rendering based on whether the document is a |
| // ChromeActiveDocument. Detecting based on hwnd is problematic as |
| // the CF Active Document window may not have been created yet. |
| base::win::ScopedComPtr<IChromeFrame> chrome_frame; |
| chrome_frame.QueryFrom(doc); |
| return chrome_frame.get(); |
| } |
| } |
| return false; |
| } |
| |
| void IEEventSink::PostMessageToCF(const std::wstring& message, |
| const std::wstring& target) { |
| EXPECT_TRUE(chrome_frame_ != NULL); |
| if (!chrome_frame_) |
| return; |
| ScopedBstr message_bstr(message.c_str()); |
| base::win::ScopedVariant target_variant(target.c_str()); |
| EXPECT_HRESULT_SUCCEEDED( |
| chrome_frame_->postMessage(message_bstr, target_variant)); |
| } |
| |
| 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)); |
| |
| 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) { |
| base::win::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::GetBrowserWindow() { |
| HWND browser_window = NULL; |
| web_browser2_->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&browser_window)); |
| EXPECT_TRUE(::IsWindow(browser_window)); |
| return browser_window; |
| } |
| |
| HWND IEEventSink::GetRendererWindow() { |
| HWND renderer_window = NULL; |
| if (IsCFRendering()) { |
| DCHECK(chrome_frame_); |
| base::win::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)); |
| |
| wchar_t class_name[MAX_PATH] = {0}; |
| HWND child_window = NULL; |
| // chrome tab window is the first (and the only) child of activex |
| for (HWND first_child = activex_window; ::IsWindow(first_child); |
| first_child = ::GetWindow(first_child, GW_CHILD)) { |
| child_window = first_child; |
| GetClassName(child_window, class_name, arraysize(class_name)); |
| if (!_wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND")) { |
| renderer_window = child_window; |
| break; |
| } |
| } |
| } |
| } else { |
| DCHECK(web_browser2_); |
| base::win::ScopedComPtr<IDispatch> doc; |
| HRESULT hr = web_browser2_->get_Document(doc.Receive()); |
| EXPECT_HRESULT_SUCCEEDED(hr); |
| EXPECT_TRUE(doc); |
| if (doc) { |
| base::win::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; |
| } |
| |
| HWND IEEventSink::GetRendererWindowSafe() { |
| HWND renderer_window = NULL; |
| if (IsCFRendering()) { |
| DCHECK(chrome_frame_); |
| base::win::ScopedComPtr<IOleWindow> ole_window; |
| ole_window.QueryFrom(chrome_frame_); |
| |
| if (ole_window) { |
| HWND activex_window = NULL; |
| ole_window->GetWindow(&activex_window); |
| |
| // chrome tab window is the first (and the only) child of activex |
| for (HWND first_child = activex_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)); |
| if (_wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND") != 0) |
| renderer_window = NULL; |
| } |
| } else { |
| DCHECK(web_browser2_); |
| base::win::ScopedComPtr<IDispatch> doc; |
| web_browser2_->get_Document(doc.Receive()); |
| if (doc) { |
| base::win::ScopedComPtr<IOleWindow> ole_window; |
| ole_window.QueryFrom(doc); |
| if (ole_window) { |
| ole_window->GetWindow(&renderer_window); |
| } |
| } |
| } |
| if (!::IsWindow(renderer_window)) |
| renderer_window = NULL; |
| return renderer_window; |
| } |
| |
| HRESULT IEEventSink::LaunchIEAndNavigate(const std::wstring& navigate_url, |
| IEEventListener* listener) { |
| listener_ = listener; |
| HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive()); |
| if (SUCCEEDED(hr)) { |
| web_browser2_->put_Visible(VARIANT_TRUE); |
| hr = Attach(web_browser2_); |
| if (SUCCEEDED(hr)) { |
| hr = Navigate(navigate_url); |
| if (FAILED(hr)) { |
| LOG(ERROR) << "Failed to navigate IE to " << navigate_url << ", hr = 0x" |
| << std::hex << hr; |
| } |
| } else { |
| LOG(ERROR) << "Failed to attach to web browser event sink for " |
| << navigate_url << ", hr = 0x" << std::hex << hr; |
| } |
| } else { |
| LOG(ERROR) << "Failed to Launch IE for " << navigate_url << ", hr = 0x" |
| << std::hex << hr; |
| } |
| |
| return hr; |
| } |
| |
| HRESULT IEEventSink::Navigate(const std::wstring& navigate_url) { |
| VARIANT empty = base::win::ScopedVariant::kEmptyVariant; |
| base::win::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() { |
| base::win::ScopedVariant refresh_level(REFRESH_COMPLETELY); |
| web_browser2_->Refresh2(refresh_level.AsInput()); |
| } |
| |
| // private methods |
| void IEEventSink::ConnectToChromeFrame() { |
| DCHECK(web_browser2_); |
| if (chrome_frame_.get()) |
| return; |
| base::win::ScopedComPtr<IShellBrowser> shell_browser; |
| DoQueryService(SID_STopLevelBrowser, web_browser2_, |
| shell_browser.Receive()); |
| |
| if (shell_browser) { |
| base::win::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_) { |
| base::win::ScopedVariant onmessage(onmessage_.ToDispatch()); |
| base::win::ScopedVariant onloaderror(onloaderror_.ToDispatch()); |
| base::win::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. |
| base::win::ScopedComPtr<IChromeFrame> chrome_frame(chrome_frame_); |
| chrome_frame_.Release(); |
| base::win::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_); |
| EXPECT_NE(static_cast<DWORD>(0), ie_process_id_); |
| } |
| |
| // Event callbacks |
| STDMETHODIMP_(void) IEEventSink::OnDownloadBegin() { |
| if (listener_) |
| listener_->OnDownloadBegin(); |
| } |
| |
| STDMETHODIMP_(void) IEEventSink::OnNewWindow2(IDispatch** dispatch, |
| VARIANT_BOOL* s) { |
| VLOG(1) << __FUNCTION__; |
| |
| EXPECT_TRUE(dispatch); |
| if (!dispatch) |
| return; |
| |
| if (listener_) |
| listener_->OnNewWindow2(dispatch, s); |
| |
| // 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) { |
| base::win::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_) |
| listener_->OnNewBrowserWindow(*dispatch, ScopedBstr()); |
| } |
| |
| STDMETHODIMP_(void) IEEventSink::OnNavigateError(IDispatch* dispatch, |
| VARIANT* url, VARIANT* frame_name, VARIANT* status_code, VARIANT* cancel) { |
| VLOG(1) << __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) { |
| VLOG(1) << __FUNCTION__ << " " |
| << base::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) { |
| VLOG(1) << __FUNCTION__; |
| ConnectToChromeFrame(); |
| if (listener_) |
| listener_->OnNavigateComplete2(dispatch, url); |
| } |
| |
| STDMETHODIMP_(void) IEEventSink::OnDocumentComplete( |
| IDispatch* dispatch, VARIANT* url) { |
| VLOG(1) << __FUNCTION__; |
| EXPECT_TRUE(url); |
| if (!url) |
| return; |
| if (listener_) |
| listener_->OnDocumentComplete(dispatch, url); |
| } |
| |
| STDMETHODIMP_(void) IEEventSink::OnFileDownload( |
| VARIANT_BOOL active_doc, VARIANT_BOOL* cancel) { |
| VLOG(1) << __FUNCTION__ << " " |
| << base::StringPrintf(" 0x%08X ad=%i", this, active_doc); |
| if (listener_) { |
| listener_->OnFileDownload(active_doc, cancel); |
| } else { |
| *cancel = VARIANT_TRUE; |
| } |
| } |
| |
| STDMETHODIMP_(void) IEEventSink::OnNewWindow3( |
| IDispatch** dispatch, VARIANT_BOOL* cancel, DWORD flags, BSTR url_context, |
| BSTR url) { |
| VLOG(1) << __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) { |
| base::win::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_) |
| listener_->OnNewBrowserWindow(*dispatch, url); |
| } |
| |
| STDMETHODIMP_(void) IEEventSink::OnQuit() { |
| VLOG(1) << __FUNCTION__; |
| |
| did_receive_on_quit_ = true; |
| |
| DispEventUnadvise(web_browser2_); |
| CoDisconnectObject(this, 0); |
| |
| if (listener_) |
| listener_->OnQuit(); |
| } |
| |
| STDMETHODIMP IEEventSink::Invoke(DISPID dispid, REFIID riid, LCID lcid, |
| WORD flags, DISPPARAMS* params, |
| VARIANT* result, EXCEPINFO* except_info, |
| UINT* arg_error) { |
| VLOG(1) << __FUNCTION__ << L" event: " << g_dispIdToName.Get().Lookup(dispid); |
| return DispEventsImpl::Invoke(dispid, riid, lcid, flags, params, result, |
| except_info, arg_error); |
| } |
| |
| HRESULT IEEventSink::OnLoad(const VARIANT* param) { |
| VLOG(1) << __FUNCTION__ << " " << param->bstrVal; |
| base::win::ScopedVariant stack_object(*param); |
| if (chrome_frame_) { |
| if (listener_) |
| listener_->OnLoad(param->bstrVal); |
| } else { |
| LOG(WARNING) << "Invalid chrome frame pointer"; |
| } |
| return S_OK; |
| } |
| |
| HRESULT IEEventSink::OnLoadError(const VARIANT* param) { |
| VLOG(1) << __FUNCTION__ << " " << param->bstrVal; |
| if (chrome_frame_) { |
| if (listener_) |
| listener_->OnLoadError(param->bstrVal); |
| } else { |
| LOG(WARNING) << "Invalid chrome frame pointer"; |
| } |
| return S_OK; |
| } |
| |
| HRESULT IEEventSink::OnMessage(const VARIANT* param) { |
| VLOG(1) << __FUNCTION__ << " " << param; |
| if (!chrome_frame_.get()) { |
| LOG(WARNING) << "Invalid chrome frame pointer"; |
| return S_OK; |
| } |
| |
| base::win::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)); |
| } |
| } |
| |
| if (listener_) |
| listener_->OnMessage(V_BSTR(&data), V_BSTR(&origin), V_BSTR(&source)); |
| return S_OK; |
| } |
| |
| } // namespace chrome_frame_test |