C++程序  |  519行  |  16.42 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_CHROME_FRAME_AUTOMATION_H_
#define CHROME_FRAME_CHROME_FRAME_AUTOMATION_H_

#include <atlbase.h>
#include <atlwin.h>
#include <map>
#include <string>
#include <vector>

#include "base/containers/stack_container.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_handle.h"
#include "base/synchronization/lock.h"
#include "base/threading/thread.h"
#include "base/timer/timer.h"
#include "chrome/test/automation/automation_proxy.h"
#include "chrome/test/automation/tab_proxy.h"
#include "chrome_frame/chrome_frame_delegate.h"
#include "chrome_frame/plugin_url_request.h"
#include "chrome_frame/sync_msg_reply_dispatcher.h"
#include "content/public/common/page_zoom.h"

// By a convoluated route, this timeout also winds up being the sync automation
// message timeout. See the ChromeFrameAutomationProxyImpl ctor and the
// AutomationProxy ctor for details.
const unsigned long kCommandExecutionTimeout = 60000;  // NOLINT, 60 seconds

class ProxyFactory;
class NavigationConstraints;
enum AutomationPageFontSize;

struct DECLSPEC_NOVTABLE ChromeFrameAutomationProxy {  // NOLINT
  virtual bool Send(IPC::Message* msg) = 0;

  virtual void SendAsAsync(
      IPC::SyncMessage* msg,
      SyncMessageReplyDispatcher::SyncMessageCallContext* context,
      void* key) = 0;
  virtual void CancelAsync(void* key) = 0;
  virtual scoped_refptr<TabProxy> CreateTabProxy(int handle) = 0;
  virtual void ReleaseTabProxy(AutomationHandle handle) = 0;
  virtual std::string server_version() = 0;

  virtual void SendProxyConfig(const std::string&) = 0;
 protected:
  virtual ~ChromeFrameAutomationProxy() {}
};

// Forward declarations.
class ProxyFactory;

// We extend the AutomationProxy class to handle our custom
// IPC messages
class ChromeFrameAutomationProxyImpl
  : public ChromeFrameAutomationProxy,
    // We have to derive from automationproxy since we want access to some
    // members (tracker_ & channel_) - simple aggregation wont work;
    // .. and non-public inheritance is verboten.
    public AutomationProxy {
 public:
  ~ChromeFrameAutomationProxyImpl();
  virtual void SendAsAsync(
      IPC::SyncMessage* msg,
      SyncMessageReplyDispatcher::SyncMessageCallContext* context,
      void* key);

  // Called on the worker thread.
  virtual void OnChannelError();

  virtual void CancelAsync(void* key);

  virtual scoped_refptr<TabProxy> CreateTabProxy(int handle);
  virtual void ReleaseTabProxy(AutomationHandle handle);

  virtual std::string server_version() {
    return AutomationProxy::server_version();
  }

  virtual bool Send(IPC::Message* msg) {
    return AutomationProxy::Send(msg);
  }

  virtual void SendProxyConfig(const std::string& p) {
    AutomationProxy::SendProxyConfig(p);
  }

 protected:
  friend class AutomationProxyCacheEntry;
  ChromeFrameAutomationProxyImpl(AutomationProxyCacheEntry* entry,
                                 std::string channel_id,
                                 base::TimeDelta launch_timeout);

  class CFMsgDispatcher;
  class TabProxyNotificationMessageFilter;

  scoped_refptr<CFMsgDispatcher> sync_;
  scoped_refptr<TabProxyNotificationMessageFilter> message_filter_;
  AutomationProxyCacheEntry* proxy_entry_;
};

// This class contains information used for launching chrome.
class ChromeFrameLaunchParams :  // NOLINT
    public base::RefCountedThreadSafe<ChromeFrameLaunchParams> {
 public:
  ChromeFrameLaunchParams(const GURL& url, const GURL& referrer,
                          const base::FilePath& profile_path,
                          const std::wstring& profile_name,
                          const std::wstring& language,
                          bool incognito, bool widget_mode,
                          bool route_all_top_level_navigations)
    : launch_timeout_(kCommandExecutionTimeout), url_(url),
      referrer_(referrer), profile_path_(profile_path),
      profile_name_(profile_name), language_(language),
      version_check_(true), incognito_mode_(incognito),
      is_widget_mode_(widget_mode),
      route_all_top_level_navigations_(route_all_top_level_navigations) {
  }

  ~ChromeFrameLaunchParams() {
  }

  void set_launch_timeout(int timeout) {
    launch_timeout_ = timeout;
  }

  int launch_timeout() const {
    return launch_timeout_;
  }

  const GURL& url() const {
    return url_;
  }

  void set_url(const GURL& url) {
    url_ = url;
  }

  const GURL& referrer() const {
    return referrer_;
  }

  void set_referrer(const GURL& referrer) {
    referrer_ = referrer;
  }

  const base::FilePath& profile_path() const {
    return profile_path_;
  }

  const std::wstring& profile_name() const {
    return profile_name_;
  }

  const std::wstring& language() const {
    return language_;
  }

  bool version_check() const {
    return version_check_;
  }

  void set_version_check(bool check) {
    version_check_ = check;
  }

  bool incognito() const {
    return incognito_mode_;
  }

  bool widget_mode() const {
    return is_widget_mode_;
  }

  void set_route_all_top_level_navigations(
      bool route_all_top_level_navigations) {
    route_all_top_level_navigations_ = route_all_top_level_navigations;
  }

  bool route_all_top_level_navigations() const {
    return route_all_top_level_navigations_;
  }

 protected:
  int launch_timeout_;
  GURL url_;
  GURL referrer_;
  base::FilePath profile_path_;
  std::wstring profile_name_;
  std::wstring language_;
  bool version_check_;
  bool incognito_mode_;
  bool is_widget_mode_;
  bool route_all_top_level_navigations_;

 private:
  DISALLOW_COPY_AND_ASSIGN(ChromeFrameLaunchParams);
};

// Callback when chrome process launch is complete and automation handshake
// (Hello message) is established.  All methods are invoked on the automation
// proxy's worker thread.
struct DECLSPEC_NOVTABLE LaunchDelegate {  // NOLINT
  virtual void LaunchComplete(ChromeFrameAutomationProxy* proxy,
                              AutomationLaunchResult result) = 0;
  virtual void AutomationServerDied() = 0;
};  // NOLINT

// Manages a cached ChromeFrameAutomationProxyImpl entry and holds
// reference-less pointers to LaunchDelegate(s) to be notified in case
// of automation server process changes.
class AutomationProxyCacheEntry
    : public base::RefCounted<AutomationProxyCacheEntry> {
 public:
  AutomationProxyCacheEntry(ChromeFrameLaunchParams* params,
                            LaunchDelegate* delegate);

  ~AutomationProxyCacheEntry();

  void AddDelegate(LaunchDelegate* delegate);
  void RemoveDelegate(LaunchDelegate* delegate, base::WaitableEvent* done,
                      bool* was_last_delegate);

  DWORD WaitForThread(DWORD timeout) {  // NOLINT
    DCHECK(thread_.get());
    return ::WaitForSingleObject(thread_->thread_handle().platform_handle(),
                                 timeout);
  }

  bool IsSameProfile(const std::wstring& name) const {
    return lstrcmpiW(name.c_str(), profile_name.c_str()) == 0;
  }

  base::Thread* thread() const {
    return thread_.get();
  }

  base::MessageLoop* message_loop() const {
    return thread_->message_loop();
  }

  bool IsSameThread(base::PlatformThreadId id) const {
    return thread_->thread_id() == id;
  }

  ChromeFrameAutomationProxyImpl* proxy() const {
    DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
    return proxy_.get();
  }

  // Called by the proxy when the automation server has unexpectedly gone away.
  void OnChannelError();

 protected:
  void CreateProxy(ChromeFrameLaunchParams* params,
                   LaunchDelegate* delegate);

 protected:
  std::wstring profile_name;
  scoped_ptr<base::Thread> thread_;
  scoped_ptr<ChromeFrameAutomationProxyImpl> proxy_;
  AutomationLaunchResult launch_result_;
  typedef std::vector<LaunchDelegate*> LaunchDelegates;
  LaunchDelegates launch_delegates_;
  // Used for UMA histogram logging to measure the time for the chrome
  // automation server to start;
  base::TimeTicks automation_server_launch_start_time_;
};

// We must create and destroy automation proxy in a thread with a message loop.
// Hence thread cannot be a member of the proxy.
class ProxyFactory {
 public:
  ProxyFactory();
  virtual ~ProxyFactory();

  // Fetches or creates a new automation server instance.
  // delegate may be NULL.  If non-null, a pointer to the delegate will
  // be stored for the lifetime of the automation process or until
  // ReleaseAutomationServer is called.
  virtual void GetAutomationServer(LaunchDelegate* delegate,
                                   ChromeFrameLaunchParams* params,
                                   void** automation_server_id);
  virtual bool ReleaseAutomationServer(void* server_id,
                                       LaunchDelegate* delegate);

 private:
  typedef base::StackVector<scoped_refptr<AutomationProxyCacheEntry>, 4> Vector;
  Vector proxies_;
  // Lock if we are going to call GetAutomationServer from more than one thread.
  base::Lock lock_;
};

// Handles all automation requests initiated from the chrome frame objects.
// These include the chrome tab/chrome frame activex plugin objects.
class ChromeFrameAutomationClient
    : public CWindowImpl<ChromeFrameAutomationClient>,
      public TaskMarshallerThroughWindowsMessages<ChromeFrameAutomationClient>,
      public base::RefCountedThreadSafe<ChromeFrameAutomationClient>,
      public PluginUrlRequestDelegate,
      public TabProxy::TabProxyDelegate,
      public LaunchDelegate {
 public:
  ChromeFrameAutomationClient();
  ~ChromeFrameAutomationClient();

  // Called from UI thread.
  virtual bool Initialize(ChromeFrameDelegate* chrome_frame_delegate,
                          ChromeFrameLaunchParams* chrome_launch_params);
  void Uninitialize();
  void NotifyAndUninitialize();

  virtual bool InitiateNavigation(
      const std::string& url,
      const std::string& referrer,
      NavigationConstraints* navigation_constraints);

  virtual bool NavigateToIndex(int index);
  bool ForwardMessageFromExternalHost(const std::string& message,
                                      const std::string& origin,
                                      const std::string& target);
  bool SetProxySettings(const std::string& json_encoded_proxy_settings);

  void FindInPage(const std::wstring& search_string,
                  FindInPageDirection forward,
                  FindInPageCase match_case,
                  bool find_next);

  virtual void OnChromeFrameHostMoved();

  TabProxy* tab() const { return tab_.get(); }

  BEGIN_MSG_MAP(ChromeFrameAutomationClient)
    CHAIN_MSG_MAP(
        TaskMarshallerThroughWindowsMessages<ChromeFrameAutomationClient>)
  END_MSG_MAP()

  // Resizes the hosted chrome window. This is brokered to the chrome
  // automation instance as the host browser could be running under low IL,
  // which would cause the SetWindowPos call to fail.
  void Resize(int width, int height, int flags);

  // Sets the passed in window as the parent of the external tab.
  void SetParentWindow(HWND parent_window);

  void SendContextMenuCommandToChromeFrame(int selected_command);

  HWND tab_window() const {
    return tab_window_;
  }

  void ReleaseAutomationServer();

  // Returns the version number of plugin dll.
  std::wstring GetVersion() const;

  // BitBlts the contents of the chrome window to the print dc.
  void Print(HDC print_dc, const RECT& print_bounds);

  // Called in full tab mode and indicates a request to chrome to print
  // the whole tab.
  void PrintTab();

  void set_use_chrome_network(bool use_chrome_network) {
    use_chrome_network_ = use_chrome_network;
  }

  bool use_chrome_network() const {
    return use_chrome_network_;
  }

#ifdef UNIT_TEST
  void set_proxy_factory(ProxyFactory* factory) {
    proxy_factory_ = factory;
  }
#endif

  void set_handle_top_level_requests(bool handle_top_level_requests) {
    handle_top_level_requests_ = handle_top_level_requests;
  }

  // Url request manager set up.
  void SetUrlFetcher(PluginUrlRequestManager* url_fetcher);

  // Attaches an existing external tab to this automation client instance.
  void AttachExternalTab(uint64 external_tab_cookie);
  void BlockExternalTab(uint64 cookie);

  void SetPageFontSize(enum AutomationPageFontSize);

  // For IDeleteBrowsingHistorySupport
  void RemoveBrowsingData(int remove_mask);

  // Sets the current zoom level on the tab.
  void SetZoomLevel(content::PageZoom zoom_level);

  // Fires before unload and unload handlers on the page if any. Allows the
  // the website to put up a confirmation dialog on unload.
  void OnUnload(bool* should_unload);

 protected:
  // ChromeFrameAutomationProxy::LaunchDelegate implementation.
  virtual void LaunchComplete(ChromeFrameAutomationProxy* proxy,
                              AutomationLaunchResult result);
  virtual void AutomationServerDied();

  // TabProxyDelegate implementation
  virtual bool OnMessageReceived(TabProxy* tab, const IPC::Message& msg);
  virtual void OnChannelError(TabProxy* tab);

  void CreateExternalTab();
  AutomationLaunchResult CreateExternalTabComplete(HWND chrome_window,
                                                   HWND tab_window,
                                                   int tab_handle,
                                                   int session_id);
  // Called in UI thread. Here we fire event to the client notifying for
  // the result of Initialize() method call.
  void InitializeComplete(AutomationLaunchResult result);

  virtual void OnFinalMessage(HWND wnd) {
    Release();
  }

  scoped_refptr<ChromeFrameLaunchParams> launch_params() {
    return chrome_launch_params_;
  }

 private:
  void OnMessageReceivedUIThread(const IPC::Message& msg);
  void OnChannelErrorUIThread();

  HWND chrome_window() const { return chrome_window_; }
  void BeginNavigate();
  void BeginNavigateCompleted(AutomationMsg_NavigationResponseValues result);

  // Helpers
  void ReportNavigationError(AutomationMsg_NavigationResponseValues error_code,
                             const std::string& url);

  bool ProcessUrlRequestMessage(TabProxy* tab, const IPC::Message& msg,
                                bool ui_thread);

  // PluginUrlRequestDelegate implementation. Simply adds tab's handle
  // as parameter and forwards to Chrome via IPC.
  virtual void OnResponseStarted(
      int request_id, const char* mime_type, const char* headers, int size,
      base::Time last_modified, const std::string& redirect_url,
      int redirect_status, const net::HostPortPair& socket_address,
      uint64 upload_size);
  virtual void OnReadComplete(int request_id, const std::string& data);
  virtual void OnResponseEnd(int request_id,
                             const net::URLRequestStatus& status);

  bool is_initialized() const {
    return init_state_ == INITIALIZED;
  }

  HWND parent_window_;
  base::PlatformThreadId ui_thread_id_;

  void* automation_server_id_;
  ChromeFrameAutomationProxy* automation_server_;

  HWND chrome_window_;
  scoped_refptr<TabProxy> tab_;
  ChromeFrameDelegate* chrome_frame_delegate_;

  // Handle to the underlying chrome window. This is a child of the external
  // tab window.
  HWND tab_window_;

  // Keeps track of the version of Chrome we're talking to.
  std::string automation_server_version_;

  typedef enum InitializationState {
    UNINITIALIZED = 0,
    INITIALIZING,
    INITIALIZED,
    UNINITIALIZING,
  };

  InitializationState init_state_;
  bool use_chrome_network_;
  bool handle_top_level_requests_;
  ProxyFactory* proxy_factory_;
  int tab_handle_;
  // The SessionId used by Chrome as the id in the Javascript Tab object.
  int session_id_;
  // Only used if we attach to an existing tab.
  uint64 external_tab_cookie_;

  // Set to true if we received a navigation request prior to the automation
  // server being initialized.
  bool navigate_after_initialization_;

  scoped_refptr<ChromeFrameLaunchParams> chrome_launch_params_;

  // Cache security manager for URL zone checking
  base::win::ScopedComPtr<IInternetSecurityManager> security_manager_;

  // When host network stack is used, this object is in charge of
  // handling network requests.
  PluginUrlRequestManager* url_fetcher_;
  PluginUrlRequestManager::ThreadSafeFlags url_fetcher_flags_;

  // set to true if the host needs to get notified of all top level navigations
  // in this page. This typically applies to hosts which would render the new
  // page without chrome frame. Defaults to false.
  bool route_all_top_level_navigations_;

  friend class BeginNavigateContext;
  friend class CreateExternalTabContext;
};

#endif  // CHROME_FRAME_CHROME_FRAME_AUTOMATION_H_