diff options
author | stoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-20 21:32:32 +0000 |
---|---|---|
committer | stoyan@chromium.org <stoyan@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-10-20 21:32:32 +0000 |
commit | 314a0696abc62a1a036faa45f90d9acd43e8525e (patch) | |
tree | a738178022398caa85d5640381ce151cad20274f | |
parent | 93aa89c7533db379bb6bab05be9f5e33af233487 (diff) | |
download | chromium_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.cc | 3 | ||||
-rw-r--r-- | chrome_frame/cfproxy_proxy.cc | 22 | ||||
-rw-r--r-- | chrome_frame/cfproxy_support.cc | 25 | ||||
-rw-r--r-- | chrome_frame/external_tab.cc | 70 | ||||
-rw-r--r-- | chrome_frame/external_tab.h | 4 | ||||
-rw-r--r-- | chrome_frame/external_tab_test.cc | 154 |
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(); } |