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.
5 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
7 #include "base/basictypes.h"
8 #include "base/compiler_specific.h"
9 #include "base/memory/scoped_ptr.h"
10 #include "base/metrics/field_trial.h"
11 #include "base/strings/string16.h"
12 #include "base/strings/utf_string_conversions.h"
13 #include "chrome/browser/autocomplete/autocomplete_match.h"
14 #include "chrome/browser/prerender/prerender_contents.h"
15 #include "chrome/browser/prerender/prerender_handle.h"
16 #include "chrome/browser/prerender/prerender_manager.h"
17 #include "chrome/browser/prerender/prerender_manager_factory.h"
18 #include "chrome/browser/prerender/prerender_origin.h"
19 #include "chrome/browser/prerender/prerender_tracker.h"
20 #include "chrome/browser/profiles/profile.h"
21 #include "chrome/browser/search/instant_service.h"
22 #include "chrome/browser/search/instant_unittest_base.h"
23 #include "chrome/browser/search/search.h"
24 #include "chrome/browser/ui/search/search_tab_helper.h"
25 #include "chrome/browser/ui/tabs/tab_strip_model.h"
26 #include "chrome/common/render_messages.h"
27 #include "content/public/browser/navigation_controller.h"
28 #include "content/public/browser/web_contents.h"
29 #include "content/public/common/url_constants.h"
30 #include "content/public/test/mock_render_process_host.h"
31 #include "ipc/ipc_message.h"
32 #include "ipc/ipc_test_sink.h"
33 #include "ui/gfx/size.h"
35 using base::ASCIIToUTF16;
39 using content::Referrer;
40 using prerender::Origin;
41 using prerender::PrerenderContents;
42 using prerender::PrerenderHandle;
43 using prerender::PrerenderManager;
44 using prerender::PrerenderManagerFactory;
46 class DummyPrerenderContents : public PrerenderContents {
48 DummyPrerenderContents(
49 PrerenderManager* prerender_manager,
52 const Referrer& referrer,
54 bool call_did_finish_load,
55 const content::SessionStorageNamespaceMap& session_storage_namespace_map);
57 virtual void StartPrerendering(
58 int ALLOW_UNUSED creator_child_id,
59 const gfx::Size& ALLOW_UNUSED size,
60 content::SessionStorageNamespace* session_storage_namespace) OVERRIDE;
61 virtual bool GetChildId(int* child_id) const OVERRIDE;
62 virtual bool GetRouteId(int* route_id) const OVERRIDE;
67 bool call_did_finish_load_;
68 content::SessionStorageNamespaceMap session_storage_namespace_map_;
70 DISALLOW_COPY_AND_ASSIGN(DummyPrerenderContents);
73 class DummyPrerenderContentsFactory : public PrerenderContents::Factory {
75 DummyPrerenderContentsFactory(
76 bool call_did_finish_load,
77 const content::SessionStorageNamespaceMap& session_storage_namespace_map)
78 : call_did_finish_load_(call_did_finish_load),
79 session_storage_namespace_map_(session_storage_namespace_map) {
82 virtual PrerenderContents* CreatePrerenderContents(
83 PrerenderManager* prerender_manager,
86 const Referrer& referrer,
88 uint8 experiment_id) OVERRIDE;
91 bool call_did_finish_load_;
92 content::SessionStorageNamespaceMap session_storage_namespace_map_;
94 DISALLOW_COPY_AND_ASSIGN(DummyPrerenderContentsFactory);
97 DummyPrerenderContents::DummyPrerenderContents(
98 PrerenderManager* prerender_manager,
101 const Referrer& referrer,
103 bool call_did_finish_load,
104 const content::SessionStorageNamespaceMap& session_storage_namespace_map)
105 : PrerenderContents(prerender_manager, profile, url, referrer, origin,
106 PrerenderManager::kNoExperiment),
109 call_did_finish_load_(call_did_finish_load),
110 session_storage_namespace_map_(session_storage_namespace_map) {
113 void DummyPrerenderContents::StartPrerendering(
114 int ALLOW_UNUSED creator_child_id,
115 const gfx::Size& ALLOW_UNUSED size,
116 content::SessionStorageNamespace* session_storage_namespace) {
117 prerender_contents_.reset(content::WebContents::CreateWithSessionStorage(
118 content::WebContents::CreateParams(profile_),
119 session_storage_namespace_map_));
120 content::NavigationController::LoadURLParams params(url_);
121 prerender_contents_->GetController().LoadURLWithParams(params);
122 SearchTabHelper::CreateForWebContents(prerender_contents_.get());
124 prerendering_has_started_ = true;
125 DCHECK(session_storage_namespace);
126 session_storage_namespace_id_ = session_storage_namespace->id();
127 NotifyPrerenderStart();
129 if (call_did_finish_load_)
130 DidFinishLoad(1, url_, true, NULL);
133 bool DummyPrerenderContents::GetChildId(int* child_id) const {
138 bool DummyPrerenderContents::GetRouteId(int* route_id) const {
143 PrerenderContents* DummyPrerenderContentsFactory::CreatePrerenderContents(
144 PrerenderManager* prerender_manager,
147 const Referrer& referrer,
149 uint8 experiment_id) {
150 return new DummyPrerenderContents(prerender_manager, profile, url, referrer,
151 origin, call_did_finish_load_,
152 session_storage_namespace_map_);
157 class InstantSearchPrerendererTest : public InstantUnitTestBase {
159 InstantSearchPrerendererTest() {}
162 virtual void SetUp() OVERRIDE {
163 ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
164 "EmbeddedSearch", "Group1 strk:20 prefetch_results:1"));
165 InstantUnitTestBase::SetUp();
168 void Init(bool prerender_search_results_base_page,
169 bool call_did_finish_load) {
170 AddTab(browser(), GURL(content::kAboutBlankURL));
172 content::SessionStorageNamespaceMap session_storage_namespace_map;
173 session_storage_namespace_map[std::string()] =
174 GetActiveWebContents()->GetController().
175 GetDefaultSessionStorageNamespace();
176 PrerenderManagerFactory::GetForProfile(browser()->profile())->
177 SetPrerenderContentsFactory(
178 new DummyPrerenderContentsFactory(call_did_finish_load,
179 session_storage_namespace_map));
181 if (prerender_search_results_base_page) {
182 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
183 prerenderer->Init(session_storage_namespace_map, gfx::Size(640, 480));
184 EXPECT_NE(static_cast<PrerenderHandle*>(NULL), prerender_handle());
188 InstantSearchPrerenderer* GetInstantSearchPrerenderer() {
189 return instant_service_->instant_search_prerenderer();
192 const GURL& GetPrerenderURL() {
193 return GetInstantSearchPrerenderer()->prerender_url_;
196 void SetLastQuery(const base::string16& query) {
197 GetInstantSearchPrerenderer()->last_instant_suggestion_ =
198 InstantSuggestion(query, std::string());
201 content::WebContents* prerender_contents() {
202 return GetInstantSearchPrerenderer()->prerender_contents();
205 bool MessageWasSent(uint32 id) {
206 content::MockRenderProcessHost* process =
207 static_cast<content::MockRenderProcessHost*>(
208 prerender_contents()->GetRenderViewHost()->GetProcess());
209 return process->sink().GetFirstMessageMatching(id) != NULL;
212 content::WebContents* GetActiveWebContents() const {
213 return browser()->tab_strip_model()->GetWebContentsAt(0);
216 PrerenderHandle* prerender_handle() {
217 return GetInstantSearchPrerenderer()->prerender_handle_.get();
220 void PrerenderSearchQuery(const base::string16& query) {
222 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
223 prerenderer->Prerender(InstantSuggestion(query, std::string()));
224 CommitPendingLoad(&prerender_contents()->GetController());
225 EXPECT_TRUE(prerenderer->CanCommitQuery(GetActiveWebContents(), query));
226 EXPECT_NE(static_cast<PrerenderHandle*>(NULL), prerender_handle());
230 TEST_F(InstantSearchPrerendererTest, GetSearchTermsFromPrerenderedPage) {
232 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
233 GURL url(GetPrerenderURL());
234 EXPECT_EQ(GURL("https://www.google.com/instant?ion=1&foo=foo#foo=foo&strk"),
236 EXPECT_EQ(UTF16ToASCII(prerenderer->get_last_query()),
237 UTF16ToASCII(chrome::GetSearchTermsFromURL(profile(), url)));
239 // Assume the prerendered page prefetched search results for the query
241 SetLastQuery(ASCIIToUTF16("flowers"));
242 EXPECT_EQ("flowers", UTF16ToASCII(prerenderer->get_last_query()));
243 EXPECT_EQ(UTF16ToASCII(prerenderer->get_last_query()),
244 UTF16ToASCII(chrome::GetSearchTermsFromURL(profile(), url)));
247 TEST_F(InstantSearchPrerendererTest, PrefetchSearchResults) {
249 EXPECT_TRUE(prerender_handle()->IsFinishedLoading());
250 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
251 prerenderer->Prerender(
252 InstantSuggestion(ASCIIToUTF16("flowers"), std::string()));
253 EXPECT_EQ("flowers", UTF16ToASCII(prerenderer->get_last_query()));
254 EXPECT_TRUE(MessageWasSent(
255 ChromeViewMsg_SearchBoxSetSuggestionToPrefetch::ID));
258 TEST_F(InstantSearchPrerendererTest, DoNotPrefetchSearchResults) {
260 // Page hasn't finished loading yet.
261 EXPECT_FALSE(prerender_handle()->IsFinishedLoading());
262 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
263 prerenderer->Prerender(
264 InstantSuggestion(ASCIIToUTF16("flowers"), std::string()));
265 EXPECT_EQ("", UTF16ToASCII(prerenderer->get_last_query()));
266 EXPECT_FALSE(MessageWasSent(
267 ChromeViewMsg_SearchBoxSetSuggestionToPrefetch::ID));
270 TEST_F(InstantSearchPrerendererTest, CanCommitQuery) {
272 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
273 base::string16 query = ASCIIToUTF16("flowers");
274 prerenderer->Prerender(InstantSuggestion(query, std::string()));
275 EXPECT_TRUE(prerenderer->CanCommitQuery(GetActiveWebContents(), query));
277 // Make sure InstantSearchPrerenderer::CanCommitQuery() returns false for
278 // invalid search queries.
279 EXPECT_FALSE(prerenderer->CanCommitQuery(GetActiveWebContents(),
280 ASCIIToUTF16("joy")));
281 EXPECT_FALSE(prerenderer->CanCommitQuery(GetActiveWebContents(),
285 TEST_F(InstantSearchPrerendererTest, CommitQuery) {
286 base::string16 query = ASCIIToUTF16("flowers");
287 PrerenderSearchQuery(query);
288 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
289 prerenderer->Commit(query);
290 EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxSubmit::ID));
293 TEST_F(InstantSearchPrerendererTest, CancelPrerenderRequestOnTabChangeEvent) {
295 EXPECT_NE(static_cast<PrerenderHandle*>(NULL), prerender_handle());
297 // Add a new tab to deactivate the current tab.
298 AddTab(browser(), GURL(content::kAboutBlankURL));
299 EXPECT_EQ(2, browser()->tab_strip_model()->count());
301 // Make sure the pending prerender request is cancelled.
302 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
305 TEST_F(InstantSearchPrerendererTest, CancelPendingPrerenderRequest) {
307 EXPECT_NE(static_cast<PrerenderHandle*>(NULL), prerender_handle());
309 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
310 prerenderer->Cancel();
311 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
314 TEST_F(InstantSearchPrerendererTest, PrerenderingAllowed) {
316 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
317 content::WebContents* active_tab = GetActiveWebContents();
318 EXPECT_EQ(GURL(content::kAboutBlankURL), active_tab->GetURL());
320 // Allow prerendering only for search type AutocompleteMatch suggestions.
321 AutocompleteMatch search_type_match(NULL, 1100, false,
322 AutocompleteMatchType::SEARCH_SUGGEST);
323 EXPECT_TRUE(AutocompleteMatch::IsSearchType(search_type_match.type));
324 EXPECT_TRUE(prerenderer->IsAllowed(search_type_match, active_tab));
326 AutocompleteMatch url_type_match(NULL, 1100, true,
327 AutocompleteMatchType::URL_WHAT_YOU_TYPED);
328 EXPECT_FALSE(AutocompleteMatch::IsSearchType(url_type_match.type));
329 EXPECT_FALSE(prerenderer->IsAllowed(url_type_match, active_tab));
331 // Search results page supports Instant search. InstantSearchPrerenderer is
332 // used only when the underlying page doesn't support Instant.
333 NavigateAndCommitActiveTab(GURL("https://www.google.com/alt#quux=foo&strk"));
334 active_tab = GetActiveWebContents();
335 EXPECT_FALSE(chrome::GetSearchTermsFromURL(profile(), active_tab->GetURL())
337 EXPECT_FALSE(chrome::ShouldPrefetchSearchResultsOnSRP());
338 EXPECT_FALSE(prerenderer->IsAllowed(search_type_match, active_tab));
341 TEST_F(InstantSearchPrerendererTest, UsePrerenderPage) {
342 PrerenderSearchQuery(ASCIIToUTF16("foo"));
344 // Open a search results page. A prerendered page exists for |url|. Make sure
345 // the browser swaps the current tab contents with the prerendered contents.
346 GURL url("https://www.google.com/alt#quux=foo&strk");
347 browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
348 content::PAGE_TRANSITION_TYPED,
350 EXPECT_EQ(GetPrerenderURL(), GetActiveWebContents()->GetURL());
351 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
354 TEST_F(InstantSearchPrerendererTest, PrerenderRequestCancelled) {
355 PrerenderSearchQuery(ASCIIToUTF16("foo"));
357 // Cancel the prerender request.
358 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
359 prerenderer->Cancel();
360 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
362 // Open a search results page. Prerendered page does not exists for |url|.
363 // Make sure the browser navigates the current tab to this |url|.
364 GURL url("https://www.google.com/alt#quux=foo&strk");
365 browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
366 content::PAGE_TRANSITION_TYPED,
368 EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
369 EXPECT_EQ(url, GetActiveWebContents()->GetURL());
372 TEST_F(InstantSearchPrerendererTest,
373 CancelPrerenderRequest_SearchQueryMistmatch) {
374 PrerenderSearchQuery(ASCIIToUTF16("foo"));
376 // Open a search results page. Committed query("pen") doesn't match with the
377 // prerendered search query("foo"). Make sure the InstantSearchPrerenderer
378 // cancels the active prerender request and the browser navigates the active
379 // tab to this |url|.
380 GURL url("https://www.google.com/alt#quux=pen&strk");
381 browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
382 content::PAGE_TRANSITION_TYPED,
384 EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
385 EXPECT_EQ(url, GetActiveWebContents()->GetURL());
386 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
389 TEST_F(InstantSearchPrerendererTest,
390 CancelPrerenderRequest_EmptySearchQueryCommitted) {
391 PrerenderSearchQuery(ASCIIToUTF16("foo"));
393 // Open a search results page. Make sure the InstantSearchPrerenderer cancels
394 // the active prerender request upon the receipt of empty search query.
395 GURL url("https://www.google.com/alt#quux=&strk");
396 browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
397 content::PAGE_TRANSITION_TYPED,
399 EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
400 EXPECT_EQ(url, GetActiveWebContents()->GetURL());
401 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
404 class ReuseInstantSearchBasePageTest : public InstantSearchPrerendererTest {
406 ReuseInstantSearchBasePageTest() {}
409 virtual void SetUp() OVERRIDE {
410 ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
412 "Group1 strk:20 prefetch_results:1 reuse_instant_search_base_page:1"));
413 InstantUnitTestBase::SetUp();
417 TEST_F(ReuseInstantSearchBasePageTest, CanCommitQuery) {
419 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
420 base::string16 query = ASCIIToUTF16("flowers");
421 prerenderer->Prerender(InstantSuggestion(query, std::string()));
422 EXPECT_TRUE(prerenderer->CanCommitQuery(GetActiveWebContents(), query));
424 // When the Instant search base page has finished loading,
425 // InstantSearchPrerenderer can commit any search query to the prerendered
426 // page (even if it doesn't match the last known suggestion query).
427 EXPECT_TRUE(prerenderer->CanCommitQuery(GetActiveWebContents(),
428 ASCIIToUTF16("joy")));
429 // Invalid search query committed.
430 EXPECT_FALSE(prerenderer->CanCommitQuery(GetActiveWebContents(),
434 TEST_F(ReuseInstantSearchBasePageTest,
435 CanCommitQuery_InstantSearchBasePageLoadInProgress) {
437 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
438 base::string16 query = ASCIIToUTF16("flowers");
439 prerenderer->Prerender(InstantSuggestion(query, std::string()));
441 // When the Instant search base page hasn't finished loading,
442 // InstantSearchPrerenderer cannot commit any search query to the base page.
443 EXPECT_FALSE(prerenderer->CanCommitQuery(GetActiveWebContents(), query));
444 EXPECT_FALSE(prerenderer->CanCommitQuery(GetActiveWebContents(),
445 ASCIIToUTF16("joy")));