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:
~MockSearchIPCRouterDelegate()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:
SetUp()70 virtual void SetUp() {
71 ChromeRenderViewHostTestHarness::SetUp();
72 SearchTabHelper::CreateForWebContents(web_contents());
73 }
74
CreateBrowserContext()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.
CreateSigninManager(const std::string & username,bool sync_history)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
MessageWasSent(uint32 id)109 bool MessageWasSent(uint32 id) {
110 return process()->sink().GetFirstMessageMatching(id) != NULL;
111 }
112
mock_delegate()113 MockSearchIPCRouterDelegate* mock_delegate() { return &delegate_; }
114
115 private:
116 MockSearchIPCRouterDelegate delegate_;
117 };
118
TEST_F(SearchTabHelperTest,DetermineIfPageSupportsInstant_Local)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
TEST_F(SearchTabHelperTest,DetermineIfPageSupportsInstant_NonLocal)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
TEST_F(SearchTabHelperTest,PageURLDoesntBelongToInstantRenderer)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
TEST_F(SearchTabHelperTest,OnChromeIdentityCheckMatch)167 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMatch) {
168 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
169 CreateSigninManager(std::string("foo@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@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
TEST_F(SearchTabHelperTest,OnChromeIdentityCheckMismatch)187 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMismatch) {
188 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
189 CreateSigninManager(std::string("foo@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@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
TEST_F(SearchTabHelperTest,OnChromeIdentityCheckSignedOutMatch)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
TEST_F(SearchTabHelperTest,OnChromeIdentityCheckSignedOutMismatch)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@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
TEST_F(SearchTabHelperTest,OnChromeIdentityCheckMatchNotSyncing)253 TEST_F(SearchTabHelperTest, OnChromeIdentityCheckMatchNotSyncing) {
254 NavigateAndCommit(GURL(chrome::kChromeSearchLocalNtpUrl));
255 CreateSigninManager(std::string("foo@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@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:
TabTitleObserver(content::WebContents * contents)275 explicit TabTitleObserver(content::WebContents* contents)
276 : WebContentsObserver(contents) {}
277
title_on_start()278 base::string16 title_on_start() { return title_on_start_; }
title_on_commit()279 base::string16 title_on_commit() { return title_on_commit_; }
280
281 private:
DidStartProvisionalLoadForFrame(int64,int64,bool,const GURL &,bool,bool,content::RenderViewHost *)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
DidNavigateMainFrame(const content::LoadCommittedDetails &,const content::FrameNavigateParams &)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
TEST_F(SearchTabHelperTest,TitleIsSetForNTP)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:
SetUp()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
TEST_F(SearchTabHelperWindowTest,OnProvisionalLoadFailRedirectNTPToLocal)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
TEST_F(SearchTabHelperWindowTest,OnProvisionalLoadFailDontRedirectIfAborted)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
TEST_F(SearchTabHelperWindowTest,OnProvisionalLoadFailDontRedirectNonNTP)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