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