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.
5 #include "chrome/browser/ui/app_list/search/omnibox_result.h"
7 #include "base/strings/utf_string_conversions.h"
8 #include "chrome/browser/autocomplete/autocomplete_controller.h"
9 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
10 #include "chrome/browser/profiles/profile.h"
11 #include "chrome/browser/ui/app_list/app_list_controller_delegate.h"
12 #include "chrome/browser/ui/app_list/search/search_util.h"
13 #include "components/bookmarks/browser/bookmark_model.h"
14 #include "components/omnibox/autocomplete_match_type.h"
15 #include "grit/theme_resources.h"
16 #include "ui/base/resource/resource_bundle.h"
18 #include "url/url_canon.h"
20 using bookmarks::BookmarkModel
;
26 // The Omnibox keyword for Google search. This is correct even if the user is
27 // using an international domain (such as google.com.au).
28 const char kGoogleSearchKeyword
[] = "google.com";
30 int ACMatchStyleToTagStyle(int styles
) {
32 if (styles
& ACMatchClassification::URL
)
33 tag_styles
|= SearchResult::Tag::URL
;
34 if (styles
& ACMatchClassification::MATCH
)
35 tag_styles
|= SearchResult::Tag::MATCH
;
36 if (styles
& ACMatchClassification::DIM
)
37 tag_styles
|= SearchResult::Tag::DIM
;
42 // Translates ACMatchClassifications into SearchResult tags.
43 void ACMatchClassificationsToTags(
44 const base::string16
& text
,
45 const ACMatchClassifications
& text_classes
,
46 SearchResult::Tags
* tags
) {
47 int tag_styles
= SearchResult::Tag::NONE
;
50 for (size_t i
= 0; i
< text_classes
.size(); ++i
) {
51 const ACMatchClassification
& text_class
= text_classes
[i
];
53 // Closes current tag.
54 if (tag_styles
!= SearchResult::Tag::NONE
) {
55 tags
->push_back(SearchResult::Tag(
56 tag_styles
, tag_start
, text_class
.offset
));
57 tag_styles
= SearchResult::Tag::NONE
;
60 if (text_class
.style
== ACMatchClassification::NONE
)
63 tag_start
= text_class
.offset
;
64 tag_styles
= ACMatchStyleToTagStyle(text_class
.style
);
67 if (tag_styles
!= SearchResult::Tag::NONE
) {
68 tags
->push_back(SearchResult::Tag(
69 tag_styles
, tag_start
, text
.length()));
73 // Converts a Google Search URL into a spoken feedback URL, by adding query
74 // parameters. |search_url| must be a Google Search URL.
75 GURL
MakeGoogleSearchSpokenFeedbackUrl(const GURL
& search_url
) {
76 std::string query
= search_url
.query();
78 GURL::Replacements replacements
;
79 replacements
.SetQueryStr(query
);
80 return search_url
.ReplaceComponents(replacements
);
85 OmniboxResult::OmniboxResult(Profile
* profile
,
86 AppListControllerDelegate
* list_controller
,
87 AutocompleteController
* autocomplete_controller
,
89 const AutocompleteMatch
& match
)
91 list_controller_(list_controller
),
92 autocomplete_controller_(autocomplete_controller
),
93 is_voice_query_(is_voice_query
),
95 if (match_
.search_terms_args
&& autocomplete_controller_
) {
96 match_
.search_terms_args
->from_app_list
= true;
97 autocomplete_controller_
->UpdateMatchDestinationURL(
98 *match_
.search_terms_args
, &match_
);
100 set_id(match_
.destination_url
.spec());
102 // Derive relevance from omnibox relevance and normalize it to [0, 1].
103 // The magic number 1500 is the highest score of an omnibox result.
104 // See comments in autocomplete_provider.h.
105 set_relevance(match_
.relevance
/ 1500.0);
108 UpdateTitleAndDetails();
110 // The raw "what you typed" search results should be promoted and
111 // automatically selected by voice queries. If a "history" result exactly
112 // matches what you typed, then the omnibox will not produce a "what you
113 // typed" result; therefore, we must also flag "history" results as voice
114 // results if they exactly match the query.
115 if (match_
.type
== AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED
||
116 (match_
.type
== AutocompleteMatchType::SEARCH_HISTORY
&&
117 match_
.search_terms_args
&&
118 match_
.contents
== match_
.search_terms_args
->original_query
)) {
119 set_voice_result(true);
123 OmniboxResult::~OmniboxResult() {
126 void OmniboxResult::Open(int event_flags
) {
127 RecordHistogram(OMNIBOX_SEARCH_RESULT
);
128 GURL url
= match_
.destination_url
;
129 if (is_voice_query_
&&
130 base::UTF16ToUTF8(match_
.keyword
) == kGoogleSearchKeyword
) {
131 url
= MakeGoogleSearchSpokenFeedbackUrl(url
);
133 list_controller_
->OpenURL(profile_
, url
, match_
.transition
,
134 ui::DispositionFromEventFlags(event_flags
));
137 scoped_ptr
<SearchResult
> OmniboxResult::Duplicate() const {
138 return scoped_ptr
<SearchResult
>(new OmniboxResult(profile_
, list_controller_
,
139 autocomplete_controller_
,
140 is_voice_query_
, match_
));
143 void OmniboxResult::UpdateIcon() {
144 BookmarkModel
* bookmark_model
= BookmarkModelFactory::GetForProfile(profile_
);
146 bookmark_model
&& bookmark_model
->IsBookmarked(match_
.destination_url
);
147 int resource_id
= is_bookmarked
? IDR_OMNIBOX_STAR
148 : AutocompleteMatch::TypeToIcon(match_
.type
);
150 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
));
153 void OmniboxResult::UpdateTitleAndDetails() {
154 set_title(match_
.contents
);
155 SearchResult::Tags title_tags
;
156 ACMatchClassificationsToTags(match_
.contents
, match_
.contents_class
,
158 set_title_tags(title_tags
);
160 set_details(match_
.description
);
161 SearchResult::Tags details_tags
;
162 ACMatchClassificationsToTags(match_
.description
, match_
.description_class
,
164 set_details_tags(details_tags
);
167 } // namespace app_list