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_tab_helper.h"
20 #include "chrome/browser/prerender/prerender_tracker.h"
21 #include "chrome/browser/profiles/profile.h"
22 #include "chrome/browser/search/instant_service.h"
23 #include "chrome/browser/search/instant_unittest_base.h"
24 #include "chrome/browser/search/search.h"
25 #include "chrome/browser/ui/search/search_tab_helper.h"
26 #include "chrome/browser/ui/tabs/tab_strip_model.h"
27 #include "chrome/common/render_messages.h"
28 #include "content/public/browser/navigation_controller.h"
29 #include "content/public/browser/web_contents.h"
30 #include "content/public/common/url_constants.h"
31 #include "content/public/test/mock_render_process_host.h"
32 #include "ipc/ipc_message.h"
33 #include "ipc/ipc_test_sink.h"
34 #include "ui/gfx/size.h"
36 using base::ASCIIToUTF16
;
40 using content::Referrer
;
41 using prerender::Origin
;
42 using prerender::PrerenderContents
;
43 using prerender::PrerenderHandle
;
44 using prerender::PrerenderManager
;
45 using prerender::PrerenderManagerFactory
;
46 using prerender::PrerenderTabHelper
;
48 class DummyPrerenderContents
: public PrerenderContents
{
50 DummyPrerenderContents(
51 PrerenderManager
* prerender_manager
,
54 const Referrer
& referrer
,
56 bool call_did_finish_load
,
57 const content::SessionStorageNamespaceMap
& session_storage_namespace_map
);
59 virtual void StartPrerendering(
60 int ALLOW_UNUSED creator_child_id
,
61 const gfx::Size
& ALLOW_UNUSED size
,
62 content::SessionStorageNamespace
* session_storage_namespace
,
63 net::URLRequestContextGetter
* request_context
) OVERRIDE
;
64 virtual bool GetChildId(int* child_id
) const OVERRIDE
;
65 virtual bool GetRouteId(int* route_id
) const OVERRIDE
;
70 bool call_did_finish_load_
;
71 content::SessionStorageNamespaceMap session_storage_namespace_map_
;
73 DISALLOW_COPY_AND_ASSIGN(DummyPrerenderContents
);
76 class DummyPrerenderContentsFactory
: public PrerenderContents::Factory
{
78 DummyPrerenderContentsFactory(
79 bool call_did_finish_load
,
80 const content::SessionStorageNamespaceMap
& session_storage_namespace_map
)
81 : call_did_finish_load_(call_did_finish_load
),
82 session_storage_namespace_map_(session_storage_namespace_map
) {
85 virtual PrerenderContents
* CreatePrerenderContents(
86 PrerenderManager
* prerender_manager
,
89 const Referrer
& referrer
,
91 uint8 experiment_id
) OVERRIDE
;
94 bool call_did_finish_load_
;
95 content::SessionStorageNamespaceMap session_storage_namespace_map_
;
97 DISALLOW_COPY_AND_ASSIGN(DummyPrerenderContentsFactory
);
100 DummyPrerenderContents::DummyPrerenderContents(
101 PrerenderManager
* prerender_manager
,
104 const Referrer
& referrer
,
106 bool call_did_finish_load
,
107 const content::SessionStorageNamespaceMap
& session_storage_namespace_map
)
108 : PrerenderContents(prerender_manager
, profile
, url
, referrer
, origin
,
109 PrerenderManager::kNoExperiment
),
112 call_did_finish_load_(call_did_finish_load
),
113 session_storage_namespace_map_(session_storage_namespace_map
) {
116 void DummyPrerenderContents::StartPrerendering(
117 int ALLOW_UNUSED creator_child_id
,
118 const gfx::Size
& ALLOW_UNUSED size
,
119 content::SessionStorageNamespace
* session_storage_namespace
,
120 net::URLRequestContextGetter
* request_context
) {
121 prerender_contents_
.reset(content::WebContents::CreateWithSessionStorage(
122 content::WebContents::CreateParams(profile_
),
123 session_storage_namespace_map_
));
124 PrerenderTabHelper::CreateForWebContentsWithPasswordManager(
125 prerender_contents_
.get(), NULL
);
126 content::NavigationController::LoadURLParams
params(url_
);
127 prerender_contents_
->GetController().LoadURLWithParams(params
);
128 SearchTabHelper::CreateForWebContents(prerender_contents_
.get());
130 prerendering_has_started_
= true;
131 DCHECK(session_storage_namespace
);
132 session_storage_namespace_id_
= session_storage_namespace
->id();
133 NotifyPrerenderStart();
135 if (call_did_finish_load_
)
136 DidFinishLoad(1, url_
, true, NULL
);
139 bool DummyPrerenderContents::GetChildId(int* child_id
) const {
144 bool DummyPrerenderContents::GetRouteId(int* route_id
) const {
149 PrerenderContents
* DummyPrerenderContentsFactory::CreatePrerenderContents(
150 PrerenderManager
* prerender_manager
,
153 const Referrer
& referrer
,
155 uint8 experiment_id
) {
156 return new DummyPrerenderContents(prerender_manager
, profile
, url
, referrer
,
157 origin
, call_did_finish_load_
,
158 session_storage_namespace_map_
);
163 class InstantSearchPrerendererTest
: public InstantUnitTestBase
{
165 InstantSearchPrerendererTest() {}
168 virtual void SetUp() OVERRIDE
{
169 ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
170 "EmbeddedSearch", "Group1 strk:20 prefetch_results:1"));
171 InstantUnitTestBase::SetUp();
174 void Init(bool prerender_search_results_base_page
,
175 bool call_did_finish_load
) {
176 AddTab(browser(), GURL(url::kAboutBlankURL
));
178 content::SessionStorageNamespaceMap session_storage_namespace_map
;
179 session_storage_namespace_map
[std::string()] =
180 GetActiveWebContents()->GetController().
181 GetDefaultSessionStorageNamespace();
182 PrerenderManagerFactory::GetForProfile(browser()->profile())->
183 SetPrerenderContentsFactory(
184 new DummyPrerenderContentsFactory(call_did_finish_load
,
185 session_storage_namespace_map
));
186 PrerenderManagerFactory::GetForProfile(browser()->profile())->
187 OnCookieStoreLoaded();
188 if (prerender_search_results_base_page
) {
189 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
190 prerenderer
->Init(session_storage_namespace_map
, gfx::Size(640, 480));
191 EXPECT_NE(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
195 InstantSearchPrerenderer
* GetInstantSearchPrerenderer() {
196 return instant_service_
->instant_search_prerenderer();
199 const GURL
& GetPrerenderURL() {
200 return GetInstantSearchPrerenderer()->prerender_url_
;
203 void SetLastQuery(const base::string16
& query
) {
204 GetInstantSearchPrerenderer()->last_instant_suggestion_
=
205 InstantSuggestion(query
, std::string());
208 content::WebContents
* prerender_contents() {
209 return GetInstantSearchPrerenderer()->prerender_contents();
212 bool MessageWasSent(uint32 id
) {
213 content::MockRenderProcessHost
* process
=
214 static_cast<content::MockRenderProcessHost
*>(
215 prerender_contents()->GetRenderViewHost()->GetProcess());
216 return process
->sink().GetFirstMessageMatching(id
) != NULL
;
219 content::WebContents
* GetActiveWebContents() const {
220 return browser()->tab_strip_model()->GetWebContentsAt(0);
223 PrerenderHandle
* prerender_handle() {
224 return GetInstantSearchPrerenderer()->prerender_handle_
.get();
227 void PrerenderSearchQuery(const base::string16
& query
) {
229 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
230 prerenderer
->Prerender(InstantSuggestion(query
, std::string()));
231 CommitPendingLoad(&prerender_contents()->GetController());
232 EXPECT_TRUE(prerenderer
->CanCommitQuery(GetActiveWebContents(), query
));
233 EXPECT_NE(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
237 TEST_F(InstantSearchPrerendererTest
, GetSearchTermsFromPrerenderedPage
) {
239 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
240 GURL
url(GetPrerenderURL());
241 EXPECT_EQ(GURL("https://www.google.com/instant?ion=1&foo=foo#foo=foo&strk"),
243 EXPECT_EQ(base::UTF16ToASCII(prerenderer
->get_last_query()),
245 chrome::ExtractSearchTermsFromURL(profile(), url
)));
247 // Assume the prerendered page prefetched search results for the query
249 SetLastQuery(ASCIIToUTF16("flowers"));
250 EXPECT_EQ("flowers", base::UTF16ToASCII(prerenderer
->get_last_query()));
251 EXPECT_EQ(base::UTF16ToASCII(prerenderer
->get_last_query()),
253 chrome::ExtractSearchTermsFromURL(profile(), url
)));
256 TEST_F(InstantSearchPrerendererTest
, PrefetchSearchResults
) {
258 EXPECT_TRUE(prerender_handle()->IsFinishedLoading());
259 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
260 prerenderer
->Prerender(
261 InstantSuggestion(ASCIIToUTF16("flowers"), std::string()));
262 EXPECT_EQ("flowers", base::UTF16ToASCII(prerenderer
->get_last_query()));
263 EXPECT_TRUE(MessageWasSent(
264 ChromeViewMsg_SearchBoxSetSuggestionToPrefetch::ID
));
267 TEST_F(InstantSearchPrerendererTest
, DoNotPrefetchSearchResults
) {
269 // Page hasn't finished loading yet.
270 EXPECT_FALSE(prerender_handle()->IsFinishedLoading());
271 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
272 prerenderer
->Prerender(
273 InstantSuggestion(ASCIIToUTF16("flowers"), std::string()));
274 EXPECT_EQ("", base::UTF16ToASCII(prerenderer
->get_last_query()));
275 EXPECT_FALSE(MessageWasSent(
276 ChromeViewMsg_SearchBoxSetSuggestionToPrefetch::ID
));
279 TEST_F(InstantSearchPrerendererTest
, CanCommitQuery
) {
281 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
282 base::string16 query
= ASCIIToUTF16("flowers");
283 prerenderer
->Prerender(InstantSuggestion(query
, std::string()));
284 EXPECT_TRUE(prerenderer
->CanCommitQuery(GetActiveWebContents(), query
));
286 // Make sure InstantSearchPrerenderer::CanCommitQuery() returns false for
287 // invalid search queries.
288 EXPECT_FALSE(prerenderer
->CanCommitQuery(GetActiveWebContents(),
289 ASCIIToUTF16("joy")));
290 EXPECT_FALSE(prerenderer
->CanCommitQuery(GetActiveWebContents(),
294 TEST_F(InstantSearchPrerendererTest
, CommitQuery
) {
295 base::string16 query
= ASCIIToUTF16("flowers");
296 PrerenderSearchQuery(query
);
297 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
298 prerenderer
->Commit(query
);
299 EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxSubmit::ID
));
302 TEST_F(InstantSearchPrerendererTest
, CancelPrerenderRequestOnTabChangeEvent
) {
304 EXPECT_NE(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
306 // Add a new tab to deactivate the current tab.
307 AddTab(browser(), GURL(url::kAboutBlankURL
));
308 EXPECT_EQ(2, browser()->tab_strip_model()->count());
310 // Make sure the pending prerender request is cancelled.
311 EXPECT_EQ(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
314 TEST_F(InstantSearchPrerendererTest
, CancelPendingPrerenderRequest
) {
316 EXPECT_NE(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
318 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
319 prerenderer
->Cancel();
320 EXPECT_EQ(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
323 TEST_F(InstantSearchPrerendererTest
, PrerenderingAllowed
) {
325 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
326 content::WebContents
* active_tab
= GetActiveWebContents();
327 EXPECT_EQ(GURL(url::kAboutBlankURL
), active_tab
->GetURL());
329 // Allow prerendering only for search type AutocompleteMatch suggestions.
330 AutocompleteMatch
search_type_match(NULL
, 1100, false,
331 AutocompleteMatchType::SEARCH_SUGGEST
);
332 EXPECT_TRUE(AutocompleteMatch::IsSearchType(search_type_match
.type
));
333 EXPECT_TRUE(prerenderer
->IsAllowed(search_type_match
, active_tab
));
335 AutocompleteMatch
url_type_match(NULL
, 1100, true,
336 AutocompleteMatchType::URL_WHAT_YOU_TYPED
);
337 EXPECT_FALSE(AutocompleteMatch::IsSearchType(url_type_match
.type
));
338 EXPECT_FALSE(prerenderer
->IsAllowed(url_type_match
, active_tab
));
340 // Search results page supports Instant search. InstantSearchPrerenderer is
341 // used only when the underlying page doesn't support Instant.
342 NavigateAndCommitActiveTab(GURL("https://www.google.com/alt#quux=foo&strk"));
343 active_tab
= GetActiveWebContents();
344 EXPECT_FALSE(chrome::ExtractSearchTermsFromURL(profile(),
345 active_tab
->GetURL()).empty());
346 EXPECT_FALSE(chrome::ShouldPrefetchSearchResultsOnSRP());
347 EXPECT_FALSE(prerenderer
->IsAllowed(search_type_match
, active_tab
));
350 TEST_F(InstantSearchPrerendererTest
, UsePrerenderPage
) {
351 PrerenderSearchQuery(ASCIIToUTF16("foo"));
353 // Open a search results page. A prerendered page exists for |url|. Make sure
354 // the browser swaps the current tab contents with the prerendered contents.
355 GURL
url("https://www.google.com/alt#quux=foo&strk");
356 browser()->OpenURL(content::OpenURLParams(url
, Referrer(), CURRENT_TAB
,
357 content::PAGE_TRANSITION_TYPED
,
359 EXPECT_EQ(GetPrerenderURL(), GetActiveWebContents()->GetURL());
360 EXPECT_EQ(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
363 TEST_F(InstantSearchPrerendererTest
, PrerenderRequestCancelled
) {
364 PrerenderSearchQuery(ASCIIToUTF16("foo"));
366 // Cancel the prerender request.
367 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
368 prerenderer
->Cancel();
369 EXPECT_EQ(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
371 // Open a search results page. Prerendered page does not exists for |url|.
372 // Make sure the browser navigates the current tab to this |url|.
373 GURL
url("https://www.google.com/alt#quux=foo&strk");
374 browser()->OpenURL(content::OpenURLParams(url
, Referrer(), CURRENT_TAB
,
375 content::PAGE_TRANSITION_TYPED
,
377 EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
378 EXPECT_EQ(url
, GetActiveWebContents()->GetURL());
381 TEST_F(InstantSearchPrerendererTest
,
382 CancelPrerenderRequest_SearchQueryMistmatch
) {
383 PrerenderSearchQuery(ASCIIToUTF16("foo"));
385 // Open a search results page. Committed query("pen") doesn't match with the
386 // prerendered search query("foo"). Make sure the InstantSearchPrerenderer
387 // cancels the active prerender request and the browser navigates the active
388 // tab to this |url|.
389 GURL
url("https://www.google.com/alt#quux=pen&strk");
390 browser()->OpenURL(content::OpenURLParams(url
, Referrer(), CURRENT_TAB
,
391 content::PAGE_TRANSITION_TYPED
,
393 EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
394 EXPECT_EQ(url
, GetActiveWebContents()->GetURL());
395 EXPECT_EQ(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
398 TEST_F(InstantSearchPrerendererTest
,
399 CancelPrerenderRequest_EmptySearchQueryCommitted
) {
400 PrerenderSearchQuery(ASCIIToUTF16("foo"));
402 // Open a search results page. Make sure the InstantSearchPrerenderer cancels
403 // the active prerender request upon the receipt of empty search query.
404 GURL
url("https://www.google.com/alt#quux=&strk");
405 browser()->OpenURL(content::OpenURLParams(url
, Referrer(), CURRENT_TAB
,
406 content::PAGE_TRANSITION_TYPED
,
408 EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
409 EXPECT_EQ(url
, GetActiveWebContents()->GetURL());
410 EXPECT_EQ(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());
413 class ReuseInstantSearchBasePageTest
: public InstantSearchPrerendererTest
{
415 ReuseInstantSearchBasePageTest() {}
418 virtual void SetUp() OVERRIDE
{
419 ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
421 "Group1 strk:20 prefetch_results:1 reuse_instant_search_base_page:1"));
422 InstantUnitTestBase::SetUp();
426 TEST_F(ReuseInstantSearchBasePageTest
, CanCommitQuery
) {
428 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
429 base::string16 query
= ASCIIToUTF16("flowers");
430 prerenderer
->Prerender(InstantSuggestion(query
, std::string()));
431 EXPECT_TRUE(prerenderer
->CanCommitQuery(GetActiveWebContents(), query
));
433 // When the Instant search base page has finished loading,
434 // InstantSearchPrerenderer can commit any search query to the prerendered
435 // page (even if it doesn't match the last known suggestion query).
436 EXPECT_TRUE(prerenderer
->CanCommitQuery(GetActiveWebContents(),
437 ASCIIToUTF16("joy")));
438 // Invalid search query committed.
439 EXPECT_FALSE(prerenderer
->CanCommitQuery(GetActiveWebContents(),
443 TEST_F(ReuseInstantSearchBasePageTest
,
444 CanCommitQuery_InstantSearchBasePageLoadInProgress
) {
446 InstantSearchPrerenderer
* prerenderer
= GetInstantSearchPrerenderer();
447 base::string16 query
= ASCIIToUTF16("flowers");
448 prerenderer
->Prerender(InstantSuggestion(query
, std::string()));
450 // When the Instant search base page hasn't finished loading,
451 // InstantSearchPrerenderer cannot commit any search query to the base page.
452 EXPECT_FALSE(prerenderer
->CanCommitQuery(GetActiveWebContents(), query
));
453 EXPECT_FALSE(prerenderer
->CanCommitQuery(GetActiveWebContents(),
454 ASCIIToUTF16("joy")));
457 #if !defined(OS_IOS) && !defined(OS_ANDROID)
458 class TestUsePrerenderPage
: public InstantSearchPrerendererTest
{
460 virtual void SetUp() OVERRIDE
{
461 // Disable query extraction flag in field trials.
462 ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
464 "Group1 strk:20 query_extraction:0 prefetch_results:1"));
465 InstantUnitTestBase::SetUpWithoutQueryExtraction();
469 TEST_F(TestUsePrerenderPage
, ExtractSearchTermsAndUsePrerenderPage
) {
470 PrerenderSearchQuery(ASCIIToUTF16("foo"));
472 // Open a search results page. Query extraction flag is disabled in field
473 // trials. Search results page URL does not contain search terms replacement
474 // key. Make sure UsePrerenderedPage() extracts the search terms from the URL
475 // and uses the prerendered page contents.
476 GURL
url("https://www.google.com/alt#quux=foo");
477 browser()->OpenURL(content::OpenURLParams(url
, Referrer(), CURRENT_TAB
,
478 content::PAGE_TRANSITION_TYPED
,
480 EXPECT_EQ(GetPrerenderURL(), GetActiveWebContents()->GetURL());
481 EXPECT_EQ(static_cast<PrerenderHandle
*>(NULL
), prerender_handle());