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