• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "chrome_frame/test/ie_event_sink.h"
6 
7 #include <shlguid.h>
8 #include <shobjidl.h>
9 
10 #include <map>
11 #include <utility>
12 
13 #include "base/lazy_instance.h"
14 #include "base/strings/string_number_conversions.h"
15 #include "base/strings/string_piece.h"
16 #include "base/strings/string_util.h"
17 #include "base/strings/stringprintf.h"
18 #include "base/strings/utf_string_conversions.h"
19 #include "base/test/test_timeouts.h"
20 #include "base/time/time.h"
21 #include "base/win/scoped_bstr.h"
22 #include "base/win/scoped_handle.h"
23 #include "base/win/scoped_variant.h"
24 #include "chrome_frame/test/chrome_frame_test_utils.h"
25 #include "testing/gtest/include/gtest/gtest.h"
26 
27 using base::win::ScopedBstr;
28 
29 namespace {
30 
31 // A lookup table from DISPID to DWebBrowserEvents and/or DWebBrowserEvents2
32 // method name.
33 class DispIdNameTable {
34  public:
35   DispIdNameTable();
36   ~DispIdNameTable();
37 
38   // Returns the method name corresponding to |dispid| or, if none is known,
39   // the string "DISPID |dispid|".
40   std::string Lookup(DISPID dispid) const;
41 
42  private:
43   std::map<DISPID,const char*> dispid_to_name_;
44   DISALLOW_COPY_AND_ASSIGN(DispIdNameTable);
45 };
46 
DispIdNameTable()47 DispIdNameTable::DispIdNameTable() {
48   static const struct {
49     DISPID dispid;
50     const char* name;
51   } kIdToName[] = {
52     // DWebBrowserEvents
53     { 100, "BeforeNavigate" },
54     { 101, "NavigateComplete" },
55     { 102, "StatusTextChange" },
56     { 108, "ProgressChange" },
57     { 104, "DownloadComplete" },
58     { 105, "CommandStateChange" },
59     { 106, "DownloadBegin" },
60     { 107, "NewWindow" },
61     { 113, "TitleChange" },
62     { 200, "FrameBeforeNavigate" },
63     { 201, "FrameNavigateComplete" },
64     { 204, "FrameNewWindow" },
65     { 103, "Quit" },
66     { 109, "WindowMove" },
67     { 110, "WindowResize" },
68     { 111, "WindowActivate" },
69     { 112, "PropertyChange" },
70     // DWebBrowserEvents2
71     { 250, "BeforeNavigate2" },
72     { 251, "NewWindow2" },
73     { 252, "NavigateComplete2" },
74     { 259, "DocumentComplete" },
75     { 253, "OnQuit" },
76     { 254, "OnVisible" },
77     { 255, "OnToolBar" },
78     { 256, "OnMenuBar" },
79     { 257, "OnStatusBar" },
80     { 258, "OnFullScreen" },
81     { 260, "OnTheaterMode" },
82     { 262, "WindowSetResizable" },
83     { 264, "WindowSetLeft" },
84     { 265, "WindowSetTop" },
85     { 266, "WindowSetWidth" },
86     { 267, "WindowSetHeight" },
87     { 263, "WindowClosing" },
88     { 268, "ClientToHostWindow" },
89     { 269, "SetSecureLockIcon" },
90     { 270, "FileDownload" },
91     { 271, "NavigateError" },
92     { 225, "PrintTemplateInstantiation" },
93     { 226, "PrintTemplateTeardown" },
94     { 227, "UpdatePageStatus" },
95     { 272, "PrivacyImpactedStateChange" },
96     { 273, "NewWindow3" },
97     { 282, "SetPhishingFilterStatus" },
98     { 283, "WindowStateChanged" },
99     { 284, "NewProcess" },
100     { 285, "ThirdPartyUrlBlocked" },
101     { 286, "RedirectXDomainBlocked" },
102     // Present in ExDispid.h but not ExDisp.idl
103     { 114, "TitleIconChange" },
104     { 261, "OnAddressBar" },
105     { 281, "ViewUpdate" },
106   };
107   size_t index_of_duplicate = 0;
108   DISPID duplicate_dispid = 0;
109   for (size_t i = 0; i < arraysize(kIdToName); ++i) {
110     if (!dispid_to_name_.insert(std::make_pair(kIdToName[i].dispid,
111                                                kIdToName[i].name)).second &&
112         index_of_duplicate == 0) {
113       index_of_duplicate = i;
114       duplicate_dispid = kIdToName[i].dispid;
115     }
116   }
117   DCHECK_EQ(static_cast<size_t>(0), index_of_duplicate)
118       << "Duplicate name for DISPID " << duplicate_dispid
119       << " at kIdToName[" << index_of_duplicate << "]";
120 }
121 
~DispIdNameTable()122 DispIdNameTable::~DispIdNameTable() {
123 }
124 
Lookup(DISPID dispid) const125 std::string DispIdNameTable::Lookup(DISPID dispid) const {
126   std::map<DISPID,const char*>::const_iterator it =
127       dispid_to_name_.find(dispid);
128   if (it != dispid_to_name_.end())
129     return it->second;
130   return std::string("DISPID ").append(base::IntToString(dispid));
131 }
132 
133 base::LazyInstance<DispIdNameTable> g_dispIdToName = LAZY_INSTANCE_INITIALIZER;
134 
135 }  // namespace
136 
137 namespace chrome_frame_test {
138 
139 const int kDefaultWaitForIEToTerminateMs = 10 * 1000;
140 
141 _ATL_FUNC_INFO IEEventSink::kNavigateErrorInfo = {
142   CC_STDCALL, VT_EMPTY, 5, {
143     VT_DISPATCH,
144     VT_VARIANT | VT_BYREF,
145     VT_VARIANT | VT_BYREF,
146     VT_VARIANT | VT_BYREF,
147     VT_BOOL | VT_BYREF,
148   }
149 };
150 
151 _ATL_FUNC_INFO IEEventSink::kNavigateComplete2Info = {
152   CC_STDCALL, VT_EMPTY, 2, {
153     VT_DISPATCH,
154     VT_VARIANT | VT_BYREF
155   }
156 };
157 
158 _ATL_FUNC_INFO IEEventSink::kBeforeNavigate2Info = {
159   CC_STDCALL, VT_EMPTY, 7, {
160     VT_DISPATCH,
161     VT_VARIANT | VT_BYREF,
162     VT_VARIANT | VT_BYREF,
163     VT_VARIANT | VT_BYREF,
164     VT_VARIANT | VT_BYREF,
165     VT_VARIANT | VT_BYREF,
166     VT_BOOL | VT_BYREF
167   }
168 };
169 
170 _ATL_FUNC_INFO IEEventSink::kNewWindow2Info = {
171   CC_STDCALL, VT_EMPTY, 2, {
172     VT_DISPATCH | VT_BYREF,
173     VT_BOOL | VT_BYREF,
174   }
175 };
176 
177 _ATL_FUNC_INFO IEEventSink::kNewWindow3Info = {
178   CC_STDCALL, VT_EMPTY, 5, {
179     VT_DISPATCH | VT_BYREF,
180     VT_BOOL | VT_BYREF,
181     VT_UINT,
182     VT_BSTR,
183     VT_BSTR
184   }
185 };
186 
187 _ATL_FUNC_INFO IEEventSink::kVoidMethodInfo = {
188     CC_STDCALL, VT_EMPTY, 0, {NULL}};
189 
190 _ATL_FUNC_INFO IEEventSink::kDocumentCompleteInfo = {
191   CC_STDCALL, VT_EMPTY, 2, {
192     VT_DISPATCH,
193     VT_VARIANT | VT_BYREF
194   }
195 };
196 
197 _ATL_FUNC_INFO IEEventSink::kFileDownloadInfo = {
198   CC_STDCALL, VT_EMPTY, 2, {
199     VT_BOOL,
200     VT_BOOL | VT_BYREF
201   }
202 };
203 
204 bool IEEventSink::abnormal_shutdown_ = false;
205 
IEEventSink()206 IEEventSink::IEEventSink()
207     : onmessage_(this, &IEEventSink::OnMessage),
208       onloaderror_(this, &IEEventSink::OnLoadError),
209       onload_(this, &IEEventSink::OnLoad),
210       listener_(NULL),
211       ie_process_id_(0),
212       did_receive_on_quit_(false) {
213 }
214 
~IEEventSink()215 IEEventSink::~IEEventSink() {
216   Uninitialize();
217 }
218 
SetAbnormalShutdown(bool abnormal_shutdown)219 void IEEventSink::SetAbnormalShutdown(bool abnormal_shutdown) {
220   abnormal_shutdown_ = abnormal_shutdown;
221 }
222 
223 // IEEventSink member defines
Attach(IDispatch * browser_disp)224 void IEEventSink::Attach(IDispatch* browser_disp) {
225   EXPECT_TRUE(NULL != browser_disp);
226   if (browser_disp) {
227     EXPECT_HRESULT_SUCCEEDED(web_browser2_.QueryFrom(browser_disp));
228     EXPECT_HRESULT_SUCCEEDED(Attach(web_browser2_.get()));
229   }
230 }
231 
Attach(IWebBrowser2 * browser)232 HRESULT IEEventSink::Attach(IWebBrowser2* browser) {
233   HRESULT result = E_INVALIDARG;
234   if (browser) {
235     web_browser2_ = browser;
236     FindIEProcessId();
237     result = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
238   }
239   return result;
240 }
241 
Uninitialize()242 void IEEventSink::Uninitialize() {
243   if (!abnormal_shutdown_) {
244     DisconnectFromChromeFrame();
245     if (web_browser2_.get()) {
246       if (m_dwEventCookie != 0xFEFEFEFE) {
247         DispEventUnadvise(web_browser2_);
248         CoDisconnectObject(this, 0);
249       }
250 
251       if (!did_receive_on_quit_) {
252         // Log the browser window url for debugging purposes.
253         ScopedBstr browser_url;
254         web_browser2_->get_LocationURL(browser_url.Receive());
255         std::wstring browser_url_wstring;
256         browser_url_wstring.assign(browser_url, browser_url.Length());
257         std::string browser_url_string = WideToUTF8(browser_url_wstring);
258         LOG(ERROR) << "OnQuit was not received for browser with url "
259                    << browser_url_string;
260         web_browser2_->Quit();
261       }
262 
263       base::win::ScopedHandle process;
264       process.Set(OpenProcess(SYNCHRONIZE, FALSE, ie_process_id_));
265       web_browser2_.Release();
266 
267       if (!process.IsValid()) {
268         LOG_IF(WARNING, !process.IsValid())
269             << base::StringPrintf("OpenProcess failed: %i", ::GetLastError());
270         return;
271       }
272       // IE may not have closed yet. Wait here for the process to finish.
273       // This is necessary at least on some browser/platform configurations.
274       WaitForSingleObject(process, kDefaultWaitForIEToTerminateMs);
275     }
276   } else {
277     LOG(ERROR) << "Terminating hung IE process";
278   }
279   chrome_frame_test::KillProcesses(chrome_frame_test::kIEImageName, 0,
280                                    !abnormal_shutdown_);
281   chrome_frame_test::KillProcesses(chrome_frame_test::kIEBrokerImageName, 0,
282                                    !abnormal_shutdown_);
283 }
284 
IsCFRendering()285 bool IEEventSink::IsCFRendering() {
286   DCHECK(web_browser2_);
287 
288   if (web_browser2_) {
289     base::win::ScopedComPtr<IDispatch> doc;
290     web_browser2_->get_Document(doc.Receive());
291     if (doc) {
292       // Detect if CF is rendering based on whether the document is a
293       // ChromeActiveDocument. Detecting based on hwnd is problematic as
294       // the CF Active Document window may not have been created yet.
295       base::win::ScopedComPtr<IChromeFrame> chrome_frame;
296       chrome_frame.QueryFrom(doc);
297       return chrome_frame.get();
298     }
299   }
300   return false;
301 }
302 
PostMessageToCF(const std::wstring & message,const std::wstring & target)303 void IEEventSink::PostMessageToCF(const std::wstring& message,
304                                   const std::wstring& target) {
305   EXPECT_TRUE(chrome_frame_ != NULL);
306   if (!chrome_frame_)
307     return;
308   ScopedBstr message_bstr(message.c_str());
309   base::win::ScopedVariant target_variant(target.c_str());
310   EXPECT_HRESULT_SUCCEEDED(
311       chrome_frame_->postMessage(message_bstr, target_variant));
312 }
313 
SetFocusToRenderer()314 void IEEventSink::SetFocusToRenderer() {
315   simulate_input::SetKeyboardFocusToWindow(GetRendererWindow());
316 }
317 
SendKeys(const char * input_string)318 void IEEventSink::SendKeys(const char* input_string) {
319   HWND window = GetRendererWindow();
320   simulate_input::SetKeyboardFocusToWindow(window);
321   const base::TimeDelta kMessageSleep = TestTimeouts::tiny_timeout();
322   const base::StringPiece codes(input_string);
323   for (size_t i = 0; i < codes.length(); ++i) {
324     char character = codes[i];
325     UINT virtual_key = 0;
326 
327     if (character >= 'a' && character <= 'z') {
328       // VK_A - VK_Z are ASCII 'A' - 'Z'.
329       virtual_key = 'A' + (character - 'a');
330     } else if (character >= '0' && character <= '9') {
331       // VK_0 - VK_9 are ASCII '0' - '9'.
332       virtual_key = character;
333     } else {
334       FAIL() << "Character value out of range at position " << i
335              << " of string \"" << input_string << "\"";
336     }
337 
338     UINT scan_code = MapVirtualKey(virtual_key, MAPVK_VK_TO_VSC);
339     EXPECT_NE(0U, scan_code) << "No translation for virtual key "
340                              << virtual_key << " for character at position "
341                              << i << " of string \"" << input_string << "\"";
342 
343     ::PostMessage(window, WM_KEYDOWN,
344                   virtual_key, MAKELPARAM(1, scan_code));
345     base::PlatformThread::Sleep(kMessageSleep);
346     ::PostMessage(window, WM_KEYUP,
347                   virtual_key, MAKELPARAM(1, scan_code | KF_UP | KF_REPEAT));
348     base::PlatformThread::Sleep(kMessageSleep);
349   }
350 }
351 
SendMouseClick(int x,int y,simulate_input::MouseButton button)352 void IEEventSink::SendMouseClick(int x, int y,
353                                  simulate_input::MouseButton button) {
354   simulate_input::SendMouseClick(GetRendererWindow(), x, y, button);
355 }
356 
ExpectRendererWindowHasFocus()357 void IEEventSink::ExpectRendererWindowHasFocus() {
358   HWND renderer_window = GetRendererWindow();
359   EXPECT_TRUE(IsWindow(renderer_window));
360 
361   DWORD renderer_thread = 0;
362   DWORD renderer_process = 0;
363   renderer_thread = GetWindowThreadProcessId(renderer_window,
364                                              &renderer_process);
365 
366   ASSERT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, TRUE));
367   HWND focus_window = GetFocus();
368   EXPECT_EQ(renderer_window, focus_window);
369   EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE));
370 }
371 
ExpectAddressBarUrl(const std::wstring & expected_url)372 void IEEventSink::ExpectAddressBarUrl(
373     const std::wstring& expected_url) {
374   DCHECK(web_browser2_);
375   if (web_browser2_) {
376     ScopedBstr address_bar_url;
377     EXPECT_EQ(S_OK, web_browser2_->get_LocationURL(address_bar_url.Receive()));
378     EXPECT_EQ(expected_url, std::wstring(address_bar_url));
379   }
380 }
381 
Exec(const GUID * cmd_group_guid,DWORD command_id,DWORD cmd_exec_opt,VARIANT * in_args,VARIANT * out_args)382 void IEEventSink::Exec(const GUID* cmd_group_guid, DWORD command_id,
383                                DWORD cmd_exec_opt, VARIANT* in_args,
384                                VARIANT* out_args) {
385   base::win::ScopedComPtr<IOleCommandTarget> shell_browser_cmd_target;
386   DoQueryService(SID_STopLevelBrowser, web_browser2_,
387                  shell_browser_cmd_target.Receive());
388   ASSERT_TRUE(NULL != shell_browser_cmd_target);
389   EXPECT_HRESULT_SUCCEEDED(shell_browser_cmd_target->Exec(cmd_group_guid,
390       command_id, cmd_exec_opt, in_args, out_args));
391 }
392 
GetBrowserWindow()393 HWND IEEventSink::GetBrowserWindow() {
394   HWND browser_window = NULL;
395   web_browser2_->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&browser_window));
396   EXPECT_TRUE(::IsWindow(browser_window));
397   return browser_window;
398 }
399 
GetRendererWindow()400 HWND IEEventSink::GetRendererWindow() {
401   HWND renderer_window = NULL;
402   if (IsCFRendering()) {
403     DCHECK(chrome_frame_);
404     base::win::ScopedComPtr<IOleWindow> ole_window;
405     ole_window.QueryFrom(chrome_frame_);
406     EXPECT_TRUE(ole_window.get());
407 
408     if (ole_window) {
409       HWND activex_window = NULL;
410       ole_window->GetWindow(&activex_window);
411       EXPECT_TRUE(IsWindow(activex_window));
412 
413       wchar_t class_name[MAX_PATH] = {0};
414       HWND child_window = NULL;
415       // chrome tab window is the first (and the only) child of activex
416       for (HWND first_child = activex_window; ::IsWindow(first_child);
417            first_child = ::GetWindow(first_child, GW_CHILD)) {
418         child_window = first_child;
419         GetClassName(child_window, class_name, arraysize(class_name));
420 #if defined(USE_AURA)
421         static const wchar_t kWndClassPrefix[] = L"Chrome_WidgetWin_";
422 #else
423         static const wchar_t kWndClassPrefix[] = L"Chrome_RenderWidgetHostHWND";
424 #endif
425         if (!_wcsnicmp(class_name, kWndClassPrefix, wcslen(kWndClassPrefix))) {
426           renderer_window = child_window;
427           break;
428         }
429       }
430     }
431   } else {
432     DCHECK(web_browser2_);
433     base::win::ScopedComPtr<IDispatch> doc;
434     HRESULT hr = web_browser2_->get_Document(doc.Receive());
435     EXPECT_HRESULT_SUCCEEDED(hr);
436     EXPECT_TRUE(doc);
437     if (doc) {
438       base::win::ScopedComPtr<IOleWindow> ole_window;
439       ole_window.QueryFrom(doc);
440       EXPECT_TRUE(ole_window);
441       if (ole_window) {
442         ole_window->GetWindow(&renderer_window);
443       }
444     }
445   }
446 
447   EXPECT_TRUE(::IsWindow(renderer_window));
448   return renderer_window;
449 }
450 
GetRendererWindowSafe()451 HWND IEEventSink::GetRendererWindowSafe() {
452   HWND renderer_window = NULL;
453   if (IsCFRendering()) {
454     DCHECK(chrome_frame_);
455     base::win::ScopedComPtr<IOleWindow> ole_window;
456     ole_window.QueryFrom(chrome_frame_);
457 
458     if (ole_window) {
459       HWND activex_window = NULL;
460       ole_window->GetWindow(&activex_window);
461 
462       // chrome tab window is the first (and the only) child of activex
463       for (HWND first_child = activex_window; ::IsWindow(first_child);
464            first_child = ::GetWindow(first_child, GW_CHILD)) {
465         renderer_window = first_child;
466       }
467       wchar_t class_name[MAX_PATH] = {0};
468       GetClassName(renderer_window, class_name, arraysize(class_name));
469       if (_wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND") != 0)
470         renderer_window = NULL;
471     }
472   } else {
473     DCHECK(web_browser2_);
474     base::win::ScopedComPtr<IDispatch> doc;
475     web_browser2_->get_Document(doc.Receive());
476     if (doc) {
477       base::win::ScopedComPtr<IOleWindow> ole_window;
478       ole_window.QueryFrom(doc);
479       if (ole_window) {
480         ole_window->GetWindow(&renderer_window);
481       }
482     }
483   }
484   if (!::IsWindow(renderer_window))
485     renderer_window = NULL;
486   return renderer_window;
487 }
488 
LaunchIEAndNavigate(const std::wstring & navigate_url,IEEventListener * listener)489 HRESULT IEEventSink::LaunchIEAndNavigate(const std::wstring& navigate_url,
490                                          IEEventListener* listener) {
491   listener_ = listener;
492   HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive());
493   if (SUCCEEDED(hr)) {
494     web_browser2_->put_Visible(VARIANT_TRUE);
495     hr = Attach(web_browser2_);
496     if (SUCCEEDED(hr)) {
497       hr = Navigate(navigate_url);
498       if (FAILED(hr)) {
499         LOG(ERROR) << "Failed to navigate IE to " << navigate_url << ", hr = 0x"
500                    << std::hex << hr;
501       }
502     } else {
503       LOG(ERROR) << "Failed to attach to web browser event sink for "
504                  << navigate_url << ", hr = 0x" << std::hex << hr;
505     }
506   } else {
507     LOG(ERROR) << "Failed to Launch IE for " << navigate_url << ", hr = 0x"
508                << std::hex << hr;
509   }
510 
511   return hr;
512 }
513 
Navigate(const std::wstring & navigate_url)514 HRESULT IEEventSink::Navigate(const std::wstring& navigate_url) {
515   VARIANT empty = base::win::ScopedVariant::kEmptyVariant;
516   base::win::ScopedVariant url;
517   url.Set(navigate_url.c_str());
518 
519   HRESULT hr = S_OK;
520   hr = web_browser2_->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty);
521   EXPECT_TRUE(hr == S_OK);
522   return hr;
523 }
524 
CloseWebBrowser()525 HRESULT IEEventSink::CloseWebBrowser() {
526   if (!web_browser2_)
527     return E_FAIL;
528 
529   DisconnectFromChromeFrame();
530   EXPECT_HRESULT_SUCCEEDED(web_browser2_->Quit());
531   return S_OK;
532 }
533 
Refresh()534 void IEEventSink::Refresh() {
535   base::win::ScopedVariant refresh_level(REFRESH_COMPLETELY);
536   web_browser2_->Refresh2(refresh_level.AsInput());
537 }
538 
539 // private methods
ConnectToChromeFrame()540 void IEEventSink::ConnectToChromeFrame() {
541   DCHECK(web_browser2_);
542   if (chrome_frame_.get())
543     return;
544   base::win::ScopedComPtr<IShellBrowser> shell_browser;
545   DoQueryService(SID_STopLevelBrowser, web_browser2_,
546                  shell_browser.Receive());
547 
548   if (shell_browser) {
549     base::win::ScopedComPtr<IShellView> shell_view;
550     shell_browser->QueryActiveShellView(shell_view.Receive());
551     if (shell_view) {
552       shell_view->GetItemObject(SVGIO_BACKGROUND, __uuidof(IChromeFrame),
553            reinterpret_cast<void**>(chrome_frame_.Receive()));
554     }
555 
556     if (chrome_frame_) {
557       base::win::ScopedVariant onmessage(onmessage_.ToDispatch());
558       base::win::ScopedVariant onloaderror(onloaderror_.ToDispatch());
559       base::win::ScopedVariant onload(onload_.ToDispatch());
560       EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onmessage(onmessage));
561       EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onloaderror(onloaderror));
562       EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onload(onload));
563     }
564   }
565 }
566 
DisconnectFromChromeFrame()567 void IEEventSink::DisconnectFromChromeFrame() {
568   if (chrome_frame_) {
569     // Use a local ref counted copy of the IChromeFrame interface as the
570     // outgoing calls could cause the interface to be deleted due to a message
571     // pump running in the context of the outgoing call.
572     base::win::ScopedComPtr<IChromeFrame> chrome_frame(chrome_frame_);
573     chrome_frame_.Release();
574     base::win::ScopedVariant dummy(static_cast<IDispatch*>(NULL));
575     chrome_frame->put_onmessage(dummy);
576     chrome_frame->put_onload(dummy);
577     chrome_frame->put_onloaderror(dummy);
578   }
579 }
580 
FindIEProcessId()581 void IEEventSink::FindIEProcessId() {
582   HWND hwnd = NULL;
583   web_browser2_->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&hwnd));
584   EXPECT_TRUE(::IsWindow(hwnd));
585   if (::IsWindow(hwnd))
586     ::GetWindowThreadProcessId(hwnd, &ie_process_id_);
587   EXPECT_NE(static_cast<DWORD>(0), ie_process_id_);
588 }
589 
590 // Event callbacks
STDMETHODIMP_(void)591 STDMETHODIMP_(void) IEEventSink::OnDownloadBegin() {
592   if (listener_)
593     listener_->OnDownloadBegin();
594 }
595 
STDMETHODIMP_(void)596 STDMETHODIMP_(void) IEEventSink::OnNewWindow2(IDispatch** dispatch,
597                                               VARIANT_BOOL* s) {
598   VLOG(1) << __FUNCTION__;
599 
600   EXPECT_TRUE(dispatch);
601   if (!dispatch)
602     return;
603 
604   if (listener_)
605     listener_->OnNewWindow2(dispatch, s);
606 
607   // Note that |dispatch| is an [in/out] argument. IE is asking listeners if
608   // they want to use a IWebBrowser2 of their choice for the new window.
609   // Since we need to listen on events on the new browser, we create one
610   // if needed.
611   if (!*dispatch) {
612     base::win::ScopedComPtr<IDispatch> new_browser;
613     HRESULT hr = new_browser.CreateInstance(CLSID_InternetExplorer, NULL,
614                                             CLSCTX_LOCAL_SERVER);
615     DCHECK(SUCCEEDED(hr) && new_browser);
616     *dispatch = new_browser.Detach();
617   }
618 
619   if (*dispatch && listener_)
620     listener_->OnNewBrowserWindow(*dispatch, ScopedBstr());
621 }
622 
STDMETHODIMP_(void)623 STDMETHODIMP_(void) IEEventSink::OnNavigateError(IDispatch* dispatch,
624     VARIANT* url, VARIANT* frame_name, VARIANT* status_code, VARIANT* cancel) {
625   VLOG(1) << __FUNCTION__;
626   if (listener_)
627     listener_->OnNavigateError(dispatch, url, frame_name, status_code, cancel);
628 }
629 
OnBeforeNavigate2(IDispatch * dispatch,VARIANT * url,VARIANT * flags,VARIANT * target_frame_name,VARIANT * post_data,VARIANT * headers,VARIANT_BOOL * cancel)630 STDMETHODIMP IEEventSink::OnBeforeNavigate2(
631     IDispatch* dispatch, VARIANT* url, VARIANT* flags,
632     VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers,
633     VARIANT_BOOL* cancel) {
634   VLOG(1) << __FUNCTION__ << " "
635           << base::StringPrintf("%ls - 0x%08X", url->bstrVal, this);
636   // Reset any existing reference to chrome frame since this is a new
637   // navigation.
638   DisconnectFromChromeFrame();
639   if (listener_)
640     listener_->OnBeforeNavigate2(dispatch, url, flags, target_frame_name,
641                                  post_data, headers, cancel);
642   return S_OK;
643 }
644 
STDMETHODIMP_(void)645 STDMETHODIMP_(void) IEEventSink::OnNavigateComplete2(
646     IDispatch* dispatch, VARIANT* url) {
647   VLOG(1) << __FUNCTION__;
648   ConnectToChromeFrame();
649   if (listener_)
650     listener_->OnNavigateComplete2(dispatch, url);
651 }
652 
STDMETHODIMP_(void)653 STDMETHODIMP_(void) IEEventSink::OnDocumentComplete(
654     IDispatch* dispatch, VARIANT* url) {
655   VLOG(1) << __FUNCTION__;
656   EXPECT_TRUE(url);
657   if (!url)
658     return;
659   if (listener_)
660     listener_->OnDocumentComplete(dispatch, url);
661 }
662 
STDMETHODIMP_(void)663 STDMETHODIMP_(void) IEEventSink::OnFileDownload(
664     VARIANT_BOOL active_doc, VARIANT_BOOL* cancel) {
665   VLOG(1) << __FUNCTION__ << " "
666           << base::StringPrintf(" 0x%08X ad=%i", this, active_doc);
667   if (listener_) {
668     listener_->OnFileDownload(active_doc, cancel);
669   } else {
670     *cancel = VARIANT_TRUE;
671   }
672 }
673 
STDMETHODIMP_(void)674 STDMETHODIMP_(void) IEEventSink::OnNewWindow3(
675     IDispatch** dispatch, VARIANT_BOOL* cancel, DWORD flags, BSTR url_context,
676     BSTR url) {
677   VLOG(1) << __FUNCTION__;
678   EXPECT_TRUE(dispatch);
679   if (!dispatch)
680     return;
681 
682   if (listener_)
683     listener_->OnNewWindow3(dispatch, cancel, flags, url_context, url);
684 
685   // Note that |dispatch| is an [in/out] argument. IE is asking listeners if
686   // they want to use a IWebBrowser2 of their choice for the new window.
687   // Since we need to listen on events on the new browser, we create one
688   // if needed.
689   if (!*dispatch) {
690     base::win::ScopedComPtr<IDispatch> new_browser;
691     HRESULT hr = new_browser.CreateInstance(CLSID_InternetExplorer, NULL,
692                                             CLSCTX_LOCAL_SERVER);
693     DCHECK(SUCCEEDED(hr) && new_browser);
694     *dispatch = new_browser.Detach();
695   }
696 
697   if (*dispatch && listener_)
698     listener_->OnNewBrowserWindow(*dispatch, url);
699 }
700 
STDMETHODIMP_(void)701 STDMETHODIMP_(void) IEEventSink::OnQuit() {
702   VLOG(1) << __FUNCTION__;
703 
704   did_receive_on_quit_ = true;
705 
706   DispEventUnadvise(web_browser2_);
707   CoDisconnectObject(this, 0);
708 
709   if (listener_)
710     listener_->OnQuit();
711 }
712 
Invoke(DISPID dispid,REFIID riid,LCID lcid,WORD flags,DISPPARAMS * params,VARIANT * result,EXCEPINFO * except_info,UINT * arg_error)713 STDMETHODIMP IEEventSink::Invoke(DISPID dispid, REFIID riid, LCID lcid,
714                                  WORD flags, DISPPARAMS* params,
715                                  VARIANT* result, EXCEPINFO* except_info,
716                                  UINT* arg_error) {
717   VLOG(1) << __FUNCTION__ << L" event: " << g_dispIdToName.Get().Lookup(dispid);
718   return DispEventsImpl::Invoke(dispid, riid, lcid, flags, params, result,
719                                 except_info, arg_error);
720 }
721 
OnLoad(const VARIANT * param)722 HRESULT IEEventSink::OnLoad(const VARIANT* param) {
723   VLOG(1) << __FUNCTION__ << " " << param->bstrVal;
724   base::win::ScopedVariant stack_object(*param);
725   if (chrome_frame_) {
726     if (listener_)
727       listener_->OnLoad(param->bstrVal);
728   } else {
729     LOG(WARNING) << "Invalid chrome frame pointer";
730   }
731   return S_OK;
732 }
733 
OnLoadError(const VARIANT * param)734 HRESULT IEEventSink::OnLoadError(const VARIANT* param) {
735   VLOG(1) << __FUNCTION__ << " " << param->bstrVal;
736   if (chrome_frame_) {
737     if (listener_)
738       listener_->OnLoadError(param->bstrVal);
739   } else {
740     LOG(WARNING) << "Invalid chrome frame pointer";
741   }
742   return S_OK;
743 }
744 
OnMessage(const VARIANT * param)745 HRESULT IEEventSink::OnMessage(const VARIANT* param) {
746   VLOG(1) << __FUNCTION__ << " " << param;
747   if (!chrome_frame_.get()) {
748     LOG(WARNING) << "Invalid chrome frame pointer";
749     return S_OK;
750   }
751 
752   base::win::ScopedVariant data, origin, source;
753   if (param && (V_VT(param) == VT_DISPATCH)) {
754     wchar_t* properties[] = { L"data", L"origin", L"source" };
755     const int prop_count = arraysize(properties);
756     DISPID ids[prop_count] = {0};
757 
758     HRESULT hr = param->pdispVal->GetIDsOfNames(IID_NULL, properties,
759         prop_count, LOCALE_SYSTEM_DEFAULT, ids);
760     if (SUCCEEDED(hr)) {
761       DISPPARAMS params = { 0 };
762       EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[0], IID_NULL,
763           LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
764           data.Receive(), NULL, NULL));
765       EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[1], IID_NULL,
766           LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
767           origin.Receive(), NULL, NULL));
768       EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[2], IID_NULL,
769           LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
770           source.Receive(), NULL, NULL));
771     }
772   }
773 
774   if (listener_)
775     listener_->OnMessage(V_BSTR(&data), V_BSTR(&origin), V_BSTR(&source));
776   return S_OK;
777 }
778 
779 }  // namespace chrome_frame_test
780