// 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/external_tab.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, const IPC::NavigationInfo& nav_info)); MOCK_METHOD1(OnUpdateTargetUrl, void(const std::wstring& new_target_url)); MOCK_METHOD3(OnExtensionInstalled, void(const FilePath& path, void* user_data, AutomationMsg_ExtensionResponseValues response)); MOCK_METHOD1(OnLoad, void(const GURL& url)); MOCK_METHOD3(OnMessageFromChromeFrame, void(const std::string& message, const std::string& origin, const std::string& target)); MOCK_METHOD3(OnHandleContextMenu, void(HANDLE menu_handle, int align_flags, const IPC::ContextMenuParams& params)); MOCK_METHOD1(OnHandleAccelerator, void(const MSG& accel_message)); MOCK_METHOD1(OnTabbedOut, void(bool reverse)); MOCK_METHOD1(OnGoToHistoryOffset, void(int offset)); MOCK_METHOD3(OnOpenURL, void(const GURL& url_to_open, const GURL& referrer, int open_disposition)); }; struct MockProxy : public ChromeProxy { MOCK_METHOD1(RemoveBrowsingData, void(int remove_mask)); MOCK_METHOD3(InstallExtension, void(ChromeProxyDelegate* delegate, const FilePath& crx_path, SyncMessageContext* ctx)); MOCK_METHOD3(LoadExtension, void(ChromeProxyDelegate* delegate, const FilePath& path, SyncMessageContext* ctx)); MOCK_METHOD2(GetEnabledExtensions, void(ChromeProxyDelegate* delegate, SyncMessageContext* ctx)); MOCK_METHOD1(SetProxyConfig, void(const std::string& json_encoded_settings)); MOCK_METHOD2(CreateTab, void(ChromeProxyDelegate* delegate, const IPC::ExternalTabSettings& settings)); MOCK_METHOD3(ConnectTab, void(ChromeProxyDelegate* delegate, HWND hwnd, uint64 cookie)); MOCK_METHOD1(BlockTab, void(uint64 cookie)); MOCK_METHOD4(Tab_PostMessage, void(int tab, const std::string& message, const std::string& origin, const std::string& target)); MOCK_METHOD1(Tab_Reload, void(int tab)); MOCK_METHOD1(Tab_Stop, void(int tab)); MOCK_METHOD1(Tab_SaveAs, void(int tab)); MOCK_METHOD1(Tab_Print, void(int tab)); MOCK_METHOD1(Tab_Cut, void(int tab)); MOCK_METHOD1(Tab_Copy, void(int tab)); MOCK_METHOD1(Tab_Paste, void(int tab)); MOCK_METHOD1(Tab_SelectAll, void(int tab)); MOCK_METHOD5(Tab_Find, void(int tab, const string16& search_string, FindInPageDirection forward, FindInPageCase match_case, bool find_next)); MOCK_METHOD2(Tab_MenuCommand, void(int tab, int selected_command)); MOCK_METHOD2(Tab_Zoom, void(int tab, PageZoom::Function zoom_level)); MOCK_METHOD2(Tab_FontSize, void(int tab, AutomationPageFontSize font_size)); MOCK_METHOD3(Tab_SetInitialFocus, void(int tab, bool reverse, bool restore_focus_to_view)); MOCK_METHOD1(Tab_SetParentWindow, void(int tab)); MOCK_METHOD1(Tab_Resize, void(int tab)); MOCK_METHOD2(Tab_ProcessAccelerator, void(int tab, const MSG& msg)); // Misc. MOCK_METHOD1(Tab_OnHostMoved, void(int tab)); MOCK_METHOD1(Tab_RunUnloadHandlers, void(int tab)); MOCK_METHOD2(Tab_SetEnableExtensionAutomation, void(int tab, const std::vector& functions_enabled)); MOCK_METHOD3(Tab_Navigate, void(int tab, const GURL& url, const GURL& referrer)); MOCK_METHOD2(Tab_OverrideEncoding, void(int tab, const char* encoding)); MOCK_METHOD1(Init, void(const ProxyParams& params)); MOCK_METHOD1(AddDelegate, int(ChromeProxyDelegate* delegate)); MOCK_METHOD1(RemoveDelegate, int(ChromeProxyDelegate* delegate)); }; 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 factory; scoped_ptr tab(new ExternalTabProxy()); AsyncEventCreator async_events(tab.get()); StrictMock* proxy = new StrictMock(); TimedMsgLoop loop; 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, CreateTab(tab.get(), _)) .WillOnce(QUIT_LOOP(loop)); 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); loop.RunFor(5); EXPECT_FALSE(loop.WasTimedOut()); tab.reset(); } // CreateTab with initial url, and the navigate to different url before // initialization completes. TEST(ExternalTabProxy, NavigateAfterCreate) { MockUIDelegate ui_delegate; StrictMock factory; scoped_ptr tab(new ExternalTabProxy()); AsyncEventCreator async_events(tab.get()); StrictMock* proxy = new StrictMock(); 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(); }