f5437f2952b6dfc8b58bce0f39cc99bc9b217a1c
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / search / instant_search_prerenderer_unittest.cc
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/instant_search_prerenderer.h"
6
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"
34
35 using base::ASCIIToUTF16;
36
37 namespace {
38
39 using content::Referrer;
40 using prerender::Origin;
41 using prerender::PrerenderContents;
42 using prerender::PrerenderHandle;
43 using prerender::PrerenderManager;
44 using prerender::PrerenderManagerFactory;
45
46 class DummyPrerenderContents : public PrerenderContents {
47  public:
48   DummyPrerenderContents(
49       PrerenderManager* prerender_manager,
50       Profile* profile,
51       const GURL& url,
52       const Referrer& referrer,
53       Origin origin,
54       bool call_did_finish_load,
55       const content::SessionStorageNamespaceMap& session_storage_namespace_map);
56
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;
63
64  private:
65   Profile* profile_;
66   const GURL url_;
67   bool call_did_finish_load_;
68   content::SessionStorageNamespaceMap session_storage_namespace_map_;
69
70   DISALLOW_COPY_AND_ASSIGN(DummyPrerenderContents);
71 };
72
73 class DummyPrerenderContentsFactory : public PrerenderContents::Factory {
74  public:
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) {
80   }
81
82   virtual PrerenderContents* CreatePrerenderContents(
83       PrerenderManager* prerender_manager,
84       Profile* profile,
85       const GURL& url,
86       const Referrer& referrer,
87       Origin origin,
88       uint8 experiment_id) OVERRIDE;
89
90  private:
91   bool call_did_finish_load_;
92   content::SessionStorageNamespaceMap session_storage_namespace_map_;
93
94   DISALLOW_COPY_AND_ASSIGN(DummyPrerenderContentsFactory);
95 };
96
97 DummyPrerenderContents::DummyPrerenderContents(
98     PrerenderManager* prerender_manager,
99     Profile* profile,
100     const GURL& url,
101     const Referrer& referrer,
102     Origin origin,
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),
107       profile_(profile),
108       url_(url),
109       call_did_finish_load_(call_did_finish_load),
110       session_storage_namespace_map_(session_storage_namespace_map) {
111 }
112
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());
123
124   prerendering_has_started_ = true;
125   DCHECK(session_storage_namespace);
126   session_storage_namespace_id_ = session_storage_namespace->id();
127   NotifyPrerenderStart();
128
129   if (call_did_finish_load_)
130     DidFinishLoad(1, url_, true, NULL);
131 }
132
133 bool DummyPrerenderContents::GetChildId(int* child_id) const {
134   *child_id = 1;
135   return true;
136 }
137
138 bool DummyPrerenderContents::GetRouteId(int* route_id) const {
139   *route_id = 1;
140   return true;
141 }
142
143 PrerenderContents* DummyPrerenderContentsFactory::CreatePrerenderContents(
144     PrerenderManager* prerender_manager,
145     Profile* profile,
146     const GURL& url,
147     const Referrer& referrer,
148     Origin origin,
149     uint8 experiment_id) {
150   return new DummyPrerenderContents(prerender_manager, profile, url, referrer,
151                                     origin, call_did_finish_load_,
152                                     session_storage_namespace_map_);
153 }
154
155 }  // namespace
156
157 class InstantSearchPrerendererTest : public InstantUnitTestBase {
158  public:
159   InstantSearchPrerendererTest() {}
160
161  protected:
162   virtual void SetUp() OVERRIDE {
163     ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
164         "EmbeddedSearch", "Group1 strk:20 prefetch_results:1"));
165     InstantUnitTestBase::SetUp();
166   }
167
168   void Init(bool prerender_search_results_base_page,
169             bool call_did_finish_load) {
170     AddTab(browser(), GURL(content::kAboutBlankURL));
171
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));
180
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());
185     }
186   }
187
188   InstantSearchPrerenderer* GetInstantSearchPrerenderer() {
189     return instant_service_->instant_search_prerenderer();
190   }
191
192   const GURL& GetPrerenderURL() {
193     return GetInstantSearchPrerenderer()->prerender_url_;
194   }
195
196   void SetLastQuery(const base::string16& query) {
197     GetInstantSearchPrerenderer()->last_instant_suggestion_ =
198         InstantSuggestion(query, std::string());
199   }
200
201   content::WebContents* prerender_contents() {
202     return GetInstantSearchPrerenderer()->prerender_contents();
203   }
204
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;
210   }
211
212   content::WebContents* GetActiveWebContents() const {
213     return browser()->tab_strip_model()->GetWebContentsAt(0);
214   }
215
216   PrerenderHandle* prerender_handle() {
217     return GetInstantSearchPrerenderer()->prerender_handle_.get();
218   }
219
220   void PrerenderSearchQuery(const base::string16& query) {
221     Init(true, true);
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());
227   }
228 };
229
230 TEST_F(InstantSearchPrerendererTest, GetSearchTermsFromPrerenderedPage) {
231   Init(false, false);
232   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
233   GURL url(GetPrerenderURL());
234   EXPECT_EQ(GURL("https://www.google.com/instant?ion=1&foo=foo#foo=foo&strk"),
235             url);
236   EXPECT_EQ(UTF16ToASCII(prerenderer->get_last_query()),
237             UTF16ToASCII(chrome::GetSearchTermsFromURL(profile(), url)));
238
239   // Assume the prerendered page prefetched search results for the query
240   // "flowers".
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)));
245 }
246
247 TEST_F(InstantSearchPrerendererTest, PrefetchSearchResults) {
248   Init(true, true);
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));
256 }
257
258 TEST_F(InstantSearchPrerendererTest, DoNotPrefetchSearchResults) {
259   Init(true, false);
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));
268 }
269
270 TEST_F(InstantSearchPrerendererTest, CanCommitQuery) {
271   Init(true, true);
272   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
273   base::string16 query = ASCIIToUTF16("flowers");
274   prerenderer->Prerender(InstantSuggestion(query, std::string()));
275   EXPECT_TRUE(prerenderer->CanCommitQuery(GetActiveWebContents(), query));
276
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(),
282                                            base::string16()));
283 }
284
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));
291 }
292
293 TEST_F(InstantSearchPrerendererTest, CancelPrerenderRequestOnTabChangeEvent) {
294   Init(true, true);
295   EXPECT_NE(static_cast<PrerenderHandle*>(NULL), prerender_handle());
296
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());
300
301   // Make sure the pending prerender request is cancelled.
302   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
303 }
304
305 TEST_F(InstantSearchPrerendererTest, CancelPendingPrerenderRequest) {
306   Init(true, true);
307   EXPECT_NE(static_cast<PrerenderHandle*>(NULL), prerender_handle());
308
309   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
310   prerenderer->Cancel();
311   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
312 }
313
314 TEST_F(InstantSearchPrerendererTest, PrerenderingAllowed) {
315   Init(true, true);
316   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
317   content::WebContents* active_tab = GetActiveWebContents();
318   EXPECT_EQ(GURL(content::kAboutBlankURL), active_tab->GetURL());
319
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));
325
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));
330
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())
336       .empty());
337   EXPECT_FALSE(chrome::ShouldPrefetchSearchResultsOnSRP());
338   EXPECT_FALSE(prerenderer->IsAllowed(search_type_match, active_tab));
339 }
340
341 TEST_F(InstantSearchPrerendererTest, UsePrerenderPage) {
342   PrerenderSearchQuery(ASCIIToUTF16("foo"));
343
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,
349                                             false));
350   EXPECT_EQ(GetPrerenderURL(), GetActiveWebContents()->GetURL());
351   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
352 }
353
354 TEST_F(InstantSearchPrerendererTest, PrerenderRequestCancelled) {
355   PrerenderSearchQuery(ASCIIToUTF16("foo"));
356
357   // Cancel the prerender request.
358   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
359   prerenderer->Cancel();
360   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
361
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,
367                                             false));
368   EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
369   EXPECT_EQ(url, GetActiveWebContents()->GetURL());
370 }
371
372 TEST_F(InstantSearchPrerendererTest,
373        CancelPrerenderRequest_SearchQueryMistmatch) {
374   PrerenderSearchQuery(ASCIIToUTF16("foo"));
375
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,
383                                             false));
384   EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
385   EXPECT_EQ(url, GetActiveWebContents()->GetURL());
386   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
387 }
388
389 TEST_F(InstantSearchPrerendererTest,
390        CancelPrerenderRequest_EmptySearchQueryCommitted) {
391   PrerenderSearchQuery(ASCIIToUTF16("foo"));
392
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,
398                                             false));
399   EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
400   EXPECT_EQ(url, GetActiveWebContents()->GetURL());
401   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
402 }
403
404 class ReuseInstantSearchBasePageTest : public InstantSearchPrerendererTest {
405   public:
406    ReuseInstantSearchBasePageTest() {}
407
408   protected:
409    virtual void SetUp() OVERRIDE {
410     ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
411         "EmbeddedSearch",
412         "Group1 strk:20 prefetch_results:1 reuse_instant_search_base_page:1"));
413     InstantUnitTestBase::SetUp();
414    }
415 };
416
417 TEST_F(ReuseInstantSearchBasePageTest, CanCommitQuery) {
418   Init(true, true);
419   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
420   base::string16 query = ASCIIToUTF16("flowers");
421   prerenderer->Prerender(InstantSuggestion(query, std::string()));
422   EXPECT_TRUE(prerenderer->CanCommitQuery(GetActiveWebContents(), query));
423
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(),
431                                            base::string16()));
432 }
433
434 TEST_F(ReuseInstantSearchBasePageTest,
435        CanCommitQuery_InstantSearchBasePageLoadInProgress) {
436   Init(true, false);
437   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
438   base::string16 query = ASCIIToUTF16("flowers");
439   prerenderer->Prerender(InstantSuggestion(query, std::string()));
440
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")));
446 }