Home | History | Annotate | Download | only in test
      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 
     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 
    122 DispIdNameTable::~DispIdNameTable() {
    123 }
    124 
    125 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 
    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 
    215 IEEventSink::~IEEventSink() {
    216   Uninitialize();
    217 }
    218 
    219 void IEEventSink::SetAbnormalShutdown(bool abnormal_shutdown) {
    220   abnormal_shutdown_ = abnormal_shutdown;
    221 }
    222 
    223 // IEEventSink member defines
    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 
    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 
    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 
    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 
    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 
    314 void IEEventSink::SetFocusToRenderer() {
    315   simulate_input::SetKeyboardFocusToWindow(GetRendererWindow());
    316 }
    317 
    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 
    352 void IEEventSink::SendMouseClick(int x, int y,
    353                                  simulate_input::MouseButton button) {
    354   simulate_input::SendMouseClick(GetRendererWindow(), x, y, button);
    355 }
    356 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    534 void IEEventSink::Refresh() {
    535   base::win::ScopedVariant refresh_level(REFRESH_COMPLETELY);
    536   web_browser2_->Refresh2(refresh_level.AsInput());
    537 }
    538 
    539 // private methods
    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 
    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 
    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
    591 STDMETHODIMP_(void) IEEventSink::OnDownloadBegin() {
    592   if (listener_)
    593     listener_->OnDownloadBegin();
    594 }
    595 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    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