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/string_split.h"
8 #include "base/strings/utf_string_conversions.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/browser/autocomplete_controller.h"
15 #include "components/omnibox/browser/autocomplete_match_type.h"
16 #include "grit/theme_resources.h"
17 #include "ui/base/resource/resource_bundle.h"
19 #include "url/url_canon.h"
21 using bookmarks::BookmarkModel
;
27 int ACMatchStyleToTagStyle(int styles
) {
29 if (styles
& ACMatchClassification::URL
)
30 tag_styles
|= SearchResult::Tag::URL
;
31 if (styles
& ACMatchClassification::MATCH
)
32 tag_styles
|= SearchResult::Tag::MATCH
;
33 if (styles
& ACMatchClassification::DIM
)
34 tag_styles
|= SearchResult::Tag::DIM
;
39 // Translates ACMatchClassifications into SearchResult tags.
40 void ACMatchClassificationsToTags(
41 const base::string16
& text
,
42 const ACMatchClassifications
& text_classes
,
43 SearchResult::Tags
* tags
) {
44 int tag_styles
= SearchResult::Tag::NONE
;
47 for (size_t i
= 0; i
< text_classes
.size(); ++i
) {
48 const ACMatchClassification
& text_class
= text_classes
[i
];
50 // Closes current tag.
51 if (tag_styles
!= SearchResult::Tag::NONE
) {
52 tags
->push_back(SearchResult::Tag(
53 tag_styles
, tag_start
, text_class
.offset
));
54 tag_styles
= SearchResult::Tag::NONE
;
57 if (text_class
.style
== ACMatchClassification::NONE
)
60 tag_start
= text_class
.offset
;
61 tag_styles
= ACMatchStyleToTagStyle(text_class
.style
);
64 if (tag_styles
!= SearchResult::Tag::NONE
) {
65 tags
->push_back(SearchResult::Tag(
66 tag_styles
, tag_start
, text
.length()));
70 // Returns true if |url| is on a Google Search domain. May return false
72 bool IsUrlGoogleSearch(const GURL
& url
) {
73 // Just return true if the second or third level domain is "google". This may
74 // result in false positives (e.g. "google.example.com"), but since we are
75 // only using this to decide when to add the spoken feedback query parameter,
76 // this doesn't have any bad consequences.
77 const char kGoogleDomainLabel
[] = "google";
79 std::vector
<std::string
> pieces
= base::SplitString(
80 url
.host(), ".", base::KEEP_WHITESPACE
, base::SPLIT_WANT_ALL
);
82 size_t num_pieces
= pieces
.size();
84 if (num_pieces
>= 2 && pieces
[num_pieces
- 2] == kGoogleDomainLabel
)
87 if (num_pieces
>= 3 && pieces
[num_pieces
- 3] == kGoogleDomainLabel
)
93 // Converts a Google Search URL into a spoken feedback URL, by adding query
94 // parameters. |search_url| must be a Google Search URL.
95 GURL
MakeGoogleSearchSpokenFeedbackUrl(const GURL
& search_url
) {
96 std::string query
= search_url
.query();
98 GURL::Replacements replacements
;
99 replacements
.SetQueryStr(query
);
100 return search_url
.ReplaceComponents(replacements
);
105 OmniboxResult::OmniboxResult(Profile
* profile
,
106 AppListControllerDelegate
* list_controller
,
107 AutocompleteController
* autocomplete_controller
,
109 const AutocompleteMatch
& match
)
111 list_controller_(list_controller
),
112 autocomplete_controller_(autocomplete_controller
),
113 is_voice_query_(is_voice_query
),
115 if (match_
.search_terms_args
&& autocomplete_controller_
) {
116 match_
.search_terms_args
->from_app_list
= true;
117 autocomplete_controller_
->UpdateMatchDestinationURL(
118 *match_
.search_terms_args
, &match_
);
120 set_id(match_
.destination_url
.spec());
122 // Derive relevance from omnibox relevance and normalize it to [0, 1].
123 // The magic number 1500 is the highest score of an omnibox result.
124 // See comments in autocomplete_provider.h.
125 set_relevance(match_
.relevance
/ 1500.0);
128 UpdateTitleAndDetails();
130 // The raw "what you typed" search results should be promoted and
131 // automatically selected by voice queries. If a "history" result exactly
132 // matches what you typed, then the omnibox will not produce a "what you
133 // typed" result; therefore, we must also flag "history" results as voice
134 // results if they exactly match the query.
135 if (match_
.type
== AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED
||
136 (match_
.type
== AutocompleteMatchType::SEARCH_HISTORY
&&
137 match_
.search_terms_args
&&
138 match_
.contents
== match_
.search_terms_args
->original_query
)) {
139 set_voice_result(true);
143 OmniboxResult::~OmniboxResult() {
146 void OmniboxResult::Open(int event_flags
) {
147 RecordHistogram(OMNIBOX_SEARCH_RESULT
);
148 GURL url
= match_
.destination_url
;
149 if (is_voice_query_
&& IsUrlGoogleSearch(url
)) {
150 url
= MakeGoogleSearchSpokenFeedbackUrl(url
);
152 list_controller_
->OpenURL(profile_
, url
, match_
.transition
,
153 ui::DispositionFromEventFlags(event_flags
));
156 scoped_ptr
<SearchResult
> OmniboxResult::Duplicate() const {
157 return scoped_ptr
<SearchResult
>(new OmniboxResult(profile_
, list_controller_
,
158 autocomplete_controller_
,
159 is_voice_query_
, match_
));
162 void OmniboxResult::UpdateIcon() {
163 BookmarkModel
* bookmark_model
= BookmarkModelFactory::GetForProfile(profile_
);
165 bookmark_model
&& bookmark_model
->IsBookmarked(match_
.destination_url
);
166 int resource_id
= is_bookmarked
? IDR_OMNIBOX_STAR
167 : AutocompleteMatch::TypeToIcon(match_
.type
);
169 *ui::ResourceBundle::GetSharedInstance().GetImageSkiaNamed(resource_id
));
172 void OmniboxResult::UpdateTitleAndDetails() {
173 set_title(match_
.contents
);
174 SearchResult::Tags title_tags
;
175 ACMatchClassificationsToTags(match_
.contents
, match_
.contents_class
,
177 set_title_tags(title_tags
);
179 set_details(match_
.description
);
180 SearchResult::Tags details_tags
;
181 ACMatchClassificationsToTags(match_
.description
, match_
.description_class
,
183 set_details_tags(details_tags
);
186 } // namespace app_list