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 "chrome/browser/autocomplete/autocomplete_match.h"
      8 #include "chrome/browser/prerender/prerender_handle.h"
      9 #include "chrome/browser/prerender/prerender_manager.h"
     10 #include "chrome/browser/prerender/prerender_manager_factory.h"
     11 #include "chrome/browser/profiles/profile.h"
     12 #include "chrome/browser/search/instant_service.h"
     13 #include "chrome/browser/search/instant_service_factory.h"
     14 #include "chrome/browser/search/search.h"
     15 #include "chrome/browser/ui/browser_navigator.h"
     16 #include "chrome/browser/ui/search/search_tab_helper.h"
     17 
     18 namespace {
     19 
     20 // Returns true if the underlying page supports Instant search.
     21 bool PageSupportsInstantSearch(content::WebContents* contents) {
     22   // Search results page supports Instant search.
     23   return SearchTabHelper::FromWebContents(contents)->IsSearchResultsPage();
     24 }
     25 
     26 }  // namespace
     27 
     28 InstantSearchPrerenderer::InstantSearchPrerenderer(Profile* profile,
     29                                                    const GURL& url)
     30     : profile_(profile),
     31       prerender_url_(url) {
     32 }
     33 
     34 InstantSearchPrerenderer::~InstantSearchPrerenderer() {
     35   if (prerender_handle_)
     36     prerender_handle_->OnCancel();
     37 }
     38 
     39 // static
     40 InstantSearchPrerenderer* InstantSearchPrerenderer::GetForProfile(
     41     Profile* profile) {
     42   DCHECK(profile);
     43   InstantService* instant_service =
     44       InstantServiceFactory::GetForProfile(profile);
     45   return instant_service ? instant_service->instant_search_prerenderer() : NULL;
     46 }
     47 
     48 void InstantSearchPrerenderer::Init(
     49     const content::SessionStorageNamespaceMap& session_storage_namespace_map,
     50     const gfx::Size& size) {
     51   // TODO(kmadhusu): Enable Instant for Incognito profile.
     52   if (profile_->IsOffTheRecord())
     53     return;
     54 
     55   // Only cancel the old prerender after starting the new one, so if the URLs
     56   // are the same, the underlying prerender will be reused.
     57   scoped_ptr<prerender::PrerenderHandle> old_prerender_handle(
     58       prerender_handle_.release());
     59   prerender::PrerenderManager* prerender_manager =
     60       prerender::PrerenderManagerFactory::GetForProfile(profile_);
     61   if (prerender_manager) {
     62     content::SessionStorageNamespace* session_storage_namespace = NULL;
     63     content::SessionStorageNamespaceMap::const_iterator it =
     64         session_storage_namespace_map.find(std::string());
     65     if (it != session_storage_namespace_map.end())
     66       session_storage_namespace = it->second.get();
     67 
     68     prerender_handle_.reset(prerender_manager->AddPrerenderForInstant(
     69         prerender_url_, session_storage_namespace, size));
     70   }
     71   if (old_prerender_handle)
     72     old_prerender_handle->OnCancel();
     73 }
     74 
     75 void InstantSearchPrerenderer::Cancel() {
     76   if (!prerender_handle_)
     77     return;
     78 
     79   last_instant_suggestion_ = InstantSuggestion();
     80   prerender_handle_->OnCancel();
     81   prerender_handle_.reset();
     82 }
     83 
     84 void InstantSearchPrerenderer::Prerender(const InstantSuggestion& suggestion) {
     85   if (!prerender_handle_)
     86     return;
     87 
     88   if (last_instant_suggestion_.text == suggestion.text)
     89     return;
     90 
     91   if (last_instant_suggestion_.text.empty() &&
     92       !prerender_handle_->IsFinishedLoading())
     93     return;
     94 
     95   if (!prerender_contents())
     96     return;
     97 
     98   last_instant_suggestion_ = suggestion;
     99   SearchTabHelper::FromWebContents(prerender_contents())->
    100       SetSuggestionToPrefetch(suggestion);
    101 }
    102 
    103 void InstantSearchPrerenderer::Commit(const base::string16& query) {
    104   DCHECK(prerender_handle_);
    105   DCHECK(prerender_contents());
    106   SearchTabHelper::FromWebContents(prerender_contents())->Submit(query);
    107 }
    108 
    109 bool InstantSearchPrerenderer::CanCommitQuery(
    110     content::WebContents* source,
    111     const base::string16& query) const {
    112   if (!source || query.empty() || !prerender_handle_ ||
    113       !prerender_handle_->IsFinishedLoading() ||
    114       !prerender_contents() || !QueryMatchesPrefetch(query)) {
    115     return false;
    116   }
    117 
    118   // InstantSearchPrerenderer can commit query to the prerendered page only if
    119   // the underlying |source| page doesn't support Instant search.
    120   return !PageSupportsInstantSearch(source);
    121 }
    122 
    123 bool InstantSearchPrerenderer::UsePrerenderedPage(
    124     const GURL& url,
    125     chrome::NavigateParams* params) {
    126   base::string16 search_terms =
    127       chrome::ExtractSearchTermsFromURL(profile_, url);
    128   prerender::PrerenderManager* prerender_manager =
    129       prerender::PrerenderManagerFactory::GetForProfile(profile_);
    130   if (search_terms.empty() || !params->target_contents ||
    131       !prerender_contents() || !prerender_manager ||
    132       !QueryMatchesPrefetch(search_terms) ||
    133       params->disposition != CURRENT_TAB) {
    134     Cancel();
    135     return false;
    136   }
    137 
    138   bool success = prerender_manager->MaybeUsePrerenderedPage(
    139       prerender_contents()->GetURL(), params);
    140   prerender_handle_.reset();
    141   return success;
    142 }
    143 
    144 bool InstantSearchPrerenderer::IsAllowed(const AutocompleteMatch& match,
    145                                          content::WebContents* source) const {
    146   return source && AutocompleteMatch::IsSearchType(match.type) &&
    147       !PageSupportsInstantSearch(source);
    148 }
    149 
    150 content::WebContents* InstantSearchPrerenderer::prerender_contents() const {
    151   return (prerender_handle_ && prerender_handle_->contents()) ?
    152       prerender_handle_->contents()->prerender_contents() : NULL;
    153 }
    154 
    155 bool InstantSearchPrerenderer::QueryMatchesPrefetch(
    156     const base::string16& query) const {
    157   if (chrome::ShouldReuseInstantSearchBasePage())
    158     return true;
    159   return last_instant_suggestion_.text == query;
    160 }
    161