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   DCHECK(browser);
    234   HRESULT result;
    235   if (browser) {
    236     web_browser2_ = browser;
    237     FindIEProcessId();
    238     result = DispEventAdvise(web_browser2_, &DIID_DWebBrowserEvents2);
    239   }
    240   return result;
    241 }
    242 
    243 void IEEventSink::Uninitialize() {
    244   if (!abnormal_shutdown_) {
    245     DisconnectFromChromeFrame();
    246     if (web_browser2_.get()) {
    247       if (m_dwEventCookie != 0xFEFEFEFE) {
    248         DispEventUnadvise(web_browser2_);
    249         CoDisconnectObject(this, 0);
    250       }
    251 
    252       if (!did_receive_on_quit_) {
    253         // Log the browser window url for debugging purposes.
    254         ScopedBstr browser_url;
    255         web_browser2_->get_LocationURL(browser_url.Receive());
    256         std::wstring browser_url_wstring;
    257         browser_url_wstring.assign(browser_url, browser_url.Length());
    258         std::string browser_url_string = WideToUTF8(browser_url_wstring);
    259         LOG(ERROR) << "OnQuit was not received for browser with url "
    260                    << browser_url_string;
    261         web_browser2_->Quit();
    262       }
    263 
    264       base::win::ScopedHandle process;
    265       process.Set(OpenProcess(SYNCHRONIZE, FALSE, ie_process_id_));
    266       web_browser2_.Release();
    267 
    268       if (!process.IsValid()) {
    269         LOG_IF(WARNING, !process.IsValid())
    270             << base::StringPrintf("OpenProcess failed: %i", ::GetLastError());
    271         return;
    272       }
    273       // IE may not have closed yet. Wait here for the process to finish.
    274       // This is necessary at least on some browser/platform configurations.
    275       WaitForSingleObject(process, kDefaultWaitForIEToTerminateMs);
    276     }
    277   } else {
    278     LOG(ERROR) << "Terminating hung IE process";
    279   }
    280   chrome_frame_test::KillProcesses(chrome_frame_test::kIEImageName, 0,
    281                                    !abnormal_shutdown_);
    282   chrome_frame_test::KillProcesses(chrome_frame_test::kIEBrokerImageName, 0,
    283                                    !abnormal_shutdown_);
    284 }
    285 
    286 bool IEEventSink::IsCFRendering() {
    287   DCHECK(web_browser2_);
    288 
    289   if (web_browser2_) {
    290     base::win::ScopedComPtr<IDispatch> doc;
    291     web_browser2_->get_Document(doc.Receive());
    292     if (doc) {
    293       // Detect if CF is rendering based on whether the document is a
    294       // ChromeActiveDocument. Detecting based on hwnd is problematic as
    295       // the CF Active Document window may not have been created yet.
    296       base::win::ScopedComPtr<IChromeFrame> chrome_frame;
    297       chrome_frame.QueryFrom(doc);
    298       return chrome_frame.get();
    299     }
    300   }
    301   return false;
    302 }
    303 
    304 void IEEventSink::PostMessageToCF(const std::wstring& message,
    305                                   const std::wstring& target) {
    306   EXPECT_TRUE(chrome_frame_ != NULL);
    307   if (!chrome_frame_)
    308     return;
    309   ScopedBstr message_bstr(message.c_str());
    310   base::win::ScopedVariant target_variant(target.c_str());
    311   EXPECT_HRESULT_SUCCEEDED(
    312       chrome_frame_->postMessage(message_bstr, target_variant));
    313 }
    314 
    315 void IEEventSink::SetFocusToRenderer() {
    316   simulate_input::SetKeyboardFocusToWindow(GetRendererWindow());
    317 }
    318 
    319 void IEEventSink::SendKeys(const char* input_string) {
    320   HWND window = GetRendererWindow();
    321   simulate_input::SetKeyboardFocusToWindow(window);
    322   const base::TimeDelta kMessageSleep = TestTimeouts::tiny_timeout();
    323   const base::StringPiece codes(input_string);
    324   for (size_t i = 0; i < codes.length(); ++i) {
    325     char character = codes[i];
    326     UINT virtual_key = 0;
    327 
    328     if (character >= 'a' && character <= 'z') {
    329       // VK_A - VK_Z are ASCII 'A' - 'Z'.
    330       virtual_key = 'A' + (character - 'a');
    331     } else if (character >= '0' && character <= '9') {
    332       // VK_0 - VK_9 are ASCII '0' - '9'.
    333       virtual_key = character;
    334     } else {
    335       FAIL() << "Character value out of range at position " << i
    336              << " of string \"" << input_string << "\"";
    337     }
    338 
    339     UINT scan_code = MapVirtualKey(virtual_key, MAPVK_VK_TO_VSC);
    340     EXPECT_NE(0U, scan_code) << "No translation for virtual key "
    341                              << virtual_key << " for character at position "
    342                              << i << " of string \"" << input_string << "\"";
    343 
    344     ::PostMessage(window, WM_KEYDOWN,
    345                   virtual_key, MAKELPARAM(1, scan_code));
    346     base::PlatformThread::Sleep(kMessageSleep);
    347     ::PostMessage(window, WM_KEYUP,
    348                   virtual_key, MAKELPARAM(1, scan_code | KF_UP | KF_REPEAT));
    349     base::PlatformThread::Sleep(kMessageSleep);
    350   }
    351 }
    352 
    353 void IEEventSink::SendMouseClick(int x, int y,
    354                                  simulate_input::MouseButton button) {
    355   simulate_input::SendMouseClick(GetRendererWindow(), x, y, button);
    356 }
    357 
    358 void IEEventSink::ExpectRendererWindowHasFocus() {
    359   HWND renderer_window = GetRendererWindow();
    360   EXPECT_TRUE(IsWindow(renderer_window));
    361 
    362   DWORD renderer_thread = 0;
    363   DWORD renderer_process = 0;
    364   renderer_thread = GetWindowThreadProcessId(renderer_window,
    365                                              &renderer_process);
    366 
    367   ASSERT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, TRUE));
    368   HWND focus_window = GetFocus();
    369   EXPECT_EQ(renderer_window, focus_window);
    370   EXPECT_TRUE(AttachThreadInput(GetCurrentThreadId(), renderer_thread, FALSE));
    371 }
    372 
    373 void IEEventSink::ExpectAddressBarUrl(
    374     const std::wstring& expected_url) {
    375   DCHECK(web_browser2_);
    376   if (web_browser2_) {
    377     ScopedBstr address_bar_url;
    378     EXPECT_EQ(S_OK, web_browser2_->get_LocationURL(address_bar_url.Receive()));
    379     EXPECT_EQ(expected_url, std::wstring(address_bar_url));
    380   }
    381 }
    382 
    383 void IEEventSink::Exec(const GUID* cmd_group_guid, DWORD command_id,
    384                                DWORD cmd_exec_opt, VARIANT* in_args,
    385                                VARIANT* out_args) {
    386   base::win::ScopedComPtr<IOleCommandTarget> shell_browser_cmd_target;
    387   DoQueryService(SID_STopLevelBrowser, web_browser2_,
    388                  shell_browser_cmd_target.Receive());
    389   ASSERT_TRUE(NULL != shell_browser_cmd_target);
    390   EXPECT_HRESULT_SUCCEEDED(shell_browser_cmd_target->Exec(cmd_group_guid,
    391       command_id, cmd_exec_opt, in_args, out_args));
    392 }
    393 
    394 HWND IEEventSink::GetBrowserWindow() {
    395   HWND browser_window = NULL;
    396   web_browser2_->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&browser_window));
    397   EXPECT_TRUE(::IsWindow(browser_window));
    398   return browser_window;
    399 }
    400 
    401 HWND IEEventSink::GetRendererWindow() {
    402   HWND renderer_window = NULL;
    403   if (IsCFRendering()) {
    404     DCHECK(chrome_frame_);
    405     base::win::ScopedComPtr<IOleWindow> ole_window;
    406     ole_window.QueryFrom(chrome_frame_);
    407     EXPECT_TRUE(ole_window.get());
    408 
    409     if (ole_window) {
    410       HWND activex_window = NULL;
    411       ole_window->GetWindow(&activex_window);
    412       EXPECT_TRUE(IsWindow(activex_window));
    413 
    414       wchar_t class_name[MAX_PATH] = {0};
    415       HWND child_window = NULL;
    416       // chrome tab window is the first (and the only) child of activex
    417       for (HWND first_child = activex_window; ::IsWindow(first_child);
    418            first_child = ::GetWindow(first_child, GW_CHILD)) {
    419         child_window = first_child;
    420         GetClassName(child_window, class_name, arraysize(class_name));
    421 #if defined(USE_AURA)
    422         static const wchar_t kWndClassPrefix[] = L"Chrome_WidgetWin_";
    423 #else
    424         static const wchar_t kWndClassPrefix[] = L"Chrome_RenderWidgetHostHWND";
    425 #endif
    426         if (!_wcsnicmp(class_name, kWndClassPrefix, wcslen(kWndClassPrefix))) {
    427           renderer_window = child_window;
    428           break;
    429         }
    430       }
    431     }
    432   } else {
    433     DCHECK(web_browser2_);
    434     base::win::ScopedComPtr<IDispatch> doc;
    435     HRESULT hr = web_browser2_->get_Document(doc.Receive());
    436     EXPECT_HRESULT_SUCCEEDED(hr);
    437     EXPECT_TRUE(doc);
    438     if (doc) {
    439       base::win::ScopedComPtr<IOleWindow> ole_window;
    440       ole_window.QueryFrom(doc);
    441       EXPECT_TRUE(ole_window);
    442       if (ole_window) {
    443         ole_window->GetWindow(&renderer_window);
    444       }
    445     }
    446   }
    447 
    448   EXPECT_TRUE(::IsWindow(renderer_window));
    449   return renderer_window;
    450 }
    451 
    452 HWND IEEventSink::GetRendererWindowSafe() {
    453   HWND renderer_window = NULL;
    454   if (IsCFRendering()) {
    455     DCHECK(chrome_frame_);
    456     base::win::ScopedComPtr<IOleWindow> ole_window;
    457     ole_window.QueryFrom(chrome_frame_);
    458 
    459     if (ole_window) {
    460       HWND activex_window = NULL;
    461       ole_window->GetWindow(&activex_window);
    462 
    463       // chrome tab window is the first (and the only) child of activex
    464       for (HWND first_child = activex_window; ::IsWindow(first_child);
    465            first_child = ::GetWindow(first_child, GW_CHILD)) {
    466         renderer_window = first_child;
    467       }
    468       wchar_t class_name[MAX_PATH] = {0};
    469       GetClassName(renderer_window, class_name, arraysize(class_name));
    470       if (_wcsicmp(class_name, L"Chrome_RenderWidgetHostHWND") != 0)
    471         renderer_window = NULL;
    472     }
    473   } else {
    474     DCHECK(web_browser2_);
    475     base::win::ScopedComPtr<IDispatch> doc;
    476     web_browser2_->get_Document(doc.Receive());
    477     if (doc) {
    478       base::win::ScopedComPtr<IOleWindow> ole_window;
    479       ole_window.QueryFrom(doc);
    480       if (ole_window) {
    481         ole_window->GetWindow(&renderer_window);
    482       }
    483     }
    484   }
    485   if (!::IsWindow(renderer_window))
    486     renderer_window = NULL;
    487   return renderer_window;
    488 }
    489 
    490 HRESULT IEEventSink::LaunchIEAndNavigate(const std::wstring& navigate_url,
    491                                          IEEventListener* listener) {
    492   listener_ = listener;
    493   HRESULT hr = LaunchIEAsComServer(web_browser2_.Receive());
    494   if (SUCCEEDED(hr)) {
    495     web_browser2_->put_Visible(VARIANT_TRUE);
    496     hr = Attach(web_browser2_);
    497     if (SUCCEEDED(hr)) {
    498       hr = Navigate(navigate_url);
    499       if (FAILED(hr)) {
    500         LOG(ERROR) << "Failed to navigate IE to " << navigate_url << ", hr = 0x"
    501                    << std::hex << hr;
    502       }
    503     } else {
    504       LOG(ERROR) << "Failed to attach to web browser event sink for "
    505                  << navigate_url << ", hr = 0x" << std::hex << hr;
    506     }
    507   } else {
    508     LOG(ERROR) << "Failed to Launch IE for " << navigate_url << ", hr = 0x"
    509                << std::hex << hr;
    510   }
    511 
    512   return hr;
    513 }
    514 
    515 HRESULT IEEventSink::Navigate(const std::wstring& navigate_url) {
    516   VARIANT empty = base::win::ScopedVariant::kEmptyVariant;
    517   base::win::ScopedVariant url;
    518   url.Set(navigate_url.c_str());
    519 
    520   HRESULT hr = S_OK;
    521   hr = web_browser2_->Navigate2(url.AsInput(), &empty, &empty, &empty, &empty);
    522   EXPECT_TRUE(hr == S_OK);
    523   return hr;
    524 }
    525 
    526 HRESULT IEEventSink::CloseWebBrowser() {
    527   if (!web_browser2_)
    528     return E_FAIL;
    529 
    530   DisconnectFromChromeFrame();
    531   EXPECT_HRESULT_SUCCEEDED(web_browser2_->Quit());
    532   return S_OK;
    533 }
    534 
    535 void IEEventSink::Refresh() {
    536   base::win::ScopedVariant refresh_level(REFRESH_COMPLETELY);
    537   web_browser2_->Refresh2(refresh_level.AsInput());
    538 }
    539 
    540 // private methods
    541 void IEEventSink::ConnectToChromeFrame() {
    542   DCHECK(web_browser2_);
    543   if (chrome_frame_.get())
    544     return;
    545   base::win::ScopedComPtr<IShellBrowser> shell_browser;
    546   DoQueryService(SID_STopLevelBrowser, web_browser2_,
    547                  shell_browser.Receive());
    548 
    549   if (shell_browser) {
    550     base::win::ScopedComPtr<IShellView> shell_view;
    551     shell_browser->QueryActiveShellView(shell_view.Receive());
    552     if (shell_view) {
    553       shell_view->GetItemObject(SVGIO_BACKGROUND, __uuidof(IChromeFrame),
    554            reinterpret_cast<void**>(chrome_frame_.Receive()));
    555     }
    556 
    557     if (chrome_frame_) {
    558       base::win::ScopedVariant onmessage(onmessage_.ToDispatch());
    559       base::win::ScopedVariant onloaderror(onloaderror_.ToDispatch());
    560       base::win::ScopedVariant onload(onload_.ToDispatch());
    561       EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onmessage(onmessage));
    562       EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onloaderror(onloaderror));
    563       EXPECT_HRESULT_SUCCEEDED(chrome_frame_->put_onload(onload));
    564     }
    565   }
    566 }
    567 
    568 void IEEventSink::DisconnectFromChromeFrame() {
    569   if (chrome_frame_) {
    570     // Use a local ref counted copy of the IChromeFrame interface as the
    571     // outgoing calls could cause the interface to be deleted due to a message
    572     // pump running in the context of the outgoing call.
    573     base::win::ScopedComPtr<IChromeFrame> chrome_frame(chrome_frame_);
    574     chrome_frame_.Release();
    575     base::win::ScopedVariant dummy(static_cast<IDispatch*>(NULL));
    576     chrome_frame->put_onmessage(dummy);
    577     chrome_frame->put_onload(dummy);
    578     chrome_frame->put_onloaderror(dummy);
    579   }
    580 }
    581 
    582 void IEEventSink::FindIEProcessId() {
    583   HWND hwnd = NULL;
    584   web_browser2_->get_HWND(reinterpret_cast<SHANDLE_PTR*>(&hwnd));
    585   EXPECT_TRUE(::IsWindow(hwnd));
    586   if (::IsWindow(hwnd))
    587     ::GetWindowThreadProcessId(hwnd, &ie_process_id_);
    588   EXPECT_NE(static_cast<DWORD>(0), ie_process_id_);
    589 }
    590 
    591 // Event callbacks
    592 STDMETHODIMP_(void) IEEventSink::OnDownloadBegin() {
    593   if (listener_)
    594     listener_->OnDownloadBegin();
    595 }
    596 
    597 STDMETHODIMP_(void) IEEventSink::OnNewWindow2(IDispatch** dispatch,
    598                                               VARIANT_BOOL* s) {
    599   VLOG(1) << __FUNCTION__;
    600 
    601   EXPECT_TRUE(dispatch);
    602   if (!dispatch)
    603     return;
    604 
    605   if (listener_)
    606     listener_->OnNewWindow2(dispatch, s);
    607 
    608   // Note that |dispatch| is an [in/out] argument. IE is asking listeners if
    609   // they want to use a IWebBrowser2 of their choice for the new window.
    610   // Since we need to listen on events on the new browser, we create one
    611   // if needed.
    612   if (!*dispatch) {
    613     base::win::ScopedComPtr<IDispatch> new_browser;
    614     HRESULT hr = new_browser.CreateInstance(CLSID_InternetExplorer, NULL,
    615                                             CLSCTX_LOCAL_SERVER);
    616     DCHECK(SUCCEEDED(hr) && new_browser);
    617     *dispatch = new_browser.Detach();
    618   }
    619 
    620   if (*dispatch && listener_)
    621     listener_->OnNewBrowserWindow(*dispatch, ScopedBstr());
    622 }
    623 
    624 STDMETHODIMP_(void) IEEventSink::OnNavigateError(IDispatch* dispatch,
    625     VARIANT* url, VARIANT* frame_name, VARIANT* status_code, VARIANT* cancel) {
    626   VLOG(1) << __FUNCTION__;
    627   if (listener_)
    628     listener_->OnNavigateError(dispatch, url, frame_name, status_code, cancel);
    629 }
    630 
    631 STDMETHODIMP IEEventSink::OnBeforeNavigate2(
    632     IDispatch* dispatch, VARIANT* url, VARIANT* flags,
    633     VARIANT* target_frame_name, VARIANT* post_data, VARIANT* headers,
    634     VARIANT_BOOL* cancel) {
    635   VLOG(1) << __FUNCTION__ << " "
    636           << base::StringPrintf("%ls - 0x%08X", url->bstrVal, this);
    637   // Reset any existing reference to chrome frame since this is a new
    638   // navigation.
    639   DisconnectFromChromeFrame();
    640   if (listener_)
    641     listener_->OnBeforeNavigate2(dispatch, url, flags, target_frame_name,
    642                                  post_data, headers, cancel);
    643   return S_OK;
    644 }
    645 
    646 STDMETHODIMP_(void) IEEventSink::OnNavigateComplete2(
    647     IDispatch* dispatch, VARIANT* url) {
    648   VLOG(1) << __FUNCTION__;
    649   ConnectToChromeFrame();
    650   if (listener_)
    651     listener_->OnNavigateComplete2(dispatch, url);
    652 }
    653 
    654 STDMETHODIMP_(void) IEEventSink::OnDocumentComplete(
    655     IDispatch* dispatch, VARIANT* url) {
    656   VLOG(1) << __FUNCTION__;
    657   EXPECT_TRUE(url);
    658   if (!url)
    659     return;
    660   if (listener_)
    661     listener_->OnDocumentComplete(dispatch, url);
    662 }
    663 
    664 STDMETHODIMP_(void) IEEventSink::OnFileDownload(
    665     VARIANT_BOOL active_doc, VARIANT_BOOL* cancel) {
    666   VLOG(1) << __FUNCTION__ << " "
    667           << base::StringPrintf(" 0x%08X ad=%i", this, active_doc);
    668   if (listener_) {
    669     listener_->OnFileDownload(active_doc, cancel);
    670   } else {
    671     *cancel = VARIANT_TRUE;
    672   }
    673 }
    674 
    675 STDMETHODIMP_(void) IEEventSink::OnNewWindow3(
    676     IDispatch** dispatch, VARIANT_BOOL* cancel, DWORD flags, BSTR url_context,
    677     BSTR url) {
    678   VLOG(1) << __FUNCTION__;
    679   EXPECT_TRUE(dispatch);
    680   if (!dispatch)
    681     return;
    682 
    683   if (listener_)
    684     listener_->OnNewWindow3(dispatch, cancel, flags, url_context, url);
    685 
    686   // Note that |dispatch| is an [in/out] argument. IE is asking listeners if
    687   // they want to use a IWebBrowser2 of their choice for the new window.
    688   // Since we need to listen on events on the new browser, we create one
    689   // if needed.
    690   if (!*dispatch) {
    691     base::win::ScopedComPtr<IDispatch> new_browser;
    692     HRESULT hr = new_browser.CreateInstance(CLSID_InternetExplorer, NULL,
    693                                             CLSCTX_LOCAL_SERVER);
    694     DCHECK(SUCCEEDED(hr) && new_browser);
    695     *dispatch = new_browser.Detach();
    696   }
    697 
    698   if (*dispatch && listener_)
    699     listener_->OnNewBrowserWindow(*dispatch, url);
    700 }
    701 
    702 STDMETHODIMP_(void) IEEventSink::OnQuit() {
    703   VLOG(1) << __FUNCTION__;
    704 
    705   did_receive_on_quit_ = true;
    706 
    707   DispEventUnadvise(web_browser2_);
    708   CoDisconnectObject(this, 0);
    709 
    710   if (listener_)
    711     listener_->OnQuit();
    712 }
    713 
    714 STDMETHODIMP IEEventSink::Invoke(DISPID dispid, REFIID riid, LCID lcid,
    715                                  WORD flags, DISPPARAMS* params,
    716                                  VARIANT* result, EXCEPINFO* except_info,
    717                                  UINT* arg_error) {
    718   VLOG(1) << __FUNCTION__ << L" event: " << g_dispIdToName.Get().Lookup(dispid);
    719   return DispEventsImpl::Invoke(dispid, riid, lcid, flags, params, result,
    720                                 except_info, arg_error);
    721 }
    722 
    723 HRESULT IEEventSink::OnLoad(const VARIANT* param) {
    724   VLOG(1) << __FUNCTION__ << " " << param->bstrVal;
    725   base::win::ScopedVariant stack_object(*param);
    726   if (chrome_frame_) {
    727     if (listener_)
    728       listener_->OnLoad(param->bstrVal);
    729   } else {
    730     LOG(WARNING) << "Invalid chrome frame pointer";
    731   }
    732   return S_OK;
    733 }
    734 
    735 HRESULT IEEventSink::OnLoadError(const VARIANT* param) {
    736   VLOG(1) << __FUNCTION__ << " " << param->bstrVal;
    737   if (chrome_frame_) {
    738     if (listener_)
    739       listener_->OnLoadError(param->bstrVal);
    740   } else {
    741     LOG(WARNING) << "Invalid chrome frame pointer";
    742   }
    743   return S_OK;
    744 }
    745 
    746 HRESULT IEEventSink::OnMessage(const VARIANT* param) {
    747   VLOG(1) << __FUNCTION__ << " " << param;
    748   if (!chrome_frame_.get()) {
    749     LOG(WARNING) << "Invalid chrome frame pointer";
    750     return S_OK;
    751   }
    752 
    753   base::win::ScopedVariant data, origin, source;
    754   if (param && (V_VT(param) == VT_DISPATCH)) {
    755     wchar_t* properties[] = { L"data", L"origin", L"source" };
    756     const int prop_count = arraysize(properties);
    757     DISPID ids[prop_count] = {0};
    758 
    759     HRESULT hr = param->pdispVal->GetIDsOfNames(IID_NULL, properties,
    760         prop_count, LOCALE_SYSTEM_DEFAULT, ids);
    761     if (SUCCEEDED(hr)) {
    762       DISPPARAMS params = { 0 };
    763       EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[0], IID_NULL,
    764           LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
    765           data.Receive(), NULL, NULL));
    766       EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[1], IID_NULL,
    767           LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
    768           origin.Receive(), NULL, NULL));
    769       EXPECT_HRESULT_SUCCEEDED(param->pdispVal->Invoke(ids[2], IID_NULL,
    770           LOCALE_SYSTEM_DEFAULT, DISPATCH_PROPERTYGET, &params,
    771           source.Receive(), NULL, NULL));
    772     }
    773   }
    774 
    775   if (listener_)
    776     listener_->OnMessage(V_BSTR(&data), V_BSTR(&origin), V_BSTR(&source));
    777   return S_OK;
    778 }
    779 
    780 }  // namespace chrome_frame_test
    781