1 // Copyright 2013 The Chromium Authors. All rights reserved. 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 #include "chrome/browser/ui/search/search_tab_helper.h" 6 7 #include "base/command_line.h" 8 #include "base/memory/scoped_ptr.h" 9 #include "base/strings/utf_string_conversions.h" 10 #include "chrome/browser/search/search.h" 11 #include "chrome/browser/search_engines/template_url_service.h" 12 #include "chrome/browser/search_engines/template_url_service_factory.h" 13 #include "chrome/browser/signin/fake_signin_manager.h" 14 #include "chrome/browser/signin/signin_manager_factory.h" 15 #include "chrome/browser/sync/profile_sync_service.h" 16 #include "chrome/browser/sync/profile_sync_service_factory.h" 17 #include "chrome/browser/sync/profile_sync_service_mock.h" 18 #include "chrome/browser/ui/search/search_ipc_router.h" 19 #include "chrome/browser/ui/tabs/tab_strip_model.h" 20 #include "chrome/common/chrome_switches.h" 21 #include "chrome/common/ntp_logging_events.h" 22 #include "chrome/common/omnibox_focus_state.h" 23 #include "chrome/common/render_messages.h" 24 #include "chrome/common/url_constants.h" 25 #include "chrome/test/base/browser_with_test_window_test.h" 26 #include "chrome/test/base/chrome_render_view_host_test_harness.h" 27 #include "chrome/test/base/testing_profile.h" 28 #include "chrome/test/base/ui_test_utils.h" 29 #include "content/public/browser/navigation_controller.h" 30 #include "content/public/browser/navigation_entry.h" 31 #include "content/public/browser/web_contents.h" 32 #include "content/public/test/mock_render_process_host.h" 33 #include "grit/generated_resources.h" 34 #include "ipc/ipc_message.h" 35 #include "ipc/ipc_test_sink.h" 36 #include "net/base/net_errors.h" 37 #include "testing/gmock/include/gmock/gmock.h" 38 #include "testing/gtest/include/gtest/gtest.h" 39 #include "ui/base/l10n/l10n_util.h" 40 #include "url/gurl.h" 41 42 using testing::Return; 43 44 namespace { 45 46 class MockSearchIPCRouterDelegate : public SearchIPCRouter::Delegate { 47 public: 48 virtual ~MockSearchIPCRouterDelegate() {} 49 50 MOCK_METHOD1(OnInstantSupportDetermined, void(bool supports_instant)); 51 MOCK_METHOD1(OnSetVoiceSearchSupport, void(bool supports_voice_search)); 52 MOCK_METHOD1(FocusOmnibox, void(OmniboxFocusState state)); 53 MOCK_METHOD3(NavigateToURL, void(const GURL&, WindowOpenDisposition, bool)); 54 MOCK_METHOD1(OnDeleteMostVisitedItem, void(const GURL& url)); 55 MOCK_METHOD1(OnUndoMostVisitedDeletion, void(const GURL& url)); 56 MOCK_METHOD0(OnUndoAllMostVisitedDeletions, void()); 57 MOCK_METHOD1(OnLogEvent, void(NTPLoggingEventType event)); 58 MOCK_METHOD2(OnLogMostVisitedImpression, 59 void(int position, const base::string16& provider)); 60 MOCK_METHOD2(OnLogMostVisitedNavigation, 61 void(int position, const base::string16& provider)); 62 MOCK_METHOD1(PasteIntoOmnibox, void(const base::string16&)); 63 MOCK_METHOD1(OnChromeIdentityCheck, void(const base::string16& identity)); 64 }; 65 66 } // namespace 67 68 class SearchTabHelperTest : public ChromeRenderViewHostTestHarness { 69 public: 70 virtual void SetUp() { 71 ChromeRenderViewHostTestHarness::SetUp(); 72 SearchTabHelper::CreateForWebContents(web_contents()); 73 } 74 75 virtual content::BrowserContext* CreateBrowserContext() OVERRIDE { 76 TestingProfile::Builder builder; 77 builder.AddTestingFactory(SigninManagerFactory::GetInstance(), 78 FakeSigninManagerBase::Build); 79 builder.AddTestingFactory( 80 ProfileSyncServiceFactory::GetInstance(), 81 ProfileSyncServiceMock::BuildMockProfileSyncService); 82 return builder.Build().release(); 83 } 84 85 // Creates a sign-in manager for tests. If |username| is not empty, the 86 // testing profile of the WebContents will be connected to the given account. 87 // The account can be configured to |sync_history| or not. 88 void CreateSigninManager(const std::string& username, bool sync_history) { 89 SigninManagerBase* signin_manager = static_cast<SigninManagerBase*>( 90 SigninManagerFactory::GetForProfile(profile())); 91 92 if (!username.empty()) { 93 ASSERT_TRUE(signin_manager); 94 signin_manager->SetAuthenticatedUsername(username); 95 } 96 97 ProfileSyncServiceMock* sync_service = static_cast<ProfileSyncServiceMock*>( 98 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile())); 99 100 EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(true)); 101 syncer::ModelTypeSet result; 102 if (sync_history) { 103 result.Put(syncer::HISTORY_DELETE_DIRECTIVES); 104 } 105 EXPECT_CALL(*sync_service, GetActiveDataTypes()) 106 .WillRepeatedly(Return(result)); 107 } 108 109 bool MessageWasSent(uint32 id) { 110 return process()->sink().GetFirstMessageMatching(id) != NULL; 111 } 112 113 MockSearchIPCRouterDelegate* mock_delegate() { return &delegate_; } 114 115 private: 116 MockSearchIPCRouterDelegate delegate_; 117 }; 118 119 TEST_F(SearchTabHelperTest, DetermineIfPageSupportsInstant_Local) { 120 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 121 EXPECT_CALL(*mock_delegate(), OnInstantSupportDetermined(true)).Times(0); 122 123 SearchTabHelper* search_tab_helper = 124 SearchTabHelper::FromWebContents(web_contents()); 125 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 126 search_tab_helper->ipc_router().set_delegate_for_testing(mock_delegate()); 127 search_tab_helper->DetermineIfPageSupportsInstant(); 128 } 129 130 TEST_F(SearchTabHelperTest, DetermineIfPageSupportsInstant_NonLocal) { 131 NavigateAndCommit(GURL("chrome-search://foo/bar")); 132 process()->sink().ClearMessages(); 133 EXPECT_CALL(*mock_delegate(), OnInstantSupportDetermined(true)).Times(1); 134 135 SearchTabHelper* search_tab_helper = 136 SearchTabHelper::FromWebContents(web_contents()); 137 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 138 search_tab_helper->ipc_router().set_delegate_for_testing(mock_delegate()); 139 search_tab_helper->DetermineIfPageSupportsInstant(); 140 ASSERT_TRUE(MessageWasSent(ChromeViewMsg_DetermineIfPageSupportsInstant::ID)); 141 142 scoped_ptr<IPC::Message> response( 143 new ChromeViewHostMsg_InstantSupportDetermined( 144 web_contents()->GetRoutingID(), 145 search_tab_helper->ipc_router().page_seq_no_for_testing(), 146 true)); 147 search_tab_helper->ipc_router().OnMessageReceived(*response); 148 } 149 150 TEST_F(SearchTabHelperTest, PageURLDoesntBelongToInstantRenderer) { 151 // Navigate to a page URL that doesn't belong to Instant renderer. 152 // SearchTabHelper::DeterminerIfPageSupportsInstant() should return 153 // immediately without dispatching any message to the renderer. 154 NavigateAndCommit(GURL("http://www.example.com")); 155 process()->sink().ClearMessages(); 156 EXPECT_CALL(*mock_delegate(), OnInstantSupportDetermined(false)).Times(0); 157 158 SearchTabHelper* search_tab_helper = 159 SearchTabHelper::FromWebContents(web_contents()); 160 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 161 search_tab_helper->ipc_router().set_delegate_for_testing(mock_delegate()); 162 search_tab_helper->DetermineIfPageSupportsInstant(); 163 ASSERT_FALSE(MessageWasSent( 164 ChromeViewMsg_DetermineIfPageSupportsInstant::ID)); 165 } 166 167 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMatch) { 168 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 169 CreateSigninManager(std::string("foo (at) bar.com"), true); 170 SearchTabHelper* search_tab_helper = 171 SearchTabHelper::FromWebContents(web_contents()); 172 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 173 174 const base::string16 test_identity = base::ASCIIToUTF16("foo (at) bar.com"); 175 search_tab_helper->OnChromeIdentityCheck(test_identity); 176 177 const IPC::Message* message = process()->sink().GetUniqueMessageMatching( 178 ChromeViewMsg_ChromeIdentityCheckResult::ID); 179 ASSERT_TRUE(message != NULL); 180 181 ChromeViewMsg_ChromeIdentityCheckResult::Param params; 182 ChromeViewMsg_ChromeIdentityCheckResult::Read(message, ¶ms); 183 EXPECT_EQ(test_identity, params.a); 184 ASSERT_TRUE(params.b); 185 } 186 187 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMismatch) { 188 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 189 CreateSigninManager(std::string("foo (at) bar.com"), true); 190 SearchTabHelper* search_tab_helper = 191 SearchTabHelper::FromWebContents(web_contents()); 192 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 193 194 const base::string16 test_identity = base::ASCIIToUTF16("bar (at) foo.com"); 195 search_tab_helper->OnChromeIdentityCheck(test_identity); 196 197 const IPC::Message* message = process()->sink().GetUniqueMessageMatching( 198 ChromeViewMsg_ChromeIdentityCheckResult::ID); 199 ASSERT_TRUE(message != NULL); 200 201 ChromeViewMsg_ChromeIdentityCheckResult::Param params; 202 ChromeViewMsg_ChromeIdentityCheckResult::Read(message, ¶ms); 203 EXPECT_EQ(test_identity, params.a); 204 ASSERT_FALSE(params.b); 205 } 206 207 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckSignedOutMatch) { 208 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 209 // This test does not sign in. 210 ProfileSyncServiceMock* sync_service = static_cast<ProfileSyncServiceMock*>( 211 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile())); 212 EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(false)); 213 SearchTabHelper* search_tab_helper = 214 SearchTabHelper::FromWebContents(web_contents()); 215 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 216 217 const base::string16 test_identity; 218 search_tab_helper->OnChromeIdentityCheck(test_identity); 219 220 const IPC::Message* message = process()->sink().GetUniqueMessageMatching( 221 ChromeViewMsg_ChromeIdentityCheckResult::ID); 222 ASSERT_TRUE(message != NULL); 223 224 ChromeViewMsg_ChromeIdentityCheckResult::Param params; 225 ChromeViewMsg_ChromeIdentityCheckResult::Read(message, ¶ms); 226 EXPECT_EQ(test_identity, params.a); 227 ASSERT_FALSE(params.b); 228 } 229 230 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckSignedOutMismatch) { 231 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 232 // This test does not sign in. 233 ProfileSyncServiceMock* sync_service = static_cast<ProfileSyncServiceMock*>( 234 ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile())); 235 EXPECT_CALL(*sync_service, sync_initialized()).WillRepeatedly(Return(false)); 236 SearchTabHelper* search_tab_helper = 237 SearchTabHelper::FromWebContents(web_contents()); 238 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 239 240 const base::string16 test_identity = base::ASCIIToUTF16("bar (at) foo.com"); 241 search_tab_helper->OnChromeIdentityCheck(test_identity); 242 243 const IPC::Message* message = process()->sink().GetUniqueMessageMatching( 244 ChromeViewMsg_ChromeIdentityCheckResult::ID); 245 ASSERT_TRUE(message != NULL); 246 247 ChromeViewMsg_ChromeIdentityCheckResult::Param params; 248 ChromeViewMsg_ChromeIdentityCheckResult::Read(message, ¶ms); 249 EXPECT_EQ(test_identity, params.a); 250 ASSERT_FALSE(params.b); 251 } 252 253 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMatchNotSyncing) { 254 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl)); 255 CreateSigninManager(std::string("foo (at) bar.com"), false); 256 SearchTabHelper* search_tab_helper = 257 SearchTabHelper::FromWebContents(web_contents()); 258 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 259 260 const base::string16 test_identity = base::ASCIIToUTF16("foo (at) bar.com"); 261 search_tab_helper->OnChromeIdentityCheck(test_identity); 262 263 const IPC::Message* message = process()->sink().GetUniqueMessageMatching( 264 ChromeViewMsg_ChromeIdentityCheckResult::ID); 265 ASSERT_TRUE(message != NULL); 266 267 ChromeViewMsg_ChromeIdentityCheckResult::Param params; 268 ChromeViewMsg_ChromeIdentityCheckResult::Read(message, ¶ms); 269 EXPECT_EQ(test_identity, params.a); 270 ASSERT_FALSE(params.b); 271 } 272 273 class TabTitleObserver : public content::WebContentsObserver { 274 public: 275 explicit TabTitleObserver(content::WebContents* contents) 276 : WebContentsObserver(contents) {} 277 278 base::string16 title_on_start() { return title_on_start_; } 279 base::string16 title_on_commit() { return title_on_commit_; } 280 281 private: 282 virtual void DidStartProvisionalLoadForFrame( 283 int64 /* frame_id */, 284 int64 /* parent_frame_id */, 285 bool /* is_main_frame */, 286 const GURL& /* validated_url */, 287 bool /* is_error_page */, 288 bool /* is_iframe_srcdoc */, 289 content::RenderViewHost* /* render_view_host */) OVERRIDE { 290 title_on_start_ = web_contents()->GetTitle(); 291 } 292 293 virtual void DidNavigateMainFrame( 294 const content::LoadCommittedDetails& /* details */, 295 const content::FrameNavigateParams& /* params */) OVERRIDE { 296 title_on_commit_ = web_contents()->GetTitle(); 297 } 298 299 base::string16 title_on_start_; 300 base::string16 title_on_commit_; 301 }; 302 303 TEST_F(SearchTabHelperTest, TitleIsSetForNTP) { 304 TabTitleObserver title_observer(web_contents()); 305 NavigateAndCommit(GURL(chrome::kChromeUINewTabURL)); 306 const base::string16 title = l10n_util::GetStringUTF16(IDS_NEW_TAB_TITLE); 307 EXPECT_EQ(title, title_observer.title_on_start()); 308 EXPECT_EQ(title, title_observer.title_on_commit()); 309 EXPECT_EQ(title, web_contents()->GetTitle()); 310 } 311 312 class SearchTabHelperWindowTest : public BrowserWithTestWindowTest { 313 protected: 314 virtual void SetUp() OVERRIDE { 315 BrowserWithTestWindowTest::SetUp(); 316 TemplateURLServiceFactory::GetInstance()->SetTestingFactoryAndUse( 317 profile(), &TemplateURLServiceFactory::BuildInstanceFor); 318 TemplateURLService* template_url_service = 319 TemplateURLServiceFactory::GetForProfile(profile()); 320 ui_test_utils::WaitForTemplateURLServiceToLoad(template_url_service); 321 322 TemplateURLData data; 323 data.SetURL("http://foo.com/url?bar={searchTerms}"); 324 data.instant_url = "http://foo.com/instant?" 325 "{google:omniboxStartMarginParameter}{google:forceInstantResults}" 326 "foo=foo#foo=foo&strk"; 327 data.new_tab_url = std::string("https://foo.com/newtab?strk"); 328 data.alternate_urls.push_back("http://foo.com/alt#quux={searchTerms}"); 329 data.search_terms_replacement_key = "strk"; 330 331 TemplateURL* template_url = new TemplateURL(data); 332 template_url_service->Add(template_url); 333 template_url_service->SetUserSelectedDefaultSearchProvider(template_url); 334 } 335 }; 336 337 TEST_F(SearchTabHelperWindowTest, OnProvisionalLoadFailRedirectNTPToLocal) { 338 AddTab(browser(), GURL(chrome::kChromeUINewTabURL)); 339 content::WebContents* contents = 340 browser()->tab_strip_model()->GetWebContentsAt(0); 341 content::NavigationController* controller = &contents->GetController(); 342 343 SearchTabHelper* search_tab_helper = 344 SearchTabHelper::FromWebContents(contents); 345 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 346 347 // A failed provisional load of a cacheable NTP should be redirected to local 348 // NTP. 349 const GURL cacheableNTPURL = chrome::GetNewTabPageURL(profile()); 350 search_tab_helper->DidFailProvisionalLoad(1, base::string16(), true, 351 cacheableNTPURL, 1, base::string16(), NULL); 352 CommitPendingLoad(controller); 353 EXPECT_EQ(GURL(chrome::kChromeSearchLocalNtpUrl), 354 controller->GetLastCommittedEntry()->GetURL()); 355 } 356 357 TEST_F(SearchTabHelperWindowTest, OnProvisionalLoadFailDontRedirectIfAborted) { 358 AddTab(browser(), GURL("chrome://blank")); 359 content::WebContents* contents = 360 browser()->tab_strip_model()->GetWebContentsAt(0); 361 content::NavigationController* controller = &contents->GetController(); 362 363 SearchTabHelper* search_tab_helper = 364 SearchTabHelper::FromWebContents(contents); 365 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 366 367 // A failed provisional load of a cacheable NTP should be redirected to local 368 // NTP. 369 const GURL cacheableNTPURL = chrome::GetNewTabPageURL(profile()); 370 search_tab_helper->DidFailProvisionalLoad(1, base::string16(), true, 371 cacheableNTPURL, net::ERR_ABORTED, base::string16(), NULL); 372 CommitPendingLoad(controller); 373 EXPECT_EQ(GURL("chrome://blank"), 374 controller->GetLastCommittedEntry()->GetURL()); 375 } 376 377 TEST_F(SearchTabHelperWindowTest, OnProvisionalLoadFailDontRedirectNonNTP) { 378 AddTab(browser(), GURL(chrome::kChromeUINewTabURL)); 379 content::WebContents* contents = 380 browser()->tab_strip_model()->GetWebContentsAt(0); 381 content::NavigationController* controller = &contents->GetController(); 382 383 SearchTabHelper* search_tab_helper = 384 SearchTabHelper::FromWebContents(contents); 385 ASSERT_NE(static_cast<SearchTabHelper*>(NULL), search_tab_helper); 386 387 // Any other web page shouldn't be redirected when provisional load fails. 388 search_tab_helper->DidFailProvisionalLoad(1, base::string16(), true, 389 GURL("http://www.example.com"), 1, base::string16(), NULL); 390 CommitPendingLoad(controller); 391 EXPECT_NE(GURL(chrome::kChromeSearchLocalNtpUrl), 392 controller->GetLastCommittedEntry()->GetURL()); 393 } 394