Home | History | Annotate | Download | only in chrome_frame
      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 #ifndef CHROME_FRAME_CHROME_FRAME_AUTOMATION_H_
      6 #define CHROME_FRAME_CHROME_FRAME_AUTOMATION_H_
      7 
      8 #include <atlbase.h>
      9 #include <atlwin.h>
     10 #include <map>
     11 #include <string>
     12 #include <vector>
     13 
     14 #include "base/containers/stack_container.h"
     15 #include "base/memory/ref_counted.h"
     16 #include "base/memory/scoped_handle.h"
     17 #include "base/synchronization/lock.h"
     18 #include "base/threading/thread.h"
     19 #include "base/timer/timer.h"
     20 #include "chrome/test/automation/automation_proxy.h"
     21 #include "chrome/test/automation/tab_proxy.h"
     22 #include "chrome_frame/chrome_frame_delegate.h"
     23 #include "chrome_frame/plugin_url_request.h"
     24 #include "chrome_frame/sync_msg_reply_dispatcher.h"
     25 #include "content/public/common/page_zoom.h"
     26 
     27 // By a convoluated route, this timeout also winds up being the sync automation
     28 // message timeout. See the ChromeFrameAutomationProxyImpl ctor and the
     29 // AutomationProxy ctor for details.
     30 const unsigned long kCommandExecutionTimeout = 60000;  // NOLINT, 60 seconds
     31 
     32 class ProxyFactory;
     33 class NavigationConstraints;
     34 enum AutomationPageFontSize;
     35 
     36 struct DECLSPEC_NOVTABLE ChromeFrameAutomationProxy {  // NOLINT
     37   virtual bool Send(IPC::Message* msg) = 0;
     38 
     39   virtual void SendAsAsync(
     40       IPC::SyncMessage* msg,
     41       SyncMessageReplyDispatcher::SyncMessageCallContext* context,
     42       void* key) = 0;
     43   virtual void CancelAsync(void* key) = 0;
     44   virtual scoped_refptr<TabProxy> CreateTabProxy(int handle) = 0;
     45   virtual void ReleaseTabProxy(AutomationHandle handle) = 0;
     46   virtual std::string server_version() = 0;
     47 
     48   virtual void SendProxyConfig(const std::string&) = 0;
     49  protected:
     50   virtual ~ChromeFrameAutomationProxy() {}
     51 };
     52 
     53 // Forward declarations.
     54 class ProxyFactory;
     55 
     56 // We extend the AutomationProxy class to handle our custom
     57 // IPC messages
     58 class ChromeFrameAutomationProxyImpl
     59   : public ChromeFrameAutomationProxy,
     60     // We have to derive from automationproxy since we want access to some
     61     // members (tracker_ & channel_) - simple aggregation wont work;
     62     // .. and non-public inheritance is verboten.
     63     public AutomationProxy {
     64  public:
     65   ~ChromeFrameAutomationProxyImpl();
     66   virtual void SendAsAsync(
     67       IPC::SyncMessage* msg,
     68       SyncMessageReplyDispatcher::SyncMessageCallContext* context,
     69       void* key);
     70 
     71   // Called on the worker thread.
     72   virtual void OnChannelError();
     73 
     74   virtual void CancelAsync(void* key);
     75 
     76   virtual scoped_refptr<TabProxy> CreateTabProxy(int handle);
     77   virtual void ReleaseTabProxy(AutomationHandle handle);
     78 
     79   virtual std::string server_version() {
     80     return AutomationProxy::server_version();
     81   }
     82 
     83   virtual bool Send(IPC::Message* msg) {
     84     return AutomationProxy::Send(msg);
     85   }
     86 
     87   virtual void SendProxyConfig(const std::string& p) {
     88     AutomationProxy::SendProxyConfig(p);
     89   }
     90 
     91  protected:
     92   friend class AutomationProxyCacheEntry;
     93   ChromeFrameAutomationProxyImpl(AutomationProxyCacheEntry* entry,
     94                                  std::string channel_id,
     95                                  base::TimeDelta launch_timeout);
     96 
     97   class CFMsgDispatcher;
     98   class TabProxyNotificationMessageFilter;
     99 
    100   scoped_refptr<CFMsgDispatcher> sync_;
    101   scoped_refptr<TabProxyNotificationMessageFilter> message_filter_;
    102   AutomationProxyCacheEntry* proxy_entry_;
    103 };
    104 
    105 // This class contains information used for launching chrome.
    106 class ChromeFrameLaunchParams :  // NOLINT
    107     public base::RefCountedThreadSafe<ChromeFrameLaunchParams> {
    108  public:
    109   ChromeFrameLaunchParams(const GURL& url, const GURL& referrer,
    110                           const base::FilePath& profile_path,
    111                           const std::wstring& profile_name,
    112                           const std::wstring& language,
    113                           bool incognito, bool widget_mode,
    114                           bool route_all_top_level_navigations)
    115     : launch_timeout_(kCommandExecutionTimeout), url_(url),
    116       referrer_(referrer), profile_path_(profile_path),
    117       profile_name_(profile_name), language_(language),
    118       version_check_(true), incognito_mode_(incognito),
    119       is_widget_mode_(widget_mode),
    120       route_all_top_level_navigations_(route_all_top_level_navigations) {
    121   }
    122 
    123   ~ChromeFrameLaunchParams() {
    124   }
    125 
    126   void set_launch_timeout(int timeout) {
    127     launch_timeout_ = timeout;
    128   }
    129 
    130   int launch_timeout() const {
    131     return launch_timeout_;
    132   }
    133 
    134   const GURL& url() const {
    135     return url_;
    136   }
    137 
    138   void set_url(const GURL& url) {
    139     url_ = url;
    140   }
    141 
    142   const GURL& referrer() const {
    143     return referrer_;
    144   }
    145 
    146   void set_referrer(const GURL& referrer) {
    147     referrer_ = referrer;
    148   }
    149 
    150   const base::FilePath& profile_path() const {
    151     return profile_path_;
    152   }
    153 
    154   const std::wstring& profile_name() const {
    155     return profile_name_;
    156   }
    157 
    158   const std::wstring& language() const {
    159     return language_;
    160   }
    161 
    162   bool version_check() const {
    163     return version_check_;
    164   }
    165 
    166   void set_version_check(bool check) {
    167     version_check_ = check;
    168   }
    169 
    170   bool incognito() const {
    171     return incognito_mode_;
    172   }
    173 
    174   bool widget_mode() const {
    175     return is_widget_mode_;
    176   }
    177 
    178   void set_route_all_top_level_navigations(
    179       bool route_all_top_level_navigations) {
    180     route_all_top_level_navigations_ = route_all_top_level_navigations;
    181   }
    182 
    183   bool route_all_top_level_navigations() const {
    184     return route_all_top_level_navigations_;
    185   }
    186 
    187  protected:
    188   int launch_timeout_;
    189   GURL url_;
    190   GURL referrer_;
    191   base::FilePath profile_path_;
    192   std::wstring profile_name_;
    193   std::wstring language_;
    194   bool version_check_;
    195   bool incognito_mode_;
    196   bool is_widget_mode_;
    197   bool route_all_top_level_navigations_;
    198 
    199  private:
    200   DISALLOW_COPY_AND_ASSIGN(ChromeFrameLaunchParams);
    201 };
    202 
    203 // Callback when chrome process launch is complete and automation handshake
    204 // (Hello message) is established.  All methods are invoked on the automation
    205 // proxy's worker thread.
    206 struct DECLSPEC_NOVTABLE LaunchDelegate {  // NOLINT
    207   virtual void LaunchComplete(ChromeFrameAutomationProxy* proxy,
    208                               AutomationLaunchResult result) = 0;
    209   virtual void AutomationServerDied() = 0;
    210 };  // NOLINT
    211 
    212 // Manages a cached ChromeFrameAutomationProxyImpl entry and holds
    213 // reference-less pointers to LaunchDelegate(s) to be notified in case
    214 // of automation server process changes.
    215 class AutomationProxyCacheEntry
    216     : public base::RefCounted<AutomationProxyCacheEntry> {
    217  public:
    218   AutomationProxyCacheEntry(ChromeFrameLaunchParams* params,
    219                             LaunchDelegate* delegate);
    220 
    221   ~AutomationProxyCacheEntry();
    222 
    223   void AddDelegate(LaunchDelegate* delegate);
    224   void RemoveDelegate(LaunchDelegate* delegate, base::WaitableEvent* done,
    225                       bool* was_last_delegate);
    226 
    227   DWORD WaitForThread(DWORD timeout) {  // NOLINT
    228     DCHECK(thread_.get());
    229     return ::WaitForSingleObject(thread_->thread_handle().platform_handle(),
    230                                  timeout);
    231   }
    232 
    233   bool IsSameProfile(const std::wstring& name) const {
    234     return lstrcmpiW(name.c_str(), profile_name.c_str()) == 0;
    235   }
    236 
    237   base::Thread* thread() const {
    238     return thread_.get();
    239   }
    240 
    241   base::MessageLoop* message_loop() const {
    242     return thread_->message_loop();
    243   }
    244 
    245   bool IsSameThread(base::PlatformThreadId id) const {
    246     return thread_->thread_id() == id;
    247   }
    248 
    249   ChromeFrameAutomationProxyImpl* proxy() const {
    250     DCHECK(IsSameThread(base::PlatformThread::CurrentId()));
    251     return proxy_.get();
    252   }
    253 
    254   // Called by the proxy when the automation server has unexpectedly gone away.
    255   void OnChannelError();
    256 
    257  protected:
    258   void CreateProxy(ChromeFrameLaunchParams* params,
    259                    LaunchDelegate* delegate);
    260 
    261  protected:
    262   std::wstring profile_name;
    263   scoped_ptr<base::Thread> thread_;
    264   scoped_ptr<ChromeFrameAutomationProxyImpl> proxy_;
    265   AutomationLaunchResult launch_result_;
    266   typedef std::vector<LaunchDelegate*> LaunchDelegates;
    267   LaunchDelegates launch_delegates_;
    268   // Used for UMA histogram logging to measure the time for the chrome
    269   // automation server to start;
    270   base::TimeTicks automation_server_launch_start_time_;
    271 };
    272 
    273 // We must create and destroy automation proxy in a thread with a message loop.
    274 // Hence thread cannot be a member of the proxy.
    275 class ProxyFactory {
    276  public:
    277   ProxyFactory();
    278   virtual ~ProxyFactory();
    279 
    280   // Fetches or creates a new automation server instance.
    281   // delegate may be NULL.  If non-null, a pointer to the delegate will
    282   // be stored for the lifetime of the automation process or until
    283   // ReleaseAutomationServer is called.
    284   virtual void GetAutomationServer(LaunchDelegate* delegate,
    285                                    ChromeFrameLaunchParams* params,
    286                                    void** automation_server_id);
    287   virtual bool ReleaseAutomationServer(void* server_id,
    288                                        LaunchDelegate* delegate);
    289 
    290  private:
    291   typedef base::StackVector<scoped_refptr<AutomationProxyCacheEntry>, 4> Vector;
    292   Vector proxies_;
    293   // Lock if we are going to call GetAutomationServer from more than one thread.
    294   base::Lock lock_;
    295 };
    296 
    297 // Handles all automation requests initiated from the chrome frame objects.
    298 // These include the chrome tab/chrome frame activex plugin objects.
    299 class ChromeFrameAutomationClient
    300     : public CWindowImpl<ChromeFrameAutomationClient>,
    301       public TaskMarshallerThroughWindowsMessages<ChromeFrameAutomationClient>,
    302       public base::RefCountedThreadSafe<ChromeFrameAutomationClient>,
    303       public PluginUrlRequestDelegate,
    304       public TabProxy::TabProxyDelegate,
    305       public LaunchDelegate {
    306  public:
    307   ChromeFrameAutomationClient();
    308   ~ChromeFrameAutomationClient();
    309 
    310   // Called from UI thread.
    311   virtual bool Initialize(ChromeFrameDelegate* chrome_frame_delegate,
    312                           ChromeFrameLaunchParams* chrome_launch_params);
    313   void Uninitialize();
    314   void NotifyAndUninitialize();
    315 
    316   virtual bool InitiateNavigation(
    317       const std::string& url,
    318       const std::string& referrer,
    319       NavigationConstraints* navigation_constraints);
    320 
    321   virtual bool NavigateToIndex(int index);
    322   bool ForwardMessageFromExternalHost(const std::string& message,
    323                                       const std::string& origin,
    324                                       const std::string& target);
    325   bool SetProxySettings(const std::string& json_encoded_proxy_settings);
    326 
    327   void FindInPage(const std::wstring& search_string,
    328                   FindInPageDirection forward,
    329                   FindInPageCase match_case,
    330                   bool find_next);
    331 
    332   virtual void OnChromeFrameHostMoved();
    333 
    334   TabProxy* tab() const { return tab_.get(); }
    335 
    336   BEGIN_MSG_MAP(ChromeFrameAutomationClient)
    337     CHAIN_MSG_MAP(
    338         TaskMarshallerThroughWindowsMessages<ChromeFrameAutomationClient>)
    339   END_MSG_MAP()
    340 
    341   // Resizes the hosted chrome window. This is brokered to the chrome
    342   // automation instance as the host browser could be running under low IL,
    343   // which would cause the SetWindowPos call to fail.
    344   void Resize(int width, int height, int flags);
    345 
    346   // Sets the passed in window as the parent of the external tab.
    347   void SetParentWindow(HWND parent_window);
    348 
    349   void SendContextMenuCommandToChromeFrame(int selected_command);
    350 
    351   HWND tab_window() const {
    352     return tab_window_;
    353   }
    354 
    355   void ReleaseAutomationServer();
    356 
    357   // Returns the version number of plugin dll.
    358   std::wstring GetVersion() const;
    359 
    360   // BitBlts the contents of the chrome window to the print dc.
    361   void Print(HDC print_dc, const RECT& print_bounds);
    362 
    363   // Called in full tab mode and indicates a request to chrome to print
    364   // the whole tab.
    365   void PrintTab();
    366 
    367   void set_use_chrome_network(bool use_chrome_network) {
    368     use_chrome_network_ = use_chrome_network;
    369   }
    370 
    371   bool use_chrome_network() const {
    372     return use_chrome_network_;
    373   }
    374 
    375 #ifdef UNIT_TEST
    376   void set_proxy_factory(ProxyFactory* factory) {
    377     proxy_factory_ = factory;
    378   }
    379 #endif
    380 
    381   void set_handle_top_level_requests(bool handle_top_level_requests) {
    382     handle_top_level_requests_ = handle_top_level_requests;
    383   }
    384 
    385   // Url request manager set up.
    386   void SetUrlFetcher(PluginUrlRequestManager* url_fetcher);
    387 
    388   // Attaches an existing external tab to this automation client instance.
    389   void AttachExternalTab(uint64 external_tab_cookie);
    390   void BlockExternalTab(uint64 cookie);
    391 
    392   void SetPageFontSize(enum AutomationPageFontSize);
    393 
    394   // For IDeleteBrowsingHistorySupport
    395   void RemoveBrowsingData(int remove_mask);
    396 
    397   // Sets the current zoom level on the tab.
    398   void SetZoomLevel(content::PageZoom zoom_level);
    399 
    400   // Fires before unload and unload handlers on the page if any. Allows the
    401   // the website to put up a confirmation dialog on unload.
    402   void OnUnload(bool* should_unload);
    403 
    404  protected:
    405   // ChromeFrameAutomationProxy::LaunchDelegate implementation.
    406   virtual void LaunchComplete(ChromeFrameAutomationProxy* proxy,
    407                               AutomationLaunchResult result);
    408   virtual void AutomationServerDied();
    409 
    410   // TabProxyDelegate implementation
    411   virtual bool OnMessageReceived(TabProxy* tab, const IPC::Message& msg);
    412   virtual void OnChannelError(TabProxy* tab);
    413 
    414   void CreateExternalTab();
    415   AutomationLaunchResult CreateExternalTabComplete(HWND chrome_window,
    416                                                    HWND tab_window,
    417                                                    int tab_handle,
    418                                                    int session_id);
    419   // Called in UI thread. Here we fire event to the client notifying for
    420   // the result of Initialize() method call.
    421   void InitializeComplete(AutomationLaunchResult result);
    422 
    423   virtual void OnFinalMessage(HWND wnd) {
    424     Release();
    425   }
    426 
    427   scoped_refptr<ChromeFrameLaunchParams> launch_params() {
    428     return chrome_launch_params_;
    429   }
    430 
    431  private:
    432   void OnMessageReceivedUIThread(const IPC::Message& msg);
    433   void OnChannelErrorUIThread();
    434 
    435   HWND chrome_window() const { return chrome_window_; }
    436   void BeginNavigate();
    437   void BeginNavigateCompleted(AutomationMsg_NavigationResponseValues result);
    438 
    439   // Helpers
    440   void ReportNavigationError(AutomationMsg_NavigationResponseValues error_code,
    441                              const std::string& url);
    442 
    443   bool ProcessUrlRequestMessage(TabProxy* tab, const IPC::Message& msg,
    444                                 bool ui_thread);
    445 
    446   // PluginUrlRequestDelegate implementation. Simply adds tab's handle
    447   // as parameter and forwards to Chrome via IPC.
    448   virtual void OnResponseStarted(
    449       int request_id, const char* mime_type, const char* headers, int size,
    450       base::Time last_modified, const std::string& redirect_url,
    451       int redirect_status, const net::HostPortPair& socket_address,
    452       uint64 upload_size);
    453   virtual void OnReadComplete(int request_id, const std::string& data);
    454   virtual void OnResponseEnd(int request_id,
    455                              const net::URLRequestStatus& status);
    456 
    457   bool is_initialized() const {
    458     return init_state_ == INITIALIZED;
    459   }
    460 
    461   HWND parent_window_;
    462   base::PlatformThreadId ui_thread_id_;
    463 
    464   void* automation_server_id_;
    465   ChromeFrameAutomationProxy* automation_server_;
    466 
    467   HWND chrome_window_;
    468   scoped_refptr<TabProxy> tab_;
    469   ChromeFrameDelegate* chrome_frame_delegate_;
    470 
    471   // Handle to the underlying chrome window. This is a child of the external
    472   // tab window.
    473   HWND tab_window_;
    474 
    475   // Keeps track of the version of Chrome we're talking to.
    476   std::string automation_server_version_;
    477 
    478   typedef enum InitializationState {
    479     UNINITIALIZED = 0,
    480     INITIALIZING,
    481     INITIALIZED,
    482     UNINITIALIZING,
    483   };
    484 
    485   InitializationState init_state_;
    486   bool use_chrome_network_;
    487   bool handle_top_level_requests_;
    488   ProxyFactory* proxy_factory_;
    489   int tab_handle_;
    490   // The SessionId used by Chrome as the id in the Javascript Tab object.
    491   int session_id_;
    492   // Only used if we attach to an existing tab.
    493   uint64 external_tab_cookie_;
    494 
    495   // Set to true if we received a navigation request prior to the automation
    496   // server being initialized.
    497   bool navigate_after_initialization_;
    498 
    499   scoped_refptr<ChromeFrameLaunchParams> chrome_launch_params_;
    500 
    501   // Cache security manager for URL zone checking
    502   base::win::ScopedComPtr<IInternetSecurityManager> security_manager_;
    503 
    504   // When host network stack is used, this object is in charge of
    505   // handling network requests.
    506   PluginUrlRequestManager* url_fetcher_;
    507   PluginUrlRequestManager::ThreadSafeFlags url_fetcher_flags_;
    508 
    509   // set to true if the host needs to get notified of all top level navigations
    510   // in this page. This typically applies to hosts which would render the new
    511   // page without chrome frame. Defaults to false.
    512   bool route_all_top_level_navigations_;
    513 
    514   friend class BeginNavigateContext;
    515   friend class CreateExternalTabContext;
    516 };
    517 
    518 #endif  // CHROME_FRAME_CHROME_FRAME_AUTOMATION_H_
    519