Home | History | Annotate | Download | only in omnibox
      1 // Copyright (c) 2012 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/webui/omnibox/omnibox_ui_handler.h"
      6 
      7 #include <string>
      8 
      9 #include "base/auto_reset.h"
     10 #include "base/bind.h"
     11 #include "base/strings/string16.h"
     12 #include "base/strings/stringprintf.h"
     13 #include "base/strings/utf_string_conversions.h"
     14 #include "base/time/time.h"
     15 #include "base/values.h"
     16 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
     17 #include "chrome/browser/autocomplete/autocomplete_controller.h"
     18 #include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
     19 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
     20 #include "chrome/browser/history/history_service.h"
     21 #include "chrome/browser/history/history_service_factory.h"
     22 #include "chrome/browser/search/search.h"
     23 #include "chrome/browser/search_engines/template_url_service_factory.h"
     24 #include "components/bookmarks/browser/bookmark_model.h"
     25 #include "components/history/core/browser/url_database.h"
     26 #include "components/metrics/proto/omnibox_event.pb.h"
     27 #include "components/omnibox/autocomplete_match.h"
     28 #include "components/omnibox/autocomplete_provider.h"
     29 #include "components/search_engines/template_url.h"
     30 #include "content/public/browser/web_ui.h"
     31 #include "mojo/common/common_type_converters.h"
     32 
     33 namespace mojo {
     34 
     35 template <>
     36 struct TypeConverter<mojo::Array<AutocompleteAdditionalInfoPtr>,
     37                      AutocompleteMatch::AdditionalInfo> {
     38   static mojo::Array<AutocompleteAdditionalInfoPtr> Convert(
     39       const AutocompleteMatch::AdditionalInfo& input) {
     40     mojo::Array<AutocompleteAdditionalInfoPtr> array(input.size());
     41     size_t index = 0;
     42     for (AutocompleteMatch::AdditionalInfo::const_iterator i = input.begin();
     43          i != input.end(); ++i, index++) {
     44       AutocompleteAdditionalInfoPtr item(AutocompleteAdditionalInfo::New());
     45       item->key = i->first;
     46       item->value = i->second;
     47       array[index] = item.Pass();
     48     }
     49     return array.Pass();
     50   }
     51 };
     52 
     53 template <>
     54 struct TypeConverter<AutocompleteMatchMojoPtr, AutocompleteMatch> {
     55   static AutocompleteMatchMojoPtr Convert(const AutocompleteMatch& input) {
     56     AutocompleteMatchMojoPtr result(AutocompleteMatchMojo::New());
     57     if (input.provider != NULL) {
     58       result->provider_name = input.provider->GetName();
     59       result->provider_done = input.provider->done();
     60     }
     61     result->relevance = input.relevance;
     62     result->deletable = input.deletable;
     63     result->fill_into_edit = mojo::String::From(input.fill_into_edit);
     64     result->inline_autocompletion =
     65         mojo::String::From(input.inline_autocompletion);
     66     result->destination_url = input.destination_url.spec();
     67     result->contents = mojo::String::From(input.contents);
     68     // At this time, we're not bothering to send along the long vector that
     69     // represent contents classification.  i.e., for each character, what
     70     // type of text it is.
     71     result->description = mojo::String::From(input.description);
     72     // At this time, we're not bothering to send along the long vector that
     73     // represents description classification.  i.e., for each character, what
     74     // type of text it is.
     75     result->transition = input.transition;
     76     result->is_history_what_you_typed_match =
     77         input.is_history_what_you_typed_match;
     78     result->allowed_to_be_default_match = input.allowed_to_be_default_match;
     79     result->type = AutocompleteMatchType::ToString(input.type);
     80     if (input.associated_keyword.get() != NULL) {
     81       result->associated_keyword =
     82           mojo::String::From(input.associated_keyword->keyword);
     83     }
     84     result->keyword = mojo::String::From(input.keyword);
     85     result->duplicates = static_cast<int32>(input.duplicate_matches.size());
     86     result->from_previous = input.from_previous;
     87 
     88     result->additional_info =
     89         mojo::Array<AutocompleteAdditionalInfoPtr>::From(input.additional_info);
     90     return result.Pass();
     91   }
     92 };
     93 
     94 template <>
     95 struct TypeConverter<AutocompleteResultsForProviderMojoPtr,
     96                      scoped_refptr<AutocompleteProvider> > {
     97   static AutocompleteResultsForProviderMojoPtr Convert(
     98       const scoped_refptr<AutocompleteProvider>& input) {
     99     AutocompleteResultsForProviderMojoPtr result(
    100         AutocompleteResultsForProviderMojo::New());
    101     result->provider_name = input->GetName();
    102     result->results =
    103         mojo::Array<AutocompleteMatchMojoPtr>::From(input->matches());
    104     return result.Pass();
    105   }
    106 };
    107 
    108 }  // namespace mojo
    109 
    110 OmniboxUIHandler::OmniboxUIHandler(Profile* profile)
    111     : profile_(profile) {
    112   ResetController();
    113 }
    114 
    115 OmniboxUIHandler::~OmniboxUIHandler() {}
    116 
    117 void OmniboxUIHandler::OnResultChanged(bool default_match_changed) {
    118   OmniboxResultMojoPtr result(OmniboxResultMojo::New());
    119   result->done = controller_->done();
    120   result->time_since_omnibox_started_ms =
    121       (base::Time::Now() - time_omnibox_started_).InMilliseconds();
    122   const base::string16 host = input_.text().substr(
    123       input_.parts().host.begin,
    124       input_.parts().host.len);
    125   result->host = mojo::String::From(host);
    126   bool is_typed_host;
    127   if (!LookupIsTypedHost(host, &is_typed_host))
    128     is_typed_host = false;
    129   result->is_typed_host = is_typed_host;
    130 
    131   {
    132     // Copy to an ACMatches to make conversion easier. Since this isn't
    133     // performance critical we don't worry about the cost here.
    134     ACMatches matches(controller_->result().begin(),
    135                       controller_->result().end());
    136     result->combined_results =
    137         mojo::Array<AutocompleteMatchMojoPtr>::From(matches);
    138   }
    139   result->results_by_provider =
    140       mojo::Array<AutocompleteResultsForProviderMojoPtr>::From(
    141           controller_->providers());
    142 
    143   // Fill AutocompleteMatchMojo::starred.
    144   BookmarkModel* bookmark_model = BookmarkModelFactory::GetForProfile(profile_);
    145   if (bookmark_model) {
    146     for (size_t i =  0; i < result->combined_results.size(); ++i) {
    147       result->combined_results[i]->starred = bookmark_model->IsBookmarked(
    148           GURL(result->combined_results[i]->destination_url));
    149     }
    150     for (size_t i = 0; i < result->results_by_provider.size(); ++i) {
    151       const AutocompleteResultsForProviderMojo& result_by_provider =
    152           *result->results_by_provider[i];
    153       for (size_t j = 0; j < result_by_provider.results.size(); ++j) {
    154         result_by_provider.results[j]->starred = bookmark_model->IsBookmarked(
    155             GURL(result_by_provider.results[j]->destination_url));
    156       }
    157     }
    158   }
    159 
    160   client()->HandleNewAutocompleteResult(result.Pass());
    161 }
    162 
    163 bool OmniboxUIHandler::LookupIsTypedHost(const base::string16& host,
    164                                          bool* is_typed_host) const {
    165   HistoryService* const history_service =
    166       HistoryServiceFactory::GetForProfile(profile_,
    167                                            Profile::EXPLICIT_ACCESS);
    168   if (!history_service)
    169     return false;
    170   history::URLDatabase* url_db = history_service->InMemoryDatabase();
    171   if (!url_db)
    172     return false;
    173   *is_typed_host = url_db->IsTypedHost(base::UTF16ToUTF8(host));
    174   return true;
    175 }
    176 
    177 void OmniboxUIHandler::StartOmniboxQuery(const mojo::String& input_string,
    178                                          int32_t cursor_position,
    179                                          bool prevent_inline_autocomplete,
    180                                          bool prefer_keyword,
    181                                          int32_t page_classification) {
    182   // Reset the controller.  If we don't do this, then the
    183   // AutocompleteController might inappropriately set its |minimal_changes|
    184   // variable (or something else) and some providers will short-circuit
    185   // important logic and return stale results.  In short, we want the
    186   // actual results to not depend on the state of the previous request.
    187   ResetController();
    188   time_omnibox_started_ = base::Time::Now();
    189   input_ = AutocompleteInput(
    190       input_string.To<base::string16>(), cursor_position, base::string16(),
    191       GURL(),
    192       static_cast<metrics::OmniboxEventProto::PageClassification>(
    193           page_classification),
    194       prevent_inline_autocomplete, prefer_keyword, true, true,
    195       ChromeAutocompleteSchemeClassifier(profile_));
    196   controller_->Start(input_);
    197 }
    198 
    199 void OmniboxUIHandler::ResetController() {
    200   controller_.reset(new AutocompleteController(profile_,
    201           TemplateURLServiceFactory::GetForProfile(profile_),
    202           this,
    203           AutocompleteClassifier::kDefaultOmniboxProviders));
    204 }
    205