blob: 150a778341c1874619db1d647fd4472966e2826c [file] [log] [blame]
// 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.
#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.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;
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 {
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) {
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> {
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),
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_;
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_;
// 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> {
AutomationProxyCacheEntry(ChromeFrameLaunchParams* params,
LaunchDelegate* delegate);
void AddDelegate(LaunchDelegate* delegate);
void RemoveDelegate(LaunchDelegate* delegate, base::WaitableEvent* done,
bool* was_last_delegate);
DWORD WaitForThread(DWORD timeout) { // NOLINT
return ::WaitForSingleObject(thread_->thread_handle().platform_handle(),
bool IsSameProfile(const std::wstring& name) const {
return lstrcmpiW(name.c_str(), profile_name.c_str()) == 0;
base::Thread* thread() const {
return thread_.get();
MessageLoop* message_loop() const {
return thread_->message_loop();
bool IsSameThread(base::PlatformThreadId id) const {
return thread_->thread_id() == id;
ChromeFrameAutomationProxyImpl* proxy() const {
return proxy_.get();
// Called by the proxy when the automation server has unexpectedly gone away.
void OnChannelError();
void CreateProxy(ChromeFrameLaunchParams* params,
LaunchDelegate* delegate);
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 {
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);
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 {
// 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(); }
// 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;
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);
// 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) {
scoped_refptr<ChromeFrameLaunchParams> launch_params() {
return chrome_launch_params_;
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);
virtual void OnCookiesRetrieved(bool success, const GURL& url,
const std::string& cookie_string, int cookie_id);
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 {
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;