- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / omnibox / omnibox_controller.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/omnibox/omnibox_controller.h"
6
7 #include "base/metrics/histogram.h"
8 #include "chrome/browser/autocomplete/autocomplete_classifier.h"
9 #include "chrome/browser/autocomplete/autocomplete_match.h"
10 #include "chrome/browser/autocomplete/search_provider.h"
11 #include "chrome/browser/net/predictor.h"
12 #include "chrome/browser/predictors/autocomplete_action_predictor.h"
13 #include "chrome/browser/prerender/prerender_field_trial.h"
14 #include "chrome/browser/prerender/prerender_manager.h"
15 #include "chrome/browser/prerender/prerender_manager_factory.h"
16 #include "chrome/browser/profiles/profile.h"
17 #include "chrome/browser/search/search.h"
18 #include "chrome/browser/ui/omnibox/omnibox_edit_controller.h"
19 #include "chrome/browser/ui/omnibox/omnibox_edit_model.h"
20 #include "chrome/browser/ui/omnibox/omnibox_popup_model.h"
21 #include "chrome/browser/ui/omnibox/omnibox_popup_view.h"
22 #include "chrome/browser/ui/search/instant_controller.h"
23 #include "chrome/common/instant_types.h"
24 #include "extensions/common/constants.h"
25 #include "ui/gfx/rect.h"
26
27 namespace {
28
29 // Returns the AutocompleteMatch that the InstantController should prefetch, if
30 // any.
31 //
32 // The SearchProvider may mark some suggestions to be prefetched based on
33 // instructions from the suggest server. If such a match ranks sufficiently
34 // highly, we'll return it. We only care about matches that are the default or
35 // else the very first entry in the dropdown (which can happen for non-default
36 // matches only if we're hiding a top verbatim match); for other matches, we
37 // think the likelihood of the user selecting them is low enough that
38 // prefetching isn't worth doing.
39 const AutocompleteMatch* GetMatchToPrefetch(const AutocompleteResult& result) {
40   const AutocompleteResult::const_iterator default_match(
41       result.default_match());
42   if (default_match == result.end())
43     return NULL;
44
45   if (SearchProvider::ShouldPrefetch(*default_match))
46     return &(*default_match);
47
48   return (result.ShouldHideTopMatch() && (result.size() > 1) &&
49       SearchProvider::ShouldPrefetch(result.match_at(1))) ?
50           &result.match_at(1) : NULL;
51 }
52
53 }  // namespace
54
55 OmniboxController::OmniboxController(OmniboxEditModel* omnibox_edit_model,
56                                      Profile* profile)
57     : omnibox_edit_model_(omnibox_edit_model),
58       profile_(profile),
59       autocomplete_controller_(new AutocompleteController(profile, this,
60           AutocompleteClassifier::kDefaultOmniboxProviders)) {
61 }
62
63 OmniboxController::~OmniboxController() {
64 }
65
66 void OmniboxController::StartAutocomplete(
67     string16 user_text,
68     size_t cursor_position,
69     const GURL& current_url,
70     AutocompleteInput::PageClassification current_page_classification,
71     bool prevent_inline_autocomplete,
72     bool prefer_keyword,
73     bool allow_exact_keyword_match) const {
74   ClearPopupKeywordMode();
75   popup_->SetHoveredLine(OmniboxPopupModel::kNoMatch);
76
77   // We don't explicitly clear OmniboxPopupModel::manually_selected_match, as
78   // Start ends up invoking OmniboxPopupModel::OnResultChanged which clears it.
79   autocomplete_controller_->Start(AutocompleteInput(
80       user_text, cursor_position, string16(), current_url,
81       current_page_classification, prevent_inline_autocomplete,
82       prefer_keyword, allow_exact_keyword_match,
83       AutocompleteInput::ALL_MATCHES));
84 }
85
86 void OmniboxController::OnResultChanged(bool default_match_changed) {
87   const bool was_open = popup_->IsOpen();
88   if (default_match_changed) {
89     // The default match has changed, we need to let the OmniboxEditModel know
90     // about new inline autocomplete text (blue highlight).
91     const AutocompleteResult& result = this->result();
92     const AutocompleteResult::const_iterator match(result.default_match());
93     if (match != result.end()) {
94       current_match_ = *match;
95       if (!prerender::IsOmniboxEnabled(profile_))
96         DoPreconnect(*match);
97       omnibox_edit_model_->OnCurrentMatchChanged();
98
99       if (chrome::IsInstantExtendedAPIEnabled() &&
100           omnibox_edit_model_->GetInstantController()) {
101         InstantSuggestion prefetch_suggestion;
102         const AutocompleteMatch* match_to_prefetch = GetMatchToPrefetch(result);
103         if (match_to_prefetch) {
104           prefetch_suggestion.text = match_to_prefetch->contents;
105           prefetch_suggestion.metadata =
106               SearchProvider::GetSuggestMetadata(*match_to_prefetch);
107         }
108         // Send the prefetch suggestion unconditionally to the InstantPage. If
109         // there is no suggestion to prefetch, we need to send a blank query to
110         // clear the prefetched results.
111         omnibox_edit_model_->GetInstantController()->SetSuggestionToPrefetch(
112             prefetch_suggestion);
113       }
114     } else {
115       InvalidateCurrentMatch();
116       popup_->OnResultChanged();
117       omnibox_edit_model_->OnPopupDataChanged(string16(), NULL, string16(),
118                                               false);
119     }
120   } else {
121     popup_->OnResultChanged();
122   }
123
124   if (!popup_->IsOpen() && was_open) {
125     // Accept the temporary text as the user text, because it makes little sense
126     // to have temporary text when the popup is closed.
127     omnibox_edit_model_->AcceptTemporaryTextAsUserText();
128   }
129 }
130
131 void OmniboxController::InvalidateCurrentMatch() {
132   current_match_ = AutocompleteMatch();
133 }
134
135 void OmniboxController::ClearPopupKeywordMode() const {
136   if (popup_->IsOpen() &&
137       popup_->selected_line_state() == OmniboxPopupModel::KEYWORD)
138     popup_->SetSelectedLineState(OmniboxPopupModel::NORMAL);
139 }
140
141 void OmniboxController::DoPreconnect(const AutocompleteMatch& match) {
142   if (!match.destination_url.SchemeIs(extensions::kExtensionScheme)) {
143     // Warm up DNS Prefetch cache, or preconnect to a search service.
144     UMA_HISTOGRAM_ENUMERATION("Autocomplete.MatchType", match.type,
145                               AutocompleteMatchType::NUM_TYPES);
146     if (profile_->GetNetworkPredictor()) {
147       profile_->GetNetworkPredictor()->AnticipateOmniboxUrl(
148           match.destination_url,
149           predictors::AutocompleteActionPredictor::IsPreconnectable(match));
150     }
151     // We could prefetch the alternate nav URL, if any, but because there
152     // can be many of these as a user types an initial series of characters,
153     // the OS DNS cache could suffer eviction problems for minimal gain.
154   }
155 }