summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-20 21:32:32 +0000
committerstoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2010-10-20 21:32:32 +0000
commit314a0696abc62a1a036faa45f90d9acd43e8525e (patch)
treea738178022398caa85d5640381ce151cad20274f
parent93aa89c7533db379bb6bab05be9f5e33af233487 (diff)
downloadchromium_src-314a0696abc62a1a036faa45f90d9acd43e8525e.zip
chromium_src-314a0696abc62a1a036faa45f90d9acd43e8525e.tar.gz
chromium_src-314a0696abc62a1a036faa45f90d9acd43e8525e.tar.bz2
Fill some gaps.
Tests for ExternalTabProxy. Review URL: http://codereview.chromium.org/3888005 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@63262 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--chrome_frame/cfproxy_factory.cc3
-rw-r--r--chrome_frame/cfproxy_proxy.cc22
-rw-r--r--chrome_frame/cfproxy_support.cc25
-rw-r--r--chrome_frame/external_tab.cc70
-rw-r--r--chrome_frame/external_tab.h4
-rw-r--r--chrome_frame/external_tab_test.cc154
6 files changed, 249 insertions, 29 deletions
diff --git a/chrome_frame/cfproxy_factory.cc b/chrome_frame/cfproxy_factory.cc
index c936873..4c9c76d 100644
--- a/chrome_frame/cfproxy_factory.cc
+++ b/chrome_frame/cfproxy_factory.cc
@@ -33,7 +33,8 @@ ChromeProxyFactory::~ChromeProxyFactory() {
AutoLock lock(lock_);
ProxyMap::iterator it = proxies_.begin();
for (; it != proxies_.end(); ++it) {
- delete it->second;
+ ChromeProxy* proxy = it->second;
+ delete proxy;
}
proxies_.clear();
}
diff --git a/chrome_frame/cfproxy_proxy.cc b/chrome_frame/cfproxy_proxy.cc
index 5a826c7..e03cb0e 100644
--- a/chrome_frame/cfproxy_proxy.cc
+++ b/chrome_frame/cfproxy_proxy.cc
@@ -30,30 +30,32 @@ void CFProxy::Init(const ProxyParams& params) {
&CFProxy::InitInIoThread, params));
}
-int CFProxy::AddDelegate(ChromeProxyDelegate* proxy) {
+int CFProxy::AddDelegate(ChromeProxyDelegate* delegate) {
ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
- &CFProxy::AddDelegateOnIoThread, proxy));
+ &CFProxy::AddDelegateOnIoThread, delegate));
return ++delegate_count_;
}
-int CFProxy::RemoveDelegate(ChromeProxyDelegate* proxy) {
+int CFProxy::RemoveDelegate(ChromeProxyDelegate* delegate) {
ipc_thread_.message_loop()->PostTask(FROM_HERE, NewRunnableMethod(this,
- &CFProxy::RemoveDelegateOnIoThread, proxy));
+ &CFProxy::RemoveDelegateOnIoThread, delegate));
return --delegate_count_;
}
-void CFProxy::AddDelegateOnIoThread(ChromeProxyDelegate* proxy) {
+void CFProxy::AddDelegateOnIoThread(ChromeProxyDelegate* delegate) {
DCHECK(CalledOnIpcThread());
- DelegateHolder::AddDelegate(proxy);
+ DelegateHolder::AddDelegate(delegate);
if (is_connected_) {
- proxy->Connected(this);
+ delegate->Connected(this);
}
}
-void CFProxy::RemoveDelegateOnIoThread(ChromeProxyDelegate* proxy) {
+void CFProxy::RemoveDelegateOnIoThread(ChromeProxyDelegate* delegate) {
DCHECK(CalledOnIpcThread());
- DelegateHolder::RemoveDelegate(proxy);
- proxy->Disconnected();
+ // Cancel any calls in progress.
+ sync_dispatcher_.Cancel(delegate);
+ DelegateHolder::RemoveDelegate(delegate);
+ delegate->Disconnected();
}
void CFProxy::InitInIoThread(const ProxyParams& params) {
diff --git a/chrome_frame/cfproxy_support.cc b/chrome_frame/cfproxy_support.cc
index 6b74a48..42e6a28 100644
--- a/chrome_frame/cfproxy_support.cc
+++ b/chrome_frame/cfproxy_support.cc
@@ -4,6 +4,7 @@
#include "chrome_frame/cfproxy_private.h"
+#include <vector>
#include "base/atomic_sequence_num.h"
#include "base/command_line.h"
#include "base/process_util.h"
@@ -227,9 +228,29 @@ void SyncMsgSender::QueueSyncMessage(const IPC::SyncMessage* msg,
}
}
+// Cancel all outgoing calls for this delegate.
void SyncMsgSender::Cancel(ChromeProxyDelegate* delegate) {
- // TODO(stoyan): Cancel all outgoing calls for this delegate
- // We may not need this. :)
+ std::vector<SingleSentMessage*> cancelled;
+ {
+ AutoLock lock(messages_lock_);
+ SentMessages::iterator it = messages_.begin();
+ for (; it != messages_.end(); ) {
+ SingleSentMessage* origin = it->second;
+ if (origin->delegate_ == delegate) {
+ cancelled.push_back(origin);
+ it = messages_.erase(it);
+ } else {
+ ++it;
+ }
+ }
+ }
+
+ for (std::vector<SingleSentMessage*>::iterator it = cancelled.begin();
+ it != cancelled.end(); ++it) {
+ SingleSentMessage* origin = *it;
+ DispatchReplyFail(origin->type_, delegate, origin->ctx_);
+ delete origin;
+ }
}
SyncMsgSender::SingleSentMessage* SyncMsgSender::RemoveMessage(int id) {
diff --git a/chrome_frame/external_tab.cc b/chrome_frame/external_tab.cc
index 322f74a..4bfeff2 100644
--- a/chrome_frame/external_tab.cc
+++ b/chrome_frame/external_tab.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "chrome_frame/external_tab.h"
+#include "base/singleton.h"
#include "base/tracked.h"
#include "base/task.h"
#include "base/waitable_event.h"
@@ -13,6 +14,8 @@ DISABLE_RUNNABLE_METHOD_REFCOUNT(ExternalTabProxy);
DISABLE_RUNNABLE_METHOD_REFCOUNT(UIDelegate);
namespace {
+ Singleton<ChromeProxyFactory> g_proxy_factory;
+
struct UserDataHolder : public SyncMessageContext {
explicit UserDataHolder(void* p) : data(p) {}
void* data;
@@ -20,7 +23,8 @@ namespace {
}
-ExternalTabProxy::ExternalTabProxy() : state_(NONE), tab_(0), proxy_(NULL),
+ExternalTabProxy::ExternalTabProxy() : state_(NONE), tab_(0), tab_wnd_(NULL),
+ chrome_wnd_(NULL), proxy_factory_(g_proxy_factory.get()), proxy_(NULL),
ui_delegate_(NULL) {
}
@@ -42,22 +46,33 @@ void ExternalTabProxy::Init() {
void ExternalTabProxy::Destroy() {
DCHECK(NULL == done_.get());
- done_.reset(new base::WaitableEvent(true, false));
- proxy_factory_->ReleaseProxy(this, tab_params_.proxy_params.profile);
- done_->Wait();
- done_.reset(NULL);
-
- proxy_ = NULL;
- tab_ = 0;
+ // TODO(stoyan): Should we release proxy first and then destroy the window
+ // (parent of the chrome window) or the other way around?
+ if (state_ != NONE) {
+ done_.reset(new base::WaitableEvent(true, false));
+ proxy_factory_->ReleaseProxy(this, tab_params_.proxy_params.profile);
+ done_->Wait();
+ done_.reset(NULL);
+
+ state_ = NONE;
+ proxy_ = NULL;
+ tab_ = 0;
+ CWindowImpl<ExternalTabProxy>::DestroyWindow();
+ tab_wnd_ = NULL;
+ chrome_wnd_ = NULL;
+ // We shall tell the TaskMarshaller to delete queued tasks.
+ // ui_.DeleteAll();
+ }
}
void ExternalTabProxy::CreateTab(const CreateTabParams& create_params,
UIDelegate* delegate) {
- DCHECK(ui_delegate_ != NULL);
+ DCHECK(ui_delegate_ == NULL);
DCHECK_EQ(NONE, state_);
// Create host window if needed.
Init();
ui_delegate_ = delegate;
+ // TODO(stoyan): Shall we check the CanNavigate(create_params.url)?
tab_params_ = create_params;
state_ = INIT_IN_PROGRESS;
proxy_factory_->GetProxy(this, create_params.proxy_params);
@@ -72,7 +87,21 @@ void ExternalTabProxy::Connected(ChromeProxy* proxy) {
void ExternalTabProxy::UiConnected(ChromeProxy* proxy) {
proxy_ = proxy;
IPC::ExternalTabSettings settings;
- // TODO(stoyan): Initialize settings.
+ settings.parent = m_hWnd;
+ settings.style = WS_CHILD;
+ settings.is_off_the_record = tab_params_.is_incognito;
+ // TODO(stoyan): FIX this.
+ settings.load_requests_via_automation = true;
+ // TODO(stoyan): FIX this.
+ settings.handle_top_level_requests = true;
+ settings.initial_url = tab_params_.url;
+ settings.referrer = tab_params_.referrer;
+ // Infobars are disabled in widget mode.
+ settings.infobars_enabled = !tab_params_.is_widget_mode;
+ // TODO(stoyan): FIX this.
+ settings.route_all_top_level_navigations = false;
+
+ state_ = CREATE_TAB_IN_PROGRESS;
proxy->CreateTab(this, settings);
}
@@ -122,7 +151,6 @@ void ExternalTabProxy::ConnectToExternalTab(uint64 external_tab_cookie) {
proxy_->ConnectTab(this, m_hWnd, external_tab_cookie);
}
-
void ExternalTabProxy::BlockExternalTab(uint64 cookie) {
proxy_->BlockTab(cookie);
}
@@ -165,10 +193,28 @@ void ExternalTabProxy::ChromeFrameHostMoved() {
}
//////////////////////////////////////////////////////////////////////////
+void ExternalTabProxy::UiCompleted_CreateTab(bool success, HWND chrome_window,
+ HWND tab_window, int tab_handle) {
+ if (success) {
+ state_ = READY;
+ tab_ = tab_handle;
+ tab_wnd_ = tab_window;
+ chrome_wnd_ = chrome_window;
+
+ // If a navigation request came while tab creation was in progress -
+ // go ahead and navigate.
+ if (pending_navigation_.url.is_valid())
+ proxy_->Tab_Navigate(tab_, pending_navigation_.url,
+ pending_navigation_.referrer);
+ }
+}
+
void ExternalTabProxy::Completed_CreateTab(bool success, HWND chrome_wnd,
HWND tab_window, int tab_handle) {
// in ipc_thread.
- // ui_.PostTask()
+ ui_.PostTask(FROM_HERE, NewRunnableMethod(this,
+ &ExternalTabProxy::UiCompleted_CreateTab,
+ success, chrome_wnd, tab_window, tab_handle));
}
void ExternalTabProxy::Completed_ConnectToTab(bool success,
diff --git a/chrome_frame/external_tab.h b/chrome_frame/external_tab.h
index 882cbed..b68a9bf 100644
--- a/chrome_frame/external_tab.h
+++ b/chrome_frame/external_tab.h
@@ -180,6 +180,8 @@ class ExternalTabProxy : public CWindowImpl<ExternalTabProxy>,
// The UiXXXX are the ChromeProxyDelegate methods but on UI thread.
void UiConnected(ChromeProxy* proxy);
void UiPeerLost(ChromeProxy* proxy, DisconnectReason reason);
+ void UiCompleted_CreateTab(bool success, HWND chrome_window,
+ HWND tab_window, int tab_handle);
// With the present state of affairs the only response we can possibly handle
// in the background IPC thread is Completed_CreateTab() where we can
@@ -194,6 +196,8 @@ class ExternalTabProxy : public CWindowImpl<ExternalTabProxy>,
RELEASE_CF_PROXY_IN_PROGRESS
} state_;
int tab_;
+ HWND tab_wnd_;
+ HWND chrome_wnd_;
ChromeProxyFactory* proxy_factory_;
// Accessed only in the UI thread for simplicity.
ChromeProxy* proxy_;
diff --git a/chrome_frame/external_tab_test.cc b/chrome_frame/external_tab_test.cc
index 8fdd7d5..8f9b918 100644
--- a/chrome_frame/external_tab_test.cc
+++ b/chrome_frame/external_tab_test.cc
@@ -3,19 +3,33 @@
// found in the LICENSE file.
#include "chrome_frame/external_tab.h"
-// #include "base/tracked.h"
-// #include "base/task.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "base/tracked.h"
+
// #include "base/waitable_event.h"
#include "chrome/test/automation/automation_messages.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/gmock/include/gmock/gmock.h"
+#include "testing/gmock_mutant.h"
+#include "chrome_frame/test/chrome_frame_test_utils.h"
// DISABLE_RUNNABLE_METHOD_REFCOUNT(ExternalTabProxy);
// DISABLE_RUNNABLE_METHOD_REFCOUNT(UIDelegate);
+DISABLE_RUNNABLE_METHOD_REFCOUNT(ChromeProxyDelegate);
+using testing::StrictMock;
+using testing::_;
+using testing::Invoke;
+using testing::InvokeWithoutArgs;
+using testing::CreateFunctor;
+using testing::Return;
+using testing::DoAll;
+using testing::Field;
+using chrome_frame_test::TimedMsgLoop;
struct MockUIDelegate : public UIDelegate {
MOCK_METHOD2(OnNavigationStateChanged, void(int flags,
@@ -82,11 +96,143 @@ struct MockProxy : public ChromeProxy {
const GURL& referrer));
MOCK_METHOD2(Tab_OverrideEncoding, void(int tab, const char* encoding));
- private:
MOCK_METHOD1(Init, void(const ProxyParams& params));
MOCK_METHOD1(AddDelegate, int(ChromeProxyDelegate* delegate));
MOCK_METHOD1(RemoveDelegate, int(ChromeProxyDelegate* delegate));
};
-TEST(ExternalTabProxy, Simple) {
+struct MockFactory : public ChromeProxyFactory {
+ MOCK_METHOD0(CreateProxy, ChromeProxy*());
+};
+
+
+// This class just calls methods of ChromeProxyDelegate but in background thread
+struct AsyncEventCreator {
+ public:
+ explicit AsyncEventCreator(ChromeProxyDelegate* delegate)
+ : delegate_(delegate), ipc_thread_("ipc") {
+ base::Thread::Options options(MessageLoop::TYPE_IO, 0);
+ ipc_thread_.StartWithOptions(options);
+ ipc_loop_ = ipc_thread_.message_loop();
+ }
+
+ void Fire_Connected(ChromeProxy* proxy, base::TimeDelta delay) {
+ ipc_loop_->PostDelayedTask(FROM_HERE, NewRunnableMethod(delegate_,
+ &ChromeProxyDelegate::Connected, proxy), delay.InMilliseconds());
+ }
+
+ void Fire_PeerLost(ChromeProxy* proxy,
+ ChromeProxyDelegate::DisconnectReason reason, base::TimeDelta delay) {
+ ipc_loop_->PostDelayedTask(FROM_HERE, NewRunnableMethod(delegate_,
+ &ChromeProxyDelegate::PeerLost, proxy, reason), delay.InMilliseconds());
+ }
+
+ void Fire_Disconnected(base::TimeDelta delay) {
+ ipc_loop_->PostDelayedTask(FROM_HERE, NewRunnableMethod(delegate_,
+ &ChromeProxyDelegate::Disconnected), delay.InMilliseconds());
+ }
+
+ void Fire_CompletedCreateTab(bool success, HWND chrome_wnd, HWND tab_window,
+ int tab_handle, base::TimeDelta delay) {
+ ipc_loop_->PostDelayedTask(FROM_HERE, NewRunnableMethod(delegate_,
+ &ChromeProxyDelegate::Completed_CreateTab, success, chrome_wnd,
+ tab_window, tab_handle), delay.InMilliseconds());
+ }
+
+ void Fire_TabLoaded(const GURL& url, base::TimeDelta delay) {
+ ipc_loop_->PostDelayedTask(FROM_HERE, NewRunnableMethod(delegate_,
+ &ChromeProxyDelegate::TabLoaded, url), delay.InMilliseconds());
+ }
+
+ private:
+ ChromeProxyDelegate* delegate_;
+ base::Thread ipc_thread_;
+ MessageLoop* ipc_loop_;
+};
+
+// We may want the same test with 'real' proxy and mock 'proxy traits'.
+TEST(ExternalTabProxy, CancelledCreateTab) {
+ MockUIDelegate ui_delegate;
+ StrictMock<MockFactory> factory;
+ scoped_ptr<ExternalTabProxy> tab(new ExternalTabProxy());
+ AsyncEventCreator async_events(tab.get());
+ StrictMock<MockProxy>* proxy = new StrictMock<MockProxy>();
+ tab->set_proxy_factory(&factory);
+
+ EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy));
+ EXPECT_CALL(*proxy, Init(_));
+ EXPECT_CALL(*proxy, AddDelegate(tab.get())).WillOnce(DoAll(InvokeWithoutArgs(
+ CreateFunctor(&async_events, &AsyncEventCreator::Fire_Connected,
+ proxy, base::TimeDelta::FromMilliseconds(30))),
+ Return(1)));
+
+ EXPECT_CALL(*proxy, RemoveDelegate(_)).WillOnce(DoAll(
+ InvokeWithoutArgs(CreateFunctor(&async_events,
+ &AsyncEventCreator::Fire_CompletedCreateTab, false, HWND(0), HWND(0),
+ 0, base::TimeDelta::FromMilliseconds(0))),
+ InvokeWithoutArgs(CreateFunctor(&async_events,
+ &AsyncEventCreator::Fire_Disconnected,
+ base::TimeDelta::FromMilliseconds(0))),
+ Return(0)));
+
+ CreateTabParams tab_params;
+ tab_params.is_incognito = true;
+ tab_params.is_widget_mode = false;
+ tab_params.url = GURL("http://Xanadu.org");
+
+ tab->CreateTab(tab_params, &ui_delegate);
+ tab.reset();
+}
+
+// CreateTab with initial url, and the navigate to different url before
+// initialization completes.
+TEST(ExternalTabProxy, NavigateAfterCreate) {
+ MockUIDelegate ui_delegate;
+ StrictMock<MockFactory> factory;
+ scoped_ptr<ExternalTabProxy> tab(new ExternalTabProxy());
+ AsyncEventCreator async_events(tab.get());
+ StrictMock<MockProxy>* proxy = new StrictMock<MockProxy>();
+ TimedMsgLoop loop;
+ tab->set_proxy_factory(&factory);
+ GURL initial_url("http://Xanadu.org");
+ GURL real_url("http://asgard.org");
+
+ EXPECT_CALL(factory, CreateProxy()).WillOnce(Return(proxy));
+ EXPECT_CALL(*proxy, Init(_));
+ EXPECT_CALL(*proxy, AddDelegate(tab.get()))
+ .WillOnce(DoAll(InvokeWithoutArgs(CreateFunctor(&async_events,
+ &AsyncEventCreator::Fire_Connected, proxy,
+ base::TimeDelta::FromMilliseconds(30))),
+ Return(1)));
+
+ EXPECT_CALL(*proxy, CreateTab(tab.get(),
+ Field(&IPC::ExternalTabSettings::initial_url, real_url)))
+ .WillOnce(DoAll(
+ InvokeWithoutArgs(CreateFunctor(&async_events,
+ &AsyncEventCreator::Fire_CompletedCreateTab,
+ true, HWND(0), HWND(0), 7, base::TimeDelta::FromMilliseconds(9))),
+ InvokeWithoutArgs(CreateFunctor(&async_events,
+ &AsyncEventCreator::Fire_TabLoaded, real_url,
+ base::TimeDelta::FromMilliseconds(150)))));
+
+ EXPECT_CALL(ui_delegate, OnLoad(real_url))
+ .WillOnce(QUIT_LOOP(loop));
+
+ EXPECT_CALL(*proxy, RemoveDelegate(_))
+ .WillOnce(DoAll(InvokeWithoutArgs(CreateFunctor(&async_events,
+ &AsyncEventCreator::Fire_Disconnected,
+ base::TimeDelta::FromMilliseconds(0))),
+ Return(0)));
+
+ CreateTabParams tab_params;
+ tab_params.is_incognito = true;
+ tab_params.is_widget_mode = false;
+ tab_params.url = initial_url;
+
+ tab->CreateTab(tab_params, &ui_delegate);
+ tab->Navigate("http://asgard.org", EmptyString(), true);
+
+ loop.RunFor(5);
+ EXPECT_FALSE(loop.WasTimedOut());
+ tab.reset();
}