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