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/prerender/prerender_contents.h"
14 #include "chrome/browser/prerender/prerender_handle.h"
15 #include "chrome/browser/prerender/prerender_manager.h"
16 #include "chrome/browser/prerender/prerender_manager_factory.h"
17 #include "chrome/browser/prerender/prerender_origin.h"
18 #include "chrome/browser/prerender/prerender_tab_helper.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 "components/omnibox/autocomplete_match.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(prerender_contents_->GetMainFrame(), url_);
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("EmbeddedSearch",
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_TRUE(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 ui::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 ui::PAGE_TRANSITION_TYPED,
377 EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
378 EXPECT_EQ(url, GetActiveWebContents()->GetURL());
381 TEST_F(InstantSearchPrerendererTest,
382 UsePrerenderedPage_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 browser swaps the current
387 // tab contents with the prerendered contents.
388 GURL url("https://www.google.com/alt#quux=pen&strk");
389 browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
390 ui::PAGE_TRANSITION_TYPED,
392 EXPECT_EQ(GetPrerenderURL(), GetActiveWebContents()->GetURL());
393 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
396 TEST_F(InstantSearchPrerendererTest,
397 CancelPrerenderRequest_EmptySearchQueryCommitted) {
398 PrerenderSearchQuery(ASCIIToUTF16("foo"));
400 // Open a search results page. Make sure the InstantSearchPrerenderer cancels
401 // the active prerender request upon the receipt of empty search query.
402 GURL url("https://www.google.com/alt#quux=&strk");
403 browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
404 ui::PAGE_TRANSITION_TYPED,
406 EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
407 EXPECT_EQ(url, GetActiveWebContents()->GetURL());
408 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
411 TEST_F(InstantSearchPrerendererTest,
412 CancelPrerenderRequest_UnsupportedDispositions) {
413 PrerenderSearchQuery(ASCIIToUTF16("pen"));
415 // Open a search results page. Make sure the InstantSearchPrerenderer cancels
416 // the active prerender request for unsupported window dispositions.
417 GURL url("https://www.google.com/alt#quux=pen&strk");
418 browser()->OpenURL(content::OpenURLParams(url, Referrer(), NEW_FOREGROUND_TAB,
419 ui::PAGE_TRANSITION_TYPED,
421 content::WebContents* new_tab =
422 browser()->tab_strip_model()->GetWebContentsAt(1);
423 EXPECT_NE(GetPrerenderURL(), new_tab->GetURL());
424 EXPECT_EQ(url, new_tab->GetURL());
425 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
428 class ReuseInstantSearchBasePageTest : public InstantSearchPrerendererTest {
430 ReuseInstantSearchBasePageTest() {}
433 virtual void SetUp() OVERRIDE {
434 ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial("EmbeddedSearch",
436 InstantUnitTestBase::SetUp();
440 TEST_F(ReuseInstantSearchBasePageTest, CanCommitQuery) {
442 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
443 base::string16 query = ASCIIToUTF16("flowers");
444 prerenderer->Prerender(InstantSuggestion(query, std::string()));
445 EXPECT_TRUE(prerenderer->CanCommitQuery(GetActiveWebContents(), query));
447 // When the Instant search base page has finished loading,
448 // InstantSearchPrerenderer can commit any search query to the prerendered
449 // page (even if it doesn't match the last known suggestion query).
450 EXPECT_TRUE(prerenderer->CanCommitQuery(GetActiveWebContents(),
451 ASCIIToUTF16("joy")));
452 // Invalid search query committed.
453 EXPECT_FALSE(prerenderer->CanCommitQuery(GetActiveWebContents(),
457 TEST_F(ReuseInstantSearchBasePageTest,
458 CanCommitQuery_InstantSearchBasePageLoadInProgress) {
460 InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
461 base::string16 query = ASCIIToUTF16("flowers");
462 prerenderer->Prerender(InstantSuggestion(query, std::string()));
464 // When the Instant search base page hasn't finished loading,
465 // InstantSearchPrerenderer cannot commit any search query to the base page.
466 EXPECT_FALSE(prerenderer->CanCommitQuery(GetActiveWebContents(), query));
467 EXPECT_FALSE(prerenderer->CanCommitQuery(GetActiveWebContents(),
468 ASCIIToUTF16("joy")));
471 #if !defined(OS_IOS) && !defined(OS_ANDROID)
472 class TestUsePrerenderPage : public InstantSearchPrerendererTest {
474 virtual void SetUp() OVERRIDE {
475 // Disable query extraction flag in field trials.
476 ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
477 "EmbeddedSearch", "Group1 strk:20 query_extraction:0"));
478 InstantUnitTestBase::SetUpWithoutQueryExtraction();
482 TEST_F(TestUsePrerenderPage, ExtractSearchTermsAndUsePrerenderPage) {
483 PrerenderSearchQuery(ASCIIToUTF16("foo"));
485 // Open a search results page. Query extraction flag is disabled in field
486 // trials. Search results page URL does not contain search terms replacement
487 // key. Make sure UsePrerenderedPage() extracts the search terms from the URL
488 // and uses the prerendered page contents.
489 GURL url("https://www.google.com/alt#quux=foo");
490 browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
491 ui::PAGE_TRANSITION_TYPED,
493 EXPECT_EQ(GetPrerenderURL(), GetActiveWebContents()->GetURL());
494 EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());