C++程序  |  277行  |  10.01 KB

// 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.

#ifndef CHROME_FRAME_TEST_IE_EVENT_SINK_H_
#define CHROME_FRAME_TEST_IE_EVENT_SINK_H_

#include <atlbase.h>
#include <atlwin.h>
#include <exdispid.h>
#include <string>

#include "base/win/scoped_comptr.h"
#include "chrome_frame/chrome_tab.h"
#include "chrome_frame/test/simulate_input.h"
#include "chrome_frame/test_utils.h"

namespace chrome_frame_test {

// Listener for all events from the IEEventSink, defined below. This includes
// IE and CF events. Unfortunately some of these events are unreliable or have
// strange behavior across different platforms/browsers. See notes besides
// each method.
class IEEventListener {
 public:
  virtual ~IEEventListener() {}

  // IE callbacks
  virtual void OnNavigateError(IDispatch* dispatch, VARIANT* url,
                               VARIANT* frame_name, VARIANT* status_code,
                               VARIANT* cancel) {}
  // This does not occur in IE 6 in CF when navigating between fragments
  // on the same page, although it does occur with back/forward across such.
  virtual void OnBeforeNavigate2(IDispatch* dispatch, VARIANT* url,
                                 VARIANT* flags, VARIANT* target_frame_name,
                                 VARIANT* post_data, VARIANT* headers,
                                 VARIANT_BOOL* cancel) {}
  virtual void OnDownloadBegin() {}
  virtual void OnNavigateComplete2(IDispatch* dispatch, VARIANT* url) {}
  virtual void OnNewWindow2(IDispatch** dispatch, VARIANT_BOOL* cancel) {}
  virtual void OnNewWindow3(IDispatch** dispatch, VARIANT_BOOL* cancel,
                            DWORD flags, BSTR url_context, BSTR url) {}
  // This occurs twice on IE >= 7 after window.open calls.
  virtual void OnDocumentComplete(IDispatch* dispatch, VARIANT* url_variant) {}
  virtual void OnFileDownload(VARIANT_BOOL active_doc, VARIANT_BOOL* cancel) {}
  virtual void OnQuit() {}

  // CF callbacks
  virtual void OnLoad(const wchar_t* url) {}
  virtual void OnLoadError(const wchar_t* url) {}
  virtual void OnMessage(const wchar_t* message, const wchar_t* origin,
                         const wchar_t* source) {}
  virtual void OnNewBrowserWindow(IDispatch* new_window, const wchar_t* url) {}
};

// Listener for IPropertyNotifySink.
class PropertyNotifySinkListener {
 public:
  virtual ~PropertyNotifySinkListener() {}
  virtual void OnChanged(DISPID dispid) {}
  virtual void OnRequestEdit(DISPID dispid) {}
};

// This class sets up event sinks to the IWebBrowser interface. It forwards
// all events to its listener.
class IEEventSink
    : public CComObjectRootEx<CComSingleThreadModel>,
      public IDispEventSimpleImpl<0, IEEventSink,
                                  &DIID_DWebBrowserEvents2>,
      public IUnknown {
 public:
  typedef IDispEventSimpleImpl<0, IEEventSink,
                               &DIID_DWebBrowserEvents2> DispEventsImpl;
  IEEventSink();
  ~IEEventSink();

  // Launches IE, sets up the sink to forward events to the listener, and
  // navigates to the given page.
  HRESULT LaunchIEAndNavigate(const std::wstring& navigate_url,
                              IEEventListener* listener);

  // Navigate to the given url.
  HRESULT Navigate(const std::wstring& navigate_url);

  // Listen to events from this |browser_disp|, which should be queryable for
  // IWebBrowser2.
  void Attach(IDispatch* browser_disp);

  // Listen to events from the given browser.
  HRESULT Attach(IWebBrowser2* browser);

  // Stop listening to the associated web browser and possibly wait for it to
  // close, if this browser has its own process.
  void Uninitialize();

  // Closes the web browser in such a way that the OnQuit notification will
  // be fired when the window closes (async).
  HRESULT CloseWebBrowser();

  // Posts a message to the given target in ChromeFrame. |target| may be "*".
  void PostMessageToCF(const std::wstring& message, const std::wstring& target);

    // Set input focus to chrome frame window.
  void SetFocusToRenderer();

  // Send keyboard input to the renderer window hosted in chrome using direct
  // key down/up messages.
  void SendKeys(const char* input_string);

  // Send mouse click to the renderer window hosted in chrome using
  // SendInput API.
  void SendMouseClick(int x, int y, simulate_input::MouseButton button);

  // Get the HWND for the browser's main window. Will fail test if window
  // not found.
  HWND GetBrowserWindow();

  // Get the HWND for the browser's renderer window. Will fail test if
  // renderer window not found.
  HWND GetRendererWindow();

  // Same as above, but does not fail the test if the window cannot be found.
  // In that case, the returned handle will be NULL.
  HWND GetRendererWindowSafe();

  // Returns whether CF is rendering the current page.
  bool IsCFRendering();

  // Expect the renderer window to have focus.
  void ExpectRendererWindowHasFocus();

  // Expect the address bar to have |url|.
  void ExpectAddressBarUrl(const std::wstring& url);

  // These methods are just simple wrappers of the IWebBrowser2 methods.
  // They are needed because you cannot post tasks to IWebBrowser2.
  void GoBack() {
    web_browser2_->GoBack();
  }

  void GoForward() {
    web_browser2_->GoForward();
  }

  void Refresh();

  void Exec(const GUID* cmd_group_guid, DWORD command_id,
            DWORD cmd_exec_opt, VARIANT* in_args, VARIANT* out_args);

  // Set the listener for this sink, which can be NULL.
  void set_listener(IEEventListener* listener) { listener_ = listener; }

  IWebBrowser2* web_browser2() { return web_browser2_.get(); }

  // Used only for debugging/logging purposes.
  bool reference_count() { return m_dwRef; }

  static void SetAbnormalShutdown(bool abnormal_shutdown);

 private:
  void ConnectToChromeFrame();
  void DisconnectFromChromeFrame();
  void FindIEProcessId();

  // IE callbacks.
BEGIN_COM_MAP(IEEventSink)
  COM_INTERFACE_ENTRY(IUnknown)
END_COM_MAP()

BEGIN_SINK_MAP(IEEventSink)
  SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_BEFORENAVIGATE2,
                  OnBeforeNavigate2, &kBeforeNavigate2Info)
  SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_DOWNLOADBEGIN,
                  OnDownloadBegin, &kVoidMethodInfo)
  SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATECOMPLETE2,
                  OnNavigateComplete2, &kNavigateComplete2Info)
  SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NAVIGATEERROR,
                  OnNavigateError, &kNavigateErrorInfo)
  SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW2,
                  OnNewWindow2, &kNewWindow2Info)
  SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_NEWWINDOW3,
                  OnNewWindow3, &kNewWindow3Info)
  SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_DOCUMENTCOMPLETE,
                  OnDocumentComplete, &kDocumentCompleteInfo)
  SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_FILEDOWNLOAD,
                  OnFileDownload, &kFileDownloadInfo)
  SINK_ENTRY_INFO(0, DIID_DWebBrowserEvents2, DISPID_ONQUIT,
                  OnQuit, &kVoidMethodInfo)
END_SINK_MAP()

  STDMETHOD_(void, OnNavigateError)(IDispatch* dispatch, VARIANT* url,
                                    VARIANT* frame_name, VARIANT* status_code,
                                    VARIANT* cancel);
  STDMETHOD(OnBeforeNavigate2)(IDispatch* dispatch, VARIANT* url,
                               VARIANT* flags, VARIANT* target_frame_name,
                               VARIANT* post_data, VARIANT* headers,
                               VARIANT_BOOL* cancel);
  STDMETHOD_(void, OnDownloadBegin)();
  STDMETHOD_(void, OnNavigateComplete2)(IDispatch* dispatch, VARIANT* url);
  STDMETHOD_(void, OnNewWindow2)(IDispatch** dispatch, VARIANT_BOOL* cancel);
  STDMETHOD_(void, OnNewWindow3)(IDispatch** dispatch, VARIANT_BOOL* cancel,
                                 DWORD flags, BSTR url_context, BSTR url);
  STDMETHOD_(void, OnDocumentComplete)(IDispatch* dispatch,
                                       VARIANT* url_variant);
  STDMETHOD_(void, OnFileDownload)(VARIANT_BOOL active_doc,
                                   VARIANT_BOOL* cancel);
  STDMETHOD_(void, OnQuit)();

  STDMETHOD(Invoke)(DISPID dispid,
                    REFIID riid, LCID lcid,
                    WORD flags,
                    DISPPARAMS* params,
                    VARIANT* result,
                    EXCEPINFO* except_info,
                    UINT* arg_error);

  // IChromeFrame callbacks
  HRESULT OnLoad(const VARIANT* param);
  HRESULT OnLoadError(const VARIANT* param);
  HRESULT OnMessage(const VARIANT* param);

  base::win::ScopedComPtr<IWebBrowser2> web_browser2_;
  base::win::ScopedComPtr<IChromeFrame> chrome_frame_;
  DispCallback<IEEventSink> onmessage_;
  DispCallback<IEEventSink> onloaderror_;
  DispCallback<IEEventSink> onload_;
  IEEventListener* listener_;
  base::ProcessId ie_process_id_;
  bool did_receive_on_quit_;
  static bool abnormal_shutdown_;

  static _ATL_FUNC_INFO kBeforeNavigate2Info;
  static _ATL_FUNC_INFO kNavigateComplete2Info;
  static _ATL_FUNC_INFO kNavigateErrorInfo;
  static _ATL_FUNC_INFO kNewWindow2Info;
  static _ATL_FUNC_INFO kNewWindow3Info;
  static _ATL_FUNC_INFO kVoidMethodInfo;
  static _ATL_FUNC_INFO kDocumentCompleteInfo;
  static _ATL_FUNC_INFO kFileDownloadInfo;
};

class PropertyNotifySinkImpl
    : public CComObjectRootEx<CComSingleThreadModel>,
      public IPropertyNotifySink {
 public:
  PropertyNotifySinkImpl() : listener_(NULL) {
  }

BEGIN_COM_MAP(PropertyNotifySinkImpl)
  COM_INTERFACE_ENTRY(IPropertyNotifySink)
END_COM_MAP()

  STDMETHOD(OnChanged)(DISPID dispid) {
    if (listener_)
      listener_->OnChanged(dispid);
    return S_OK;
  }

  STDMETHOD(OnRequestEdit)(DISPID dispid) {
    if (listener_)
      listener_->OnRequestEdit(dispid);
    return S_OK;
  }

  void set_listener(PropertyNotifySinkListener* listener) {
    DCHECK(listener_ == NULL || listener == NULL);
    listener_ = listener;
  }

 protected:
  PropertyNotifySinkListener* listener_;
};

}  // namespace chrome_frame_test

#endif  // CHROME_FRAME_TEST_IE_EVENT_SINK_H_