Home | History | Annotate | Download | only in main
      1 // Copyright 2014 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 "athena/main/url_search_provider.h"
      6 
      7 #include "athena/activity/public/activity.h"
      8 #include "athena/activity/public/activity_factory.h"
      9 #include "athena/content/public/scheme_classifier_factory.h"
     10 #include "base/strings/utf_string_conversions.h"
     11 #include "base/values.h"
     12 #include "components/metrics/proto/omnibox_event.pb.h"
     13 #include "components/metrics/proto/omnibox_input_type.pb.h"
     14 #include "components/omnibox/autocomplete_input.h"
     15 #include "components/omnibox/autocomplete_provider_client.h"
     16 #include "components/omnibox/search_provider.h"
     17 #include "components/search_engines/search_terms_data.h"
     18 #include "components/search_engines/template_url_service.h"
     19 #include "components/search_engines/template_url_service_client.h"
     20 #include "content/public/browser/browser_context.h"
     21 #include "ui/app_list/search_result.h"
     22 #include "ui/base/resource/resource_bundle.h"
     23 #include "url/gurl.h"
     24 
     25 namespace athena {
     26 
     27 namespace {
     28 
     29 // This constant was copied from HistoryURLProvider.
     30 // TODO(hashimoto): Componentize HistoryURLProvider and delete this.
     31 const int kScoreForWhatYouTypedResult = 1203;
     32 
     33 // The SearchTermsData implementation for Athena.
     34 class AthenaSearchTermsData : public SearchTermsData {
     35  public:
     36   // SearchTermsData:
     37   virtual std::string GetSuggestClient() const OVERRIDE {
     38     return "chrome";
     39   }
     40 };
     41 
     42 // The templateURLServiceClient for Athena. Mainly for the interaction with
     43 // history module (see chrome/browser/search_engines for Chrome implementation).
     44 // TODO(mukai): Implement the contents of this class when it's necessary.
     45 class AthenaTemplateURLServiceClient : public TemplateURLServiceClient {
     46  public:
     47   AthenaTemplateURLServiceClient() {}
     48   virtual ~AthenaTemplateURLServiceClient() {}
     49 
     50  private:
     51   // TemplateURLServiceClient:
     52   virtual void SetOwner(TemplateURLService* owner) OVERRIDE {}
     53   virtual void DeleteAllSearchTermsForKeyword(TemplateURLID id) OVERRIDE {}
     54   virtual void SetKeywordSearchTermsForURL(
     55       const GURL& url,
     56       TemplateURLID id,
     57       const base::string16& term) OVERRIDE {}
     58   virtual void AddKeywordGeneratedVisit(const GURL& url) OVERRIDE {}
     59   virtual void RestoreExtensionInfoIfNecessary(
     60       TemplateURL* template_url) OVERRIDE {}
     61 
     62   DISALLOW_COPY_AND_ASSIGN(AthenaTemplateURLServiceClient);
     63 };
     64 
     65 // The AutocompleteProviderClient for Athena.
     66 class AthenaAutocompleteProviderClient : public AutocompleteProviderClient {
     67  public:
     68   explicit AthenaAutocompleteProviderClient(
     69       content::BrowserContext* browser_context)
     70       : browser_context_(browser_context),
     71         scheme_classifier_(CreateSchemeClassifier(browser_context)) {}
     72   virtual ~AthenaAutocompleteProviderClient() {}
     73 
     74   virtual net::URLRequestContextGetter* RequestContext() OVERRIDE {
     75     return browser_context_->GetRequestContext();
     76   }
     77   virtual bool IsOffTheRecord() OVERRIDE {
     78     return browser_context_->IsOffTheRecord();
     79   }
     80   virtual std::string AcceptLanguages() OVERRIDE {
     81     // TODO(hashimoto): Return the value stored in the prefs.
     82     return "en-US";
     83   }
     84   virtual bool SearchSuggestEnabled() OVERRIDE { return true; }
     85   virtual bool ShowBookmarkBar() OVERRIDE { return false; }
     86   virtual const AutocompleteSchemeClassifier& SchemeClassifier() OVERRIDE {
     87     return *scheme_classifier_;
     88   }
     89   virtual void Classify(
     90       const base::string16& text,
     91       bool prefer_keyword,
     92       bool allow_exact_keyword_match,
     93       metrics::OmniboxEventProto::PageClassification page_classification,
     94       AutocompleteMatch* match,
     95       GURL* alternate_nav_url) OVERRIDE {}
     96   virtual history::URLDatabase* InMemoryDatabase() OVERRIDE { return NULL; }
     97   virtual void DeleteMatchingURLsForKeywordFromHistory(
     98       history::KeywordID keyword_id,
     99       const base::string16& term) OVERRIDE {}
    100   virtual bool TabSyncEnabledAndUnencrypted() OVERRIDE { return false; }
    101   virtual void PrefetchImage(const GURL& url) OVERRIDE {}
    102 
    103  private:
    104   content::BrowserContext* browser_context_;
    105   scoped_ptr<AutocompleteSchemeClassifier> scheme_classifier_;
    106 
    107   DISALLOW_COPY_AND_ASSIGN(AthenaAutocompleteProviderClient);
    108 };
    109 
    110 int ACMatchStyleToTagStyle(int styles) {
    111   int tag_styles = 0;
    112   if (styles & ACMatchClassification::URL)
    113     tag_styles |= app_list::SearchResult::Tag::URL;
    114   if (styles & ACMatchClassification::MATCH)
    115     tag_styles |= app_list::SearchResult::Tag::MATCH;
    116   if (styles & ACMatchClassification::DIM)
    117     tag_styles |= app_list::SearchResult::Tag::DIM;
    118 
    119   return tag_styles;
    120 }
    121 
    122 // Translates ACMatchClassifications into SearchResult tags.
    123 void ACMatchClassificationsToTags(
    124     const base::string16& text,
    125     const ACMatchClassifications& text_classes,
    126     app_list::SearchResult::Tags* tags) {
    127   int tag_styles = app_list::SearchResult::Tag::NONE;
    128   size_t tag_start = 0;
    129 
    130   for (size_t i = 0; i < text_classes.size(); ++i) {
    131     const ACMatchClassification& text_class = text_classes[i];
    132 
    133     // Closes current tag.
    134     if (tag_styles != app_list::SearchResult::Tag::NONE) {
    135       tags->push_back(app_list::SearchResult::Tag(
    136           tag_styles, tag_start, text_class.offset));
    137       tag_styles = app_list::SearchResult::Tag::NONE;
    138     }
    139 
    140     if (text_class.style == ACMatchClassification::NONE)
    141       continue;
    142 
    143     tag_start = text_class.offset;
    144     tag_styles = ACMatchStyleToTagStyle(text_class.style);
    145   }
    146 
    147   if (tag_styles != app_list::SearchResult::Tag::NONE) {
    148     tags->push_back(app_list::SearchResult::Tag(
    149         tag_styles, tag_start, text.length()));
    150   }
    151 }
    152 
    153 class UrlSearchResult : public app_list::SearchResult {
    154  public:
    155   UrlSearchResult(content::BrowserContext* browser_context,
    156                   const AutocompleteMatch& match)
    157       : browser_context_(browser_context),
    158         match_(match) {
    159     set_id(match_.destination_url.spec());
    160 
    161     // Derive relevance from omnibox relevance and normalize it to [0, 1].
    162     // The magic number 1500 is the highest score of an omnibox result.
    163     // See comments in autocomplete_provider.h.
    164     set_relevance(match_.relevance / 1500.0);
    165 
    166     UpdateIcon();
    167     UpdateTitleAndDetails();
    168   }
    169 
    170   virtual ~UrlSearchResult() {}
    171 
    172  private:
    173   // Overriddenn from app_list::SearchResult:
    174   virtual void Open(int event_flags) OVERRIDE {
    175     Activity* activity = ActivityFactory::Get()->CreateWebActivity(
    176         browser_context_, base::string16(), match_.destination_url);
    177     Activity::Show(activity);
    178   }
    179 
    180   void UpdateIcon() {
    181     SetIcon(*ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(
    182         AutocompleteMatch::TypeToIcon(match_.type)));
    183   }
    184 
    185   void UpdateTitleAndDetails() {
    186     set_title(match_.contents);
    187     SearchResult::Tags title_tags;
    188     ACMatchClassificationsToTags(match_.contents,
    189                                  match_.contents_class,
    190                                  &title_tags);
    191     set_title_tags(title_tags);
    192 
    193     set_details(match_.description);
    194     SearchResult::Tags details_tags;
    195     ACMatchClassificationsToTags(match_.description,
    196                                  match_.description_class,
    197                                  &details_tags);
    198     set_details_tags(details_tags);
    199   }
    200 
    201   content::BrowserContext* browser_context_;
    202   AutocompleteMatch match_;
    203 
    204   DISALLOW_COPY_AND_ASSIGN(UrlSearchResult);
    205 };
    206 
    207 }  // namespace
    208 
    209 UrlSearchProvider::UrlSearchProvider(content::BrowserContext* browser_context)
    210     : browser_context_(browser_context),
    211       // TODO(mukai): introduce the real parameters when it's necessary.
    212       template_url_service_(
    213           new TemplateURLService(NULL /* prefs */,
    214                                  scoped_ptr<SearchTermsData>(
    215                                      new AthenaSearchTermsData()),
    216                                  NULL /* KeywordWebDataService */,
    217                                  scoped_ptr<TemplateURLServiceClient>(
    218                                      new AthenaTemplateURLServiceClient()),
    219                                  NULL /*GoogleURLTracker */,
    220                                  NULL /* RapporService */,
    221                                  base::Closure() /* dsp_change_callback */)),
    222       provider_(new ::SearchProvider(
    223           this,
    224           template_url_service_.get(),
    225           scoped_ptr<AutocompleteProviderClient>(
    226               new AthenaAutocompleteProviderClient(browser_context_)))) {
    227   template_url_service_->Load();
    228 }
    229 
    230 UrlSearchProvider::~UrlSearchProvider() {
    231 }
    232 
    233 void UrlSearchProvider::Start(const base::string16& query) {
    234   const bool minimal_changes = query == input_.text();
    235   scoped_ptr<AutocompleteSchemeClassifier> scheme_classifier(
    236       CreateSchemeClassifier(browser_context_));
    237   input_ = AutocompleteInput(query,
    238                              base::string16::npos /* cursor_position */,
    239                              base::string16() /* desired_tld */,
    240                              GURL() /* current_url */,
    241                              metrics::OmniboxEventProto::INVALID_SPEC,
    242                              false /* prevent_inline_autocomplete */,
    243                              false /* prefer_keyword */,
    244                              true /* allow_extract_keyword_match */,
    245                              true /* want_asynchronous_matches */,
    246                              *scheme_classifier);
    247 
    248   // Clearing results here may cause unexpected results.
    249   // TODO(mukai): fix this by fixing crbug.com/415500
    250   if (!minimal_changes)
    251     ClearResults();
    252 
    253   if (input_.type() == metrics::OmniboxInputType::URL) {
    254     // TODO(hashimoto): Componentize HistoryURLProvider and remove this code.
    255     AutocompleteMatch what_you_typed_match(
    256         NULL, 0, false, AutocompleteMatchType::URL_WHAT_YOU_TYPED);
    257     what_you_typed_match.destination_url = input_.canonicalized_url();
    258     what_you_typed_match.contents = input_.text();
    259     what_you_typed_match.relevance = kScoreForWhatYouTypedResult;
    260     Add(scoped_ptr<app_list::SearchResult>(new UrlSearchResult(
    261         browser_context_, what_you_typed_match)));
    262   }
    263 
    264   provider_->Start(input_, minimal_changes);
    265 }
    266 
    267 void UrlSearchProvider::Stop() {
    268   provider_->Stop(false);
    269 }
    270 
    271 void UrlSearchProvider::OnProviderUpdate(bool updated_matches) {
    272   if (!updated_matches)
    273     return;
    274 
    275   const ACMatches& matches = provider_->matches();
    276   for (ACMatches::const_iterator it = matches.begin(); it != matches.end();
    277        ++it) {
    278     if (!it->destination_url.is_valid())
    279       continue;
    280 
    281     Add(scoped_ptr<app_list::SearchResult>(new UrlSearchResult(
    282         browser_context_, *it)));
    283   }
    284 }
    285 
    286 }  // namespace athena
    287