// Copyright (c) 2010 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 "chrome_frame/cfproxy_private.h" #include "base/tuple.h" #include "ipc/ipc_sync_message.h" #include "chrome/test/automation/automation_messages.h" CFProxy::CFProxy(CFProxyTraits* api) : ipc_thread_("ipc"), sync_dispatcher_(&tab2delegate_), ipc_sender_(NULL), api_(api), delegate_count_(0), is_connected_(false) { } CFProxy::~CFProxy() { ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &CFProxy::CleanupOnIoThread)); // ipc_thread destructor will do the Stop anyway. this is for debug :) ipc_thread_.Stop(); } void CFProxy::Init(const ProxyParams& params) { ipc_thread_.StartWithOptions(base::Thread::Options(MessageLoop::TYPE_IO, 0)); ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &CFProxy::InitInIoThread, params)); } int CFProxy::AddDelegate(ChromeProxyDelegate* delegate) { ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &CFProxy::AddDelegateOnIoThread, delegate)); return ++delegate_count_; } int CFProxy::RemoveDelegate(ChromeProxyDelegate* delegate) { ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &CFProxy::RemoveDelegateOnIoThread, delegate)); return --delegate_count_; } void CFProxy::AddDelegateOnIoThread(ChromeProxyDelegate* delegate) { DCHECK(CalledOnIpcThread()); DelegateHolder::AddDelegate(delegate); if (is_connected_) { delegate->Connected(this); } } void CFProxy::RemoveDelegateOnIoThread(ChromeProxyDelegate* delegate) { DCHECK(CalledOnIpcThread()); // Cancel any calls in progress. sync_dispatcher_.Cancel(delegate); DelegateHolder::RemoveDelegate(delegate); delegate->Disconnected(); } void CFProxy::InitInIoThread(const ProxyParams& params) { DCHECK(CalledOnIpcThread()); std::string channel_id = GenerateChannelId(); ipc_sender_ = api_->CreateChannel(channel_id, this); std::wstring cmd_line = BuildCmdLine(channel_id, params.profile_path, params.extra_params); if (api_->LaunchApp(cmd_line)) { CancelableTask* launch_timeout = NewRunnableMethod(this, &CFProxy::LaunchTimeOut); ipc_thread_.message_loop()->PostDelayedTask(FROM_HERE, launch_timeout, params.timeout.InMilliseconds()); } else { OnPeerLost(ChromeProxyDelegate::CHROME_EXE_LAUNCH_FAILED); } } void CFProxy::CleanupOnIoThread() { DCHECK(CalledOnIpcThread()); if (ipc_sender_) { api_->CloseChannel(ipc_sender_); ipc_sender_ = NULL; } // TODO(stoyan): shall we notify delegates? // The object is dying, so under normal circumstances there should be // no delegates. DCHECK_EQ(0, delegate_count_); DCHECK_EQ(0u, delegate_list_.size()); DCHECK_EQ(0u, tab2delegate_.size()); } void CFProxy::LaunchTimeOut() { DCHECK(CalledOnIpcThread()); if (!is_connected_) { OnPeerLost(ChromeProxyDelegate::CHROME_EXE_LAUNCH_TIMEOUT); } } void CFProxy::OnPeerLost(ChromeProxyDelegate::DisconnectReason reason) { // Kill the channel. Inform delegates DCHECK(CalledOnIpcThread()); if (ipc_sender_) { api_->CloseChannel(ipc_sender_); ipc_sender_ = NULL; } for (DelegateList::iterator it = delegate_list_.begin(); it != delegate_list_.end(); ++it) { (*it)->PeerLost(this, reason); } } void CFProxy::SendIpcMessage(IPC::Message* m) { ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this, &CFProxy::SendIpcMessageOnIoThread, m)); } void CFProxy::SendIpcMessageOnIoThread(IPC::Message* m) { DCHECK(CalledOnIpcThread()); if (ipc_sender_) { ipc_sender_->Send(m); } else { delete m; } } ////////////////////////////////////////////////////////////////////////// // Sync messages. void CFProxy::InstallExtension(ChromeProxyDelegate* delegate, const FilePath& crx_path, SyncMessageContext* ctx) { IPC::SyncMessage* m = new AutomationMsg_InstallExtension(0, crx_path, NULL); sync_dispatcher_.QueueSyncMessage(m, delegate, ctx); SendIpcMessage(m); } void CFProxy::LoadExtension(ChromeProxyDelegate* delegate, const FilePath& path, SyncMessageContext* ctx) { IPC::SyncMessage* m = new AutomationMsg_LoadExpandedExtension(0, path, 0); sync_dispatcher_.QueueSyncMessage(m, delegate, ctx); SendIpcMessage(m); } void CFProxy::GetEnabledExtensions(ChromeProxyDelegate* delegate, SyncMessageContext* ctx) { IPC::SyncMessage* m = new AutomationMsg_GetEnabledExtensions(0, NULL); sync_dispatcher_.QueueSyncMessage(m, delegate, ctx); SendIpcMessage(m); } void CFProxy::Tab_Find(int tab, const string16& search_string, FindInPageDirection forward, FindInPageCase match_case, bool find_next) { AutomationMsg_Find_Params params; params.unused = 0; params.search_string = search_string; params.find_next = find_next; params.match_case = (match_case == CASE_SENSITIVE); params.forward = (forward == FWD); IPC::SyncMessage* m = new AutomationMsg_Find(0, tab, params, NULL, NULL); // Not interested in result. sync_dispatcher_.QueueSyncMessage(m, NULL, NULL); SendIpcMessage(m); } void CFProxy::Tab_OverrideEncoding(int tab, const char* encoding) { IPC::SyncMessage* m = new AutomationMsg_OverrideEncoding(0, tab, encoding, NULL); // Not interested in result. sync_dispatcher_.QueueSyncMessage(m, NULL, NULL); SendIpcMessage(m); } void CFProxy::Tab_Navigate(int tab, const GURL& url, const GURL& referrer) { IPC::SyncMessage* m = new AutomationMsg_NavigateInExternalTab(0, tab, url, referrer, NULL); // We probably are not interested in result since provider just checks // whether tab handle is valid. sync_dispatcher_.QueueSyncMessage(m, NULL, NULL); SendIpcMessage(m); } void CFProxy::CreateTab(ChromeProxyDelegate* delegate, const IPC::ExternalTabSettings& p) { IPC::SyncMessage* m = new AutomationMsg_CreateExternalTab(0, p, 0, 0, 0); sync_dispatcher_.QueueSyncMessage(m, delegate, NULL); SendIpcMessage(m); } void CFProxy::ConnectTab(ChromeProxyDelegate* delegate, HWND hwnd, uint64 cookie) { IPC::SyncMessage* m = new AutomationMsg_ConnectExternalTab(0, cookie, true, hwnd, NULL, NULL, NULL); sync_dispatcher_.QueueSyncMessage(m, delegate, NULL); SendIpcMessage(m); } void CFProxy::BlockTab(uint64 cookie) { IPC::SyncMessage* m = new AutomationMsg_ConnectExternalTab(0, cookie, false, NULL, NULL, NULL, NULL); sync_dispatcher_.QueueSyncMessage(m, NULL, NULL); SendIpcMessage(m); } void CFProxy::Tab_RunUnloadHandlers(int tab) { IPC::SyncMessage* m = new AutomationMsg_RunUnloadHandlers(0, tab, 0); ChromeProxyDelegate* p = Tab2Delegate(tab); sync_dispatcher_.QueueSyncMessage(m, p, NULL); SendIpcMessage(m); } // IPC::Channel::Listener void CFProxy::OnMessageReceived(const IPC::Message& message) { // Handle sync message reply. bool done = sync_dispatcher_.OnReplyReceived(&message); if (done) return; // Handle tab related message. int tab_handle = IsTabMessage(message); if (tab_handle != 0) { ChromeProxyDelegate* d = Tab2Delegate(tab_handle); if (d) DispatchTabMessageToDelegate(d, message); return; } DLOG(WARNING) << "Unknown message received!"; } void CFProxy::OnChannelConnected(int32 peer_pid) { is_connected_ = true; // TODO(stoyan): May be we should wait for Hello message. for (DelegateList::iterator it = delegate_list_.begin(); it != delegate_list_.end(); ++it) { (*it)->Connected(this); } } void CFProxy::OnChannelError() { is_connected_ = false; // Inform the sync message callbacks that there are not going to see // any reply. sync_dispatcher_.OnChannelClosed(); OnPeerLost(ChromeProxyDelegate::CHANNEL_ERROR); // TODO(stoyan): Relaunch? }