chrome_frame_automation.h revision d57369da7c6519fef57db42085f7b42d4c8845c1
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.
30const unsigned long kCommandExecutionTimeout = 60000;  // NOLINT, 60 seconds
31
32class ProxyFactory;
33class NavigationConstraints;
34enum AutomationPageFontSize;
35
36struct 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.
54class ProxyFactory;
55
56// We extend the AutomationProxy class to handle our custom
57// IPC messages
58class 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.
106class 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.
206struct 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.
215class 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.
275class 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.
299class 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