Home | History | Annotate | Download | only in search
      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 namespace {
     36 
     37 using content::Referrer;
     38 using prerender::Origin;
     39 using prerender::PrerenderContents;
     40 using prerender::PrerenderHandle;
     41 using prerender::PrerenderManager;
     42 using prerender::PrerenderManagerFactory;
     43 
     44 class DummyPrerenderContents : public PrerenderContents {
     45  public:
     46   DummyPrerenderContents(
     47       PrerenderManager* prerender_manager,
     48       Profile* profile,
     49       const GURL& url,
     50       const Referrer& referrer,
     51       Origin origin,
     52       bool call_did_finish_load,
     53       const content::SessionStorageNamespaceMap& session_storage_namespace_map);
     54 
     55   virtual void StartPrerendering(
     56       int ALLOW_UNUSED creator_child_id,
     57       const gfx::Size& ALLOW_UNUSED size,
     58       content::SessionStorageNamespace* session_storage_namespace) OVERRIDE;
     59   virtual bool GetChildId(int* child_id) const OVERRIDE;
     60   virtual bool GetRouteId(int* route_id) const OVERRIDE;
     61 
     62  private:
     63   Profile* profile_;
     64   const GURL url_;
     65   bool call_did_finish_load_;
     66   content::SessionStorageNamespaceMap session_storage_namespace_map_;
     67 
     68   DISALLOW_COPY_AND_ASSIGN(DummyPrerenderContents);
     69 };
     70 
     71 class DummyPrerenderContentsFactory : public PrerenderContents::Factory {
     72  public:
     73   DummyPrerenderContentsFactory(
     74       bool call_did_finish_load,
     75       const content::SessionStorageNamespaceMap& session_storage_namespace_map)
     76       : call_did_finish_load_(call_did_finish_load),
     77         session_storage_namespace_map_(session_storage_namespace_map) {
     78   }
     79 
     80   virtual PrerenderContents* CreatePrerenderContents(
     81       PrerenderManager* prerender_manager,
     82       Profile* profile,
     83       const GURL& url,
     84       const Referrer& referrer,
     85       Origin origin,
     86       uint8 experiment_id) OVERRIDE;
     87 
     88  private:
     89   bool call_did_finish_load_;
     90   content::SessionStorageNamespaceMap session_storage_namespace_map_;
     91 
     92   DISALLOW_COPY_AND_ASSIGN(DummyPrerenderContentsFactory);
     93 };
     94 
     95 DummyPrerenderContents::DummyPrerenderContents(
     96     PrerenderManager* prerender_manager,
     97     Profile* profile,
     98     const GURL& url,
     99     const Referrer& referrer,
    100     Origin origin,
    101     bool call_did_finish_load,
    102     const content::SessionStorageNamespaceMap& session_storage_namespace_map)
    103     : PrerenderContents(prerender_manager, profile, url, referrer, origin,
    104                         PrerenderManager::kNoExperiment),
    105       profile_(profile),
    106       url_(url),
    107       call_did_finish_load_(call_did_finish_load),
    108       session_storage_namespace_map_(session_storage_namespace_map) {
    109 }
    110 
    111 void DummyPrerenderContents::StartPrerendering(
    112     int ALLOW_UNUSED creator_child_id,
    113     const gfx::Size& ALLOW_UNUSED size,
    114     content::SessionStorageNamespace* session_storage_namespace) {
    115   prerender_contents_.reset(content::WebContents::CreateWithSessionStorage(
    116       content::WebContents::CreateParams(profile_),
    117       session_storage_namespace_map_));
    118   content::NavigationController::LoadURLParams params(url_);
    119   prerender_contents_->GetController().LoadURLWithParams(params);
    120   SearchTabHelper::CreateForWebContents(prerender_contents_.get());
    121 
    122   AddObserver(prerender_manager()->prerender_tracker());
    123   prerendering_has_started_ = true;
    124   DCHECK(session_storage_namespace);
    125   session_storage_namespace_id_ = session_storage_namespace->id();
    126   NotifyPrerenderStart();
    127 
    128   if (call_did_finish_load_)
    129     DidFinishLoad(1, url_, true, NULL);
    130 }
    131 
    132 bool DummyPrerenderContents::GetChildId(int* child_id) const {
    133   *child_id = 1;
    134   return true;
    135 }
    136 
    137 bool DummyPrerenderContents::GetRouteId(int* route_id) const {
    138   *route_id = 1;
    139   return true;
    140 }
    141 
    142 PrerenderContents* DummyPrerenderContentsFactory::CreatePrerenderContents(
    143     PrerenderManager* prerender_manager,
    144     Profile* profile,
    145     const GURL& url,
    146     const Referrer& referrer,
    147     Origin origin,
    148     uint8 experiment_id) {
    149   return new DummyPrerenderContents(prerender_manager, profile, url, referrer,
    150                                     origin, call_did_finish_load_,
    151                                     session_storage_namespace_map_);
    152 }
    153 
    154 }  // namespace
    155 
    156 class InstantSearchPrerendererTest : public InstantUnitTestBase {
    157  public:
    158   InstantSearchPrerendererTest() {}
    159 
    160  protected:
    161   virtual void SetUp() OVERRIDE {
    162     ASSERT_TRUE(base::FieldTrialList::CreateFieldTrial(
    163         "EmbeddedSearch",
    164         "Group1 strk:20 use_cacheable_ntp:1 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 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 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   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(), string16()));
    282 }
    283 
    284 TEST_F(InstantSearchPrerendererTest, CommitQuery) {
    285   string16 query = ASCIIToUTF16("flowers");
    286   PrerenderSearchQuery(query);
    287   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
    288   prerenderer->Commit(query);
    289   EXPECT_TRUE(MessageWasSent(ChromeViewMsg_SearchBoxSubmit::ID));
    290 }
    291 
    292 TEST_F(InstantSearchPrerendererTest, CancelPrerenderRequestOnTabChangeEvent) {
    293   Init(true, true);
    294   EXPECT_NE(static_cast<PrerenderHandle*>(NULL), prerender_handle());
    295 
    296   // Add a new tab to deactivate the current tab.
    297   AddTab(browser(), GURL(content::kAboutBlankURL));
    298   EXPECT_EQ(2, browser()->tab_strip_model()->count());
    299 
    300   // Make sure the pending prerender request is cancelled.
    301   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
    302 }
    303 
    304 TEST_F(InstantSearchPrerendererTest, CancelPendingPrerenderRequest) {
    305   Init(true, true);
    306   EXPECT_NE(static_cast<PrerenderHandle*>(NULL), prerender_handle());
    307 
    308   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
    309   prerenderer->Cancel();
    310   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
    311 }
    312 
    313 TEST_F(InstantSearchPrerendererTest, PrerenderingAllowed) {
    314   Init(true, true);
    315   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
    316   content::WebContents* active_tab = GetActiveWebContents();
    317   EXPECT_EQ(GURL(content::kAboutBlankURL), active_tab->GetURL());
    318 
    319   // Allow prerendering only for search type AutocompleteMatch suggestions.
    320   AutocompleteMatch search_type_match(NULL, 1100, false,
    321                                       AutocompleteMatchType::SEARCH_SUGGEST);
    322   EXPECT_TRUE(AutocompleteMatch::IsSearchType(search_type_match.type));
    323   EXPECT_TRUE(prerenderer->IsAllowed(search_type_match, active_tab));
    324 
    325   AutocompleteMatch url_type_match(NULL, 1100, true,
    326                                    AutocompleteMatchType::URL_WHAT_YOU_TYPED);
    327   EXPECT_FALSE(AutocompleteMatch::IsSearchType(url_type_match.type));
    328   EXPECT_FALSE(prerenderer->IsAllowed(url_type_match, active_tab));
    329 
    330   // Search results page supports Instant search. InstantSearchPrerenderer is
    331   // used only when the underlying page doesn't support Instant.
    332   NavigateAndCommitActiveTab(GURL("https://www.google.com/alt#quux=foo&strk"));
    333   active_tab = GetActiveWebContents();
    334   EXPECT_FALSE(chrome::GetSearchTermsFromURL(profile(), active_tab->GetURL())
    335       .empty());
    336   EXPECT_FALSE(chrome::ShouldPrefetchSearchResultsOnSRP());
    337   EXPECT_FALSE(prerenderer->IsAllowed(search_type_match, active_tab));
    338 }
    339 
    340 TEST_F(InstantSearchPrerendererTest, UsePrerenderPage) {
    341   PrerenderSearchQuery(ASCIIToUTF16("foo"));
    342 
    343   // Open a search results page. A prerendered page exists for |url|. Make sure
    344   // the browser swaps the current tab contents with the prerendered contents.
    345   GURL url("https://www.google.com/alt#quux=foo&strk");
    346   browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
    347                                             content::PAGE_TRANSITION_TYPED,
    348                                             false));
    349   EXPECT_EQ(GetPrerenderURL(), GetActiveWebContents()->GetURL());
    350   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
    351 }
    352 
    353 TEST_F(InstantSearchPrerendererTest, PrerenderRequestCancelled) {
    354   PrerenderSearchQuery(ASCIIToUTF16("foo"));
    355 
    356   // Cancel the prerender request.
    357   InstantSearchPrerenderer* prerenderer = GetInstantSearchPrerenderer();
    358   prerenderer->Cancel();
    359   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
    360 
    361   // Open a search results page. Prerendered page does not exists for |url|.
    362   // Make sure the browser navigates the current tab to this |url|.
    363   GURL url("https://www.google.com/alt#quux=foo&strk");
    364   browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
    365                                             content::PAGE_TRANSITION_TYPED,
    366                                             false));
    367   EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
    368   EXPECT_EQ(url, GetActiveWebContents()->GetURL());
    369 }
    370 
    371 TEST_F(InstantSearchPrerendererTest,
    372        CancelPrerenderRequest_SearchQueryMistmatch) {
    373   PrerenderSearchQuery(ASCIIToUTF16("foo"));
    374 
    375   // Open a search results page. Committed query("pen") doesn't match with the
    376   // prerendered search query("foo"). Make sure the InstantSearchPrerenderer
    377   // cancels the active prerender request and the browser navigates the active
    378   // tab to this |url|.
    379   GURL url("https://www.google.com/alt#quux=pen&strk");
    380   browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
    381                                             content::PAGE_TRANSITION_TYPED,
    382                                             false));
    383   EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
    384   EXPECT_EQ(url, GetActiveWebContents()->GetURL());
    385   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
    386 }
    387 
    388 TEST_F(InstantSearchPrerendererTest,
    389        CancelPrerenderRequest_EmptySearchQueryCommitted) {
    390   PrerenderSearchQuery(ASCIIToUTF16("foo"));
    391 
    392   // Open a search results page. Make sure the InstantSearchPrerenderer cancels
    393   // the active prerender request upon the receipt of empty search query.
    394   GURL url("https://www.google.com/alt#quux=&strk");
    395   browser()->OpenURL(content::OpenURLParams(url, Referrer(), CURRENT_TAB,
    396                                             content::PAGE_TRANSITION_TYPED,
    397                                             false));
    398   EXPECT_NE(GetPrerenderURL(), GetActiveWebContents()->GetURL());
    399   EXPECT_EQ(url, GetActiveWebContents()->GetURL());
    400   EXPECT_EQ(static_cast<PrerenderHandle*>(NULL), prerender_handle());
    401 }
    402