OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/autocomplete/keyword_provider.h" | 5 #include "chrome/browser/autocomplete/keyword_provider.h" |
6 | 6 |
7 #include <algorithm> | 7 #include <algorithm> |
8 #include <vector> | 8 #include <vector> |
9 | 9 |
10 #include "base/string16.h" | 10 #include "base/string16.h" |
(...skipping 23 matching lines...) Expand all Loading... |
34 } | 34 } |
35 | 35 |
36 void StayInKeywordMode() { | 36 void StayInKeywordMode() { |
37 provider_ = NULL; | 37 provider_ = NULL; |
38 } | 38 } |
39 private: | 39 private: |
40 KeywordProvider* provider_; | 40 KeywordProvider* provider_; |
41 }; | 41 }; |
42 | 42 |
43 // static | 43 // static |
44 string16 KeywordProvider::SplitReplacementStringFromInput( | 44 std::wstring KeywordProvider::SplitReplacementStringFromInput( |
45 const string16& input, | 45 const std::wstring& input, |
46 bool trim_leading_whitespace) { | 46 bool trim_leading_whitespace) { |
47 // The input may contain leading whitespace, strip it. | 47 // The input may contain leading whitespace, strip it. |
48 string16 trimmed_input; | 48 std::wstring trimmed_input; |
49 TrimWhitespace(input, TRIM_LEADING, &trimmed_input); | 49 TrimWhitespace(input, TRIM_LEADING, &trimmed_input); |
50 | 50 |
51 // And extract the replacement string. | 51 // And extract the replacement string. |
52 string16 remaining_input; | 52 std::wstring remaining_input; |
53 SplitKeywordFromInput(trimmed_input, trim_leading_whitespace, | 53 SplitKeywordFromInput(trimmed_input, trim_leading_whitespace, |
54 &remaining_input); | 54 &remaining_input); |
55 return remaining_input; | 55 return remaining_input; |
56 } | 56 } |
57 | 57 |
58 KeywordProvider::KeywordProvider(ACProviderListener* listener, Profile* profile) | 58 KeywordProvider::KeywordProvider(ACProviderListener* listener, Profile* profile) |
59 : AutocompleteProvider(listener, profile, "Keyword"), | 59 : AutocompleteProvider(listener, profile, "Keyword"), |
60 model_(NULL), | 60 model_(NULL), |
61 current_input_id_(0) { | 61 current_input_id_(0) { |
62 // Extension suggestions always come from the original profile, since that's | 62 // Extension suggestions always come from the original profile, since that's |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
100 // We need our input IDs to be unique across all profiles, so we keep a global | 100 // We need our input IDs to be unique across all profiles, so we keep a global |
101 // UID that each provider uses. | 101 // UID that each provider uses. |
102 static int global_input_uid_; | 102 static int global_input_uid_; |
103 | 103 |
104 } // namespace | 104 } // namespace |
105 | 105 |
106 // static | 106 // static |
107 const TemplateURL* KeywordProvider::GetSubstitutingTemplateURLForInput( | 107 const TemplateURL* KeywordProvider::GetSubstitutingTemplateURLForInput( |
108 Profile* profile, | 108 Profile* profile, |
109 const AutocompleteInput& input, | 109 const AutocompleteInput& input, |
110 string16* remaining_input) { | 110 std::wstring* remaining_input) { |
111 if (!input.allow_exact_keyword_match()) | 111 if (!input.allow_exact_keyword_match()) |
112 return NULL; | 112 return NULL; |
113 | 113 |
114 string16 keyword; | 114 std::wstring keyword; |
115 if (!ExtractKeywordFromInput(input, &keyword, remaining_input)) | 115 if (!ExtractKeywordFromInput(input, &keyword, remaining_input)) |
116 return NULL; | 116 return NULL; |
117 | 117 |
118 // Make sure the model is loaded. This is cheap and quickly bails out if | 118 // Make sure the model is loaded. This is cheap and quickly bails out if |
119 // the model is already loaded. | 119 // the model is already loaded. |
120 TemplateURLModel* model = profile->GetTemplateURLModel(); | 120 TemplateURLModel* model = profile->GetTemplateURLModel(); |
121 DCHECK(model); | 121 DCHECK(model); |
122 model->Load(); | 122 model->Load(); |
123 | 123 |
124 const TemplateURL* template_url = model->GetTemplateURLForKeyword(keyword); | 124 const TemplateURL* template_url = |
| 125 model->GetTemplateURLForKeyword(WideToUTF16Hack(keyword)); |
125 return TemplateURL::SupportsReplacement(template_url) ? template_url : NULL; | 126 return TemplateURL::SupportsReplacement(template_url) ? template_url : NULL; |
126 } | 127 } |
127 | 128 |
128 void KeywordProvider::Start(const AutocompleteInput& input, | 129 void KeywordProvider::Start(const AutocompleteInput& input, |
129 bool minimal_changes) { | 130 bool minimal_changes) { |
130 // This object ensures we end keyword mode if we exit the function without | 131 // This object ensures we end keyword mode if we exit the function without |
131 // toggling keyword mode to on. | 132 // toggling keyword mode to on. |
132 ScopedEndExtensionKeywordMode keyword_mode_toggle(this); | 133 ScopedEndExtensionKeywordMode keyword_mode_toggle(this); |
133 | 134 |
134 matches_.clear(); | 135 matches_.clear(); |
(...skipping 12 matching lines...) Expand all Loading... |
147 // the assumption that they might not realize they no longer need to go to a | 148 // the assumption that they might not realize they no longer need to go to a |
148 // site to be able to search it. So we call CleanUserInputKeyword() to strip | 149 // site to be able to search it. So we call CleanUserInputKeyword() to strip |
149 // any initial scheme and/or "www.". NOTE: Any heuristics or UI used to | 150 // any initial scheme and/or "www.". NOTE: Any heuristics or UI used to |
150 // automatically/manually create keywords will need to be in sync with | 151 // automatically/manually create keywords will need to be in sync with |
151 // whatever we do here! | 152 // whatever we do here! |
152 // | 153 // |
153 // TODO(pkasting): http://b/1112681 If someday we remember usage frequency for | 154 // TODO(pkasting): http://b/1112681 If someday we remember usage frequency for |
154 // keywords, we might suggest keywords that haven't even been partially typed, | 155 // keywords, we might suggest keywords that haven't even been partially typed, |
155 // if the user uses them enough and isn't obviously typing something else. In | 156 // if the user uses them enough and isn't obviously typing something else. In |
156 // this case we'd consider all input here to be query input. | 157 // this case we'd consider all input here to be query input. |
157 string16 keyword, remaining_input; | 158 std::wstring keyword, remaining_input; |
158 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) | 159 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) |
159 return; | 160 return; |
160 | 161 |
161 // Make sure the model is loaded. This is cheap and quickly bails out if | 162 // Make sure the model is loaded. This is cheap and quickly bails out if |
162 // the model is already loaded. | 163 // the model is already loaded. |
163 TemplateURLModel* model = profile_ ? profile_->GetTemplateURLModel() : model_; | 164 TemplateURLModel* model = profile_ ? profile_->GetTemplateURLModel() : model_; |
164 DCHECK(model); | 165 DCHECK(model); |
165 model->Load(); | 166 model->Load(); |
166 | 167 |
167 // Get the best matches for this keyword. | 168 // Get the best matches for this keyword. |
168 // | 169 // |
169 // NOTE: We could cache the previous keywords and reuse them here in the | 170 // NOTE: We could cache the previous keywords and reuse them here in the |
170 // |minimal_changes| case, but since we'd still have to recalculate their | 171 // |minimal_changes| case, but since we'd still have to recalculate their |
171 // relevances and we can just recreate the results synchronously anyway, we | 172 // relevances and we can just recreate the results synchronously anyway, we |
172 // don't bother. | 173 // don't bother. |
173 // | 174 // |
174 // TODO(pkasting): http://b/893701 We should remember the user's use of a | 175 // TODO(pkasting): http://b/893701 We should remember the user's use of a |
175 // search query both from the autocomplete popup and from web pages | 176 // search query both from the autocomplete popup and from web pages |
176 // themselves. | 177 // themselves. |
177 std::vector<string16> keyword_matches; | 178 std::vector<string16> keyword_matches; |
178 model->FindMatchingKeywords(keyword, | 179 model->FindMatchingKeywords(WideToUTF16Hack(keyword), |
179 !remaining_input.empty(), | 180 !remaining_input.empty(), |
180 &keyword_matches); | 181 &keyword_matches); |
181 | 182 |
182 // Prune any extension keywords that are disallowed in incognito mode (if | 183 // Prune any extension keywords that are disallowed in incognito mode (if |
183 // we're incognito), or disabled. | 184 // we're incognito), or disabled. |
184 for (std::vector<string16>::iterator i(keyword_matches.begin()); | 185 for (std::vector<string16>::iterator i(keyword_matches.begin()); |
185 i != keyword_matches.end(); ) { | 186 i != keyword_matches.end(); ) { |
186 const TemplateURL* template_url(model->GetTemplateURLForKeyword(*i)); | 187 const TemplateURL* template_url(model->GetTemplateURLForKeyword(*i)); |
187 if (profile_ && | 188 if (profile_ && |
188 !input.synchronous_only() && template_url->IsExtensionKeyword()) { | 189 !input.synchronous_only() && template_url->IsExtensionKeyword()) { |
(...skipping 10 matching lines...) Expand all Loading... |
199 ++i; | 200 ++i; |
200 } | 201 } |
201 if (keyword_matches.empty()) | 202 if (keyword_matches.empty()) |
202 return; | 203 return; |
203 std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality()); | 204 std::sort(keyword_matches.begin(), keyword_matches.end(), CompareQuality()); |
204 | 205 |
205 // Limit to one exact or three inexact matches, and mark them up for display | 206 // Limit to one exact or three inexact matches, and mark them up for display |
206 // in the autocomplete popup. | 207 // in the autocomplete popup. |
207 // Any exact match is going to be the highest quality match, and thus at the | 208 // Any exact match is going to be the highest quality match, and thus at the |
208 // front of our vector. | 209 // front of our vector. |
209 if (keyword_matches.front() == keyword) { | 210 if (keyword_matches.front() == WideToUTF16Hack(keyword)) { |
210 const TemplateURL* template_url(model->GetTemplateURLForKeyword(keyword)); | 211 const TemplateURL* template_url( |
| 212 model->GetTemplateURLForKeyword(WideToUTF16Hack(keyword))); |
211 // TODO(pkasting): We should probably check that if the user explicitly | 213 // TODO(pkasting): We should probably check that if the user explicitly |
212 // typed a scheme, that scheme matches the one in |template_url|. | 214 // typed a scheme, that scheme matches the one in |template_url|. |
213 matches_.push_back(CreateAutocompleteMatch(model, keyword, input, | 215 matches_.push_back(CreateAutocompleteMatch(model, keyword, input, |
214 keyword.length(), | 216 keyword.length(), |
215 remaining_input, -1)); | 217 remaining_input, -1)); |
216 | 218 |
217 if (profile_ && | 219 if (profile_ && |
218 !input.synchronous_only() && template_url->IsExtensionKeyword()) { | 220 !input.synchronous_only() && template_url->IsExtensionKeyword()) { |
219 if (template_url->GetExtensionId() != current_keyword_extension_id_) | 221 if (template_url->GetExtensionId() != current_keyword_extension_id_) |
220 MaybeEndExtensionKeywordMode(); | 222 MaybeEndExtensionKeywordMode(); |
221 if (current_keyword_extension_id_.empty()) | 223 if (current_keyword_extension_id_.empty()) |
222 EnterExtensionKeywordMode(template_url->GetExtensionId()); | 224 EnterExtensionKeywordMode(template_url->GetExtensionId()); |
223 keyword_mode_toggle.StayInKeywordMode(); | 225 keyword_mode_toggle.StayInKeywordMode(); |
224 | 226 |
225 ApplyDefaultSuggestionForExtensionKeyword(profile_, template_url, | 227 ApplyDefaultSuggestionForExtensionKeyword(profile_, template_url, |
226 remaining_input, | 228 WideToUTF16(remaining_input), |
227 &matches_[0]); | 229 &matches_[0]); |
228 | 230 |
229 if (minimal_changes) { | 231 if (minimal_changes) { |
230 // If the input hasn't significantly changed, we can just use the | 232 // If the input hasn't significantly changed, we can just use the |
231 // suggestions from last time. We need to readjust the relevance to | 233 // suggestions from last time. We need to readjust the relevance to |
232 // ensure it is less than the main match's relevance. | 234 // ensure it is less than the main match's relevance. |
233 for (size_t i = 0; i < extension_suggest_matches_.size(); ++i) { | 235 for (size_t i = 0; i < extension_suggest_matches_.size(); ++i) { |
234 matches_.push_back(extension_suggest_matches_[i]); | 236 matches_.push_back(extension_suggest_matches_[i]); |
235 matches_.back().relevance = matches_[0].relevance - (i + 1); | 237 matches_.back().relevance = matches_[0].relevance - (i + 1); |
236 } | 238 } |
237 } else { | 239 } else { |
238 extension_suggest_last_input_ = input; | 240 extension_suggest_last_input_ = input; |
239 extension_suggest_matches_.clear(); | 241 extension_suggest_matches_.clear(); |
240 | 242 |
241 bool have_listeners = ExtensionOmniboxEventRouter::OnInputChanged( | 243 bool have_listeners = ExtensionOmniboxEventRouter::OnInputChanged( |
242 profile_, template_url->GetExtensionId(), | 244 profile_, template_url->GetExtensionId(), |
243 UTF16ToUTF8(remaining_input), current_input_id_); | 245 WideToUTF8(remaining_input), current_input_id_); |
244 | 246 |
245 // We only have to wait for suggest results if there are actually | 247 // We only have to wait for suggest results if there are actually |
246 // extensions listening for input changes. | 248 // extensions listening for input changes. |
247 if (have_listeners) | 249 if (have_listeners) |
248 done_ = false; | 250 done_ = false; |
249 } | 251 } |
250 } | 252 } |
251 } else { | 253 } else { |
252 if (keyword_matches.size() > kMaxMatches) { | 254 if (keyword_matches.size() > kMaxMatches) { |
253 keyword_matches.erase(keyword_matches.begin() + kMaxMatches, | 255 keyword_matches.erase(keyword_matches.begin() + kMaxMatches, |
254 keyword_matches.end()); | 256 keyword_matches.end()); |
255 } | 257 } |
256 for (std::vector<string16>::const_iterator i(keyword_matches.begin()); | 258 for (std::vector<string16>::const_iterator i(keyword_matches.begin()); |
257 i != keyword_matches.end(); ++i) { | 259 i != keyword_matches.end(); ++i) { |
258 matches_.push_back(CreateAutocompleteMatch(model, *i, | 260 matches_.push_back(CreateAutocompleteMatch(model, UTF16ToWideHack(*i), |
259 input, keyword.length(), | 261 input, keyword.length(), |
260 remaining_input, -1)); | 262 remaining_input, -1)); |
261 } | 263 } |
262 } | 264 } |
263 } | 265 } |
264 | 266 |
265 void KeywordProvider::Stop() { | 267 void KeywordProvider::Stop() { |
266 done_ = true; | 268 done_ = true; |
267 MaybeEndExtensionKeywordMode(); | 269 MaybeEndExtensionKeywordMode(); |
268 } | 270 } |
269 | 271 |
270 KeywordProvider::~KeywordProvider() {} | 272 KeywordProvider::~KeywordProvider() {} |
271 | 273 |
272 // static | 274 // static |
273 bool KeywordProvider::ExtractKeywordFromInput(const AutocompleteInput& input, | 275 bool KeywordProvider::ExtractKeywordFromInput(const AutocompleteInput& input, |
274 string16* keyword, | 276 std::wstring* keyword, |
275 string16* remaining_input) { | 277 std::wstring* remaining_input) { |
276 if ((input.type() == AutocompleteInput::INVALID) || | 278 if ((input.type() == AutocompleteInput::INVALID) || |
277 (input.type() == AutocompleteInput::FORCED_QUERY)) | 279 (input.type() == AutocompleteInput::FORCED_QUERY)) |
278 return false; | 280 return false; |
279 | 281 |
280 *keyword = TemplateURLModel::CleanUserInputKeyword( | 282 *keyword = |
281 SplitKeywordFromInput(input.text(), true, remaining_input)); | 283 UTF16ToWideHack(TemplateURLModel::CleanUserInputKeyword(WideToUTF16Hack( |
| 284 SplitKeywordFromInput(input.text(), true, remaining_input)))); |
282 return !keyword->empty(); | 285 return !keyword->empty(); |
283 } | 286 } |
284 | 287 |
285 // static | 288 // static |
286 string16 KeywordProvider::SplitKeywordFromInput( | 289 std::wstring KeywordProvider::SplitKeywordFromInput( |
287 const string16& input, | 290 const std::wstring& input, |
288 bool trim_leading_whitespace, | 291 bool trim_leading_whitespace, |
289 string16* remaining_input) { | 292 std::wstring* remaining_input) { |
290 // Find end of first token. The AutocompleteController has trimmed leading | 293 // Find end of first token. The AutocompleteController has trimmed leading |
291 // whitespace, so we need not skip over that. | 294 // whitespace, so we need not skip over that. |
292 const size_t first_white(input.find_first_of(kWhitespaceUTF16)); | 295 const size_t first_white(input.find_first_of(kWhitespaceWide)); |
293 DCHECK_NE(0U, first_white); | 296 DCHECK_NE(0U, first_white); |
294 if (first_white == string16::npos) | 297 if (first_white == std::wstring::npos) |
295 return input; // Only one token provided. | 298 return input; // Only one token provided. |
296 | 299 |
297 // Set |remaining_input| to everything after the first token. | 300 // Set |remaining_input| to everything after the first token. |
298 DCHECK(remaining_input != NULL); | 301 DCHECK(remaining_input != NULL); |
299 const size_t remaining_start = trim_leading_whitespace ? | 302 const size_t remaining_start = trim_leading_whitespace ? |
300 input.find_first_not_of(kWhitespaceUTF16, first_white) : first_white + 1; | 303 input.find_first_not_of(kWhitespaceWide, first_white) : first_white + 1; |
301 | 304 |
302 if (remaining_start < input.length()) | 305 if (remaining_start < input.length()) |
303 remaining_input->assign(input.begin() + remaining_start, input.end()); | 306 remaining_input->assign(input.begin() + remaining_start, input.end()); |
304 | 307 |
305 // Return first token as keyword. | 308 // Return first token as keyword. |
306 return input.substr(0, first_white); | 309 return input.substr(0, first_white); |
307 } | 310 } |
308 | 311 |
309 // static | 312 // static |
310 void KeywordProvider::FillInURLAndContents( | 313 void KeywordProvider::FillInURLAndContents( |
311 const string16& remaining_input, | 314 const std::wstring& remaining_input, |
312 const TemplateURL* element, | 315 const TemplateURL* element, |
313 AutocompleteMatch* match) { | 316 AutocompleteMatch* match) { |
314 DCHECK(!element->short_name().empty()); | 317 DCHECK(!element->short_name().empty()); |
315 DCHECK(element->url()); | 318 DCHECK(element->url()); |
316 DCHECK(element->url()->IsValid()); | 319 DCHECK(element->url()->IsValid()); |
317 int message_id = element->IsExtensionKeyword() ? | 320 int message_id = element->IsExtensionKeyword() ? |
318 IDS_EXTENSION_KEYWORD_COMMAND : IDS_KEYWORD_SEARCH; | 321 IDS_EXTENSION_KEYWORD_COMMAND : IDS_KEYWORD_SEARCH; |
319 if (remaining_input.empty()) { | 322 if (remaining_input.empty()) { |
320 // Allow extension keyword providers to accept empty string input. This is | 323 // Allow extension keyword providers to accept empty string input. This is |
321 // useful to allow extensions to do something in the case where no input is | 324 // useful to allow extensions to do something in the case where no input is |
322 // entered. | 325 // entered. |
323 if (element->url()->SupportsReplacement() && | 326 if (element->url()->SupportsReplacement() && |
324 !element->IsExtensionKeyword()) { | 327 !element->IsExtensionKeyword()) { |
325 // No query input; return a generic, no-destination placeholder. | 328 // No query input; return a generic, no-destination placeholder. |
326 match->contents.assign( | 329 match->contents.assign(UTF16ToWideHack( |
327 l10n_util::GetStringFUTF16(message_id, | 330 l10n_util::GetStringFUTF16(message_id, |
328 element->AdjustedShortNameForLocaleDirection(), | 331 element->AdjustedShortNameForLocaleDirection(), |
329 l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE))); | 332 l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE)))); |
330 match->contents_class.push_back( | 333 match->contents_class.push_back( |
331 ACMatchClassification(0, ACMatchClassification::DIM)); | 334 ACMatchClassification(0, ACMatchClassification::DIM)); |
332 } else { | 335 } else { |
333 // Keyword that has no replacement text (aka a shorthand for a URL). | 336 // Keyword that has no replacement text (aka a shorthand for a URL). |
334 match->destination_url = GURL(element->url()->url()); | 337 match->destination_url = GURL(element->url()->url()); |
335 match->contents.assign(element->short_name()); | 338 match->contents.assign(UTF16ToWideHack(element->short_name())); |
336 AutocompleteMatch::ClassifyLocationInString(0, match->contents.length(), | 339 AutocompleteMatch::ClassifyLocationInString(0, match->contents.length(), |
337 match->contents.length(), ACMatchClassification::NONE, | 340 match->contents.length(), ACMatchClassification::NONE, |
338 &match->contents_class); | 341 &match->contents_class); |
339 } | 342 } |
340 } else { | 343 } else { |
341 // Create destination URL by escaping user input and substituting into | 344 // Create destination URL by escaping user input and substituting into |
342 // keyword template URL. The escaping here handles whitespace in user | 345 // keyword template URL. The escaping here handles whitespace in user |
343 // input, but we rely on later canonicalization functions to do more | 346 // input, but we rely on later canonicalization functions to do more |
344 // fixup to make the URL valid if necessary. | 347 // fixup to make the URL valid if necessary. |
345 DCHECK(element->url()->SupportsReplacement()); | 348 DCHECK(element->url()->SupportsReplacement()); |
346 match->destination_url = GURL(element->url()->ReplaceSearchTerms( | 349 match->destination_url = GURL(element->url()->ReplaceSearchTerms( |
347 *element, remaining_input, | 350 *element, WideToUTF16Hack(remaining_input), |
348 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16())); | 351 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE, string16())); |
349 std::vector<size_t> content_param_offsets; | 352 std::vector<size_t> content_param_offsets; |
350 match->contents.assign(l10n_util::GetStringFUTF16(message_id, | 353 match->contents.assign(UTF16ToWideHack( |
351 element->short_name(), | 354 l10n_util::GetStringFUTF16(message_id, |
352 remaining_input, | 355 element->short_name(), |
353 &content_param_offsets)); | 356 WideToUTF16Hack(remaining_input), |
| 357 &content_param_offsets))); |
354 if (content_param_offsets.size() == 2) { | 358 if (content_param_offsets.size() == 2) { |
355 AutocompleteMatch::ClassifyLocationInString(content_param_offsets[1], | 359 AutocompleteMatch::ClassifyLocationInString(content_param_offsets[1], |
356 remaining_input.length(), match->contents.length(), | 360 remaining_input.length(), match->contents.length(), |
357 ACMatchClassification::NONE, &match->contents_class); | 361 ACMatchClassification::NONE, &match->contents_class); |
358 } else { | 362 } else { |
359 // See comments on an identical NOTREACHED() in search_provider.cc. | 363 // See comments on an identical NOTREACHED() in search_provider.cc. |
360 NOTREACHED(); | 364 NOTREACHED(); |
361 } | 365 } |
362 } | 366 } |
363 } | 367 } |
364 | 368 |
365 // static | 369 // static |
366 int KeywordProvider::CalculateRelevance(AutocompleteInput::Type type, | 370 int KeywordProvider::CalculateRelevance(AutocompleteInput::Type type, |
367 bool complete, | 371 bool complete, |
368 bool no_query_text_needed, | 372 bool no_query_text_needed, |
369 bool allow_exact_keyword_match) { | 373 bool allow_exact_keyword_match) { |
370 if (!complete) | 374 if (!complete) |
371 return (type == AutocompleteInput::URL) ? 700 : 450; | 375 return (type == AutocompleteInput::URL) ? 700 : 450; |
372 if (!allow_exact_keyword_match) | 376 if (!allow_exact_keyword_match) |
373 return 1100; | 377 return 1100; |
374 if (no_query_text_needed) | 378 if (no_query_text_needed) |
375 return 1500; | 379 return 1500; |
376 return (type == AutocompleteInput::QUERY) ? 1450 : 1100; | 380 return (type == AutocompleteInput::QUERY) ? 1450 : 1100; |
377 } | 381 } |
378 | 382 |
379 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( | 383 AutocompleteMatch KeywordProvider::CreateAutocompleteMatch( |
380 TemplateURLModel* model, | 384 TemplateURLModel* model, |
381 const string16& keyword, | 385 const std::wstring& keyword, |
382 const AutocompleteInput& input, | 386 const AutocompleteInput& input, |
383 size_t prefix_length, | 387 size_t prefix_length, |
384 const string16& remaining_input, | 388 const std::wstring& remaining_input, |
385 int relevance) { | 389 int relevance) { |
386 DCHECK(model); | 390 DCHECK(model); |
387 // Get keyword data from data store. | 391 // Get keyword data from data store. |
388 const TemplateURL* element( | 392 const TemplateURL* element( |
389 model->GetTemplateURLForKeyword(keyword)); | 393 model->GetTemplateURLForKeyword(WideToUTF16Hack(keyword))); |
390 DCHECK(element && element->url()); | 394 DCHECK(element && element->url()); |
391 const bool supports_replacement = element->url()->SupportsReplacement(); | 395 const bool supports_replacement = element->url()->SupportsReplacement(); |
392 | 396 |
393 // Create an edit entry of "[keyword] [remaining input]". This is helpful | 397 // Create an edit entry of "[keyword] [remaining input]". This is helpful |
394 // even when [remaining input] is empty, as the user can select the popup | 398 // even when [remaining input] is empty, as the user can select the popup |
395 // choice and immediately begin typing in query input. | 399 // choice and immediately begin typing in query input. |
396 const bool keyword_complete = (prefix_length == keyword.length()); | 400 const bool keyword_complete = (prefix_length == keyword.length()); |
397 if (relevance < 0) { | 401 if (relevance < 0) { |
398 relevance = | 402 relevance = |
399 CalculateRelevance(input.type(), keyword_complete, | 403 CalculateRelevance(input.type(), keyword_complete, |
400 // When the user wants keyword matches to take | 404 // When the user wants keyword matches to take |
401 // preference, score them highly regardless of | 405 // preference, score them highly regardless of |
402 // whether the input provides query text. | 406 // whether the input provides query text. |
403 input.prefer_keyword() || !supports_replacement, | 407 input.prefer_keyword() || !supports_replacement, |
404 input.allow_exact_keyword_match()); | 408 input.allow_exact_keyword_match()); |
405 } | 409 } |
406 AutocompleteMatch result(this, relevance, false, | 410 AutocompleteMatch result(this, relevance, false, |
407 supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE : | 411 supports_replacement ? AutocompleteMatch::SEARCH_OTHER_ENGINE : |
408 AutocompleteMatch::HISTORY_KEYWORD); | 412 AutocompleteMatch::HISTORY_KEYWORD); |
409 result.fill_into_edit.assign(keyword); | 413 result.fill_into_edit.assign(keyword); |
410 if (!remaining_input.empty() || !keyword_complete || supports_replacement) | 414 if (!remaining_input.empty() || !keyword_complete || supports_replacement) |
411 result.fill_into_edit.push_back(L' '); | 415 result.fill_into_edit.push_back(L' '); |
412 result.fill_into_edit.append(remaining_input); | 416 result.fill_into_edit.append(remaining_input); |
413 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd | 417 // If we wanted to set |result.inline_autocomplete_offset| correctly, we'd |
414 // need CleanUserInputKeyword() to return the amount of adjustment it's made | 418 // need CleanUserInputKeyword() to return the amount of adjustment it's made |
415 // to the user's input. Because right now inexact keyword matches can't score | 419 // to the user's input. Because right now inexact keyword matches can't score |
416 // more highly than a "what you typed" match from one of the other providers, | 420 // more highly than a "what you typed" match from one of the other providers, |
417 // we just don't bother to do this, and leave inline autocompletion off. | 421 // we just don't bother to do this, and leave inline autocompletion off. |
418 result.inline_autocomplete_offset = string16::npos; | 422 result.inline_autocomplete_offset = std::wstring::npos; |
419 | 423 |
420 // Create destination URL and popup entry content by substituting user input | 424 // Create destination URL and popup entry content by substituting user input |
421 // into keyword templates. | 425 // into keyword templates. |
422 FillInURLAndContents(remaining_input, element, &result); | 426 FillInURLAndContents(remaining_input, element, &result); |
423 | 427 |
424 if (supports_replacement) | 428 if (supports_replacement) |
425 result.template_url = element; | 429 result.template_url = element; |
426 result.transition = PageTransition::KEYWORD; | 430 result.transition = PageTransition::KEYWORD; |
427 | 431 |
428 // Create popup entry description based on the keyword name. | 432 // Create popup entry description based on the keyword name. |
429 if (!element->IsExtensionKeyword()) { | 433 if (!element->IsExtensionKeyword()) { |
430 result.description.assign(l10n_util::GetStringFUTF16( | 434 result.description.assign(UTF16ToWideHack(l10n_util::GetStringFUTF16( |
431 IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION, keyword)); | 435 IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION, WideToUTF16Hack(keyword)))); |
432 string16 keyword_desc( | 436 string16 keyword_desc( |
433 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION)); | 437 l10n_util::GetStringUTF16(IDS_AUTOCOMPLETE_KEYWORD_DESCRIPTION)); |
434 AutocompleteMatch::ClassifyLocationInString( | 438 AutocompleteMatch::ClassifyLocationInString( |
435 keyword_desc.find(ASCIIToUTF16("%s")), | 439 keyword_desc.find(ASCIIToUTF16("%s")), |
436 prefix_length, | 440 prefix_length, |
437 result.description.length(), | 441 result.description.length(), |
438 ACMatchClassification::DIM, | 442 ACMatchClassification::DIM, |
439 &result.description_class); | 443 &result.description_class); |
440 } | 444 } |
441 | 445 |
442 return result; | 446 return result; |
443 } | 447 } |
444 | 448 |
445 void KeywordProvider::Observe(NotificationType type, | 449 void KeywordProvider::Observe(NotificationType type, |
446 const NotificationSource& source, | 450 const NotificationSource& source, |
447 const NotificationDetails& details) { | 451 const NotificationDetails& details) { |
448 TemplateURLModel* model = profile_ ? profile_->GetTemplateURLModel() : model_; | 452 TemplateURLModel* model = profile_ ? profile_->GetTemplateURLModel() : model_; |
449 const AutocompleteInput& input = extension_suggest_last_input_; | 453 const AutocompleteInput& input = extension_suggest_last_input_; |
450 | 454 |
451 switch (type.value) { | 455 switch (type.value) { |
452 case NotificationType::EXTENSION_OMNIBOX_INPUT_ENTERED: | 456 case NotificationType::EXTENSION_OMNIBOX_INPUT_ENTERED: |
453 // Input has been accepted, so we're done with this input session. Ensure | 457 // Input has been accepted, so we're done with this input session. Ensure |
454 // we don't send the OnInputCancelled event. | 458 // we don't send the OnInputCancelled event. |
455 current_keyword_extension_id_.clear(); | 459 current_keyword_extension_id_.clear(); |
456 return; | 460 return; |
457 | 461 |
458 case NotificationType::EXTENSION_OMNIBOX_DEFAULT_SUGGESTION_CHANGED: { | 462 case NotificationType::EXTENSION_OMNIBOX_DEFAULT_SUGGESTION_CHANGED: { |
459 // It's possible to change the default suggestion while not in an editing | 463 // It's possible to change the default suggestion while not in an editing |
460 // session. | 464 // session. |
461 string16 keyword, remaining_input; | 465 std::wstring keyword, remaining_input; |
462 if (matches_.empty() || current_keyword_extension_id_.empty() || | 466 if (matches_.empty() || current_keyword_extension_id_.empty() || |
463 !ExtractKeywordFromInput(input, &keyword, &remaining_input)) | 467 !ExtractKeywordFromInput(input, &keyword, &remaining_input)) |
464 return; | 468 return; |
465 | 469 |
466 const TemplateURL* template_url( | 470 const TemplateURL* template_url( |
467 model->GetTemplateURLForKeyword(keyword)); | 471 model->GetTemplateURLForKeyword(WideToUTF16Hack(keyword))); |
468 ApplyDefaultSuggestionForExtensionKeyword(profile_, template_url, | 472 ApplyDefaultSuggestionForExtensionKeyword(profile_, template_url, |
469 remaining_input, | 473 WideToUTF16(remaining_input), |
470 &matches_[0]); | 474 &matches_[0]); |
471 listener_->OnProviderUpdate(true); | 475 listener_->OnProviderUpdate(true); |
472 return; | 476 return; |
473 } | 477 } |
474 | 478 |
475 case NotificationType::EXTENSION_OMNIBOX_SUGGESTIONS_READY: { | 479 case NotificationType::EXTENSION_OMNIBOX_SUGGESTIONS_READY: { |
476 const ExtensionOmniboxSuggestions& suggestions = | 480 const ExtensionOmniboxSuggestions& suggestions = |
477 *Details<ExtensionOmniboxSuggestions>(details).ptr(); | 481 *Details<ExtensionOmniboxSuggestions>(details).ptr(); |
478 if (suggestions.request_id != current_input_id_) | 482 if (suggestions.request_id != current_input_id_) |
479 return; // This is an old result. Just ignore. | 483 return; // This is an old result. Just ignore. |
480 | 484 |
481 string16 keyword, remaining_input; | 485 std::wstring keyword, remaining_input; |
482 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) { | 486 if (!ExtractKeywordFromInput(input, &keyword, &remaining_input)) { |
483 NOTREACHED(); | 487 NOTREACHED(); |
484 return; | 488 return; |
485 } | 489 } |
486 | 490 |
487 // TODO(mpcomplete): consider clamping the number of suggestions to | 491 // TODO(mpcomplete): consider clamping the number of suggestions to |
488 // AutocompleteProvider::kMaxMatches. | 492 // AutocompleteProvider::kMaxMatches. |
489 for (size_t i = 0; i < suggestions.suggestions.size(); ++i) { | 493 for (size_t i = 0; i < suggestions.suggestions.size(); ++i) { |
490 const ExtensionOmniboxSuggestion& suggestion = | 494 const ExtensionOmniboxSuggestion& suggestion = |
491 suggestions.suggestions[i]; | 495 suggestions.suggestions[i]; |
492 // We want to order these suggestions in descending order, so start with | 496 // We want to order these suggestions in descending order, so start with |
493 // the relevance of the first result (added synchronously in Start()), | 497 // the relevance of the first result (added synchronously in Start()), |
494 // and subtract 1 for each subsequent suggestion from the extension. | 498 // and subtract 1 for each subsequent suggestion from the extension. |
495 // We know that |complete| is true, because we wouldn't get results from | 499 // We know that |complete| is true, because we wouldn't get results from |
496 // the extension unless the full keyword had been typed. | 500 // the extension unless the full keyword had been typed. |
497 int first_relevance = CalculateRelevance(input.type(), true, | 501 int first_relevance = CalculateRelevance(input.type(), true, |
498 input.prefer_keyword(), input.allow_exact_keyword_match()); | 502 input.prefer_keyword(), input.allow_exact_keyword_match()); |
499 extension_suggest_matches_.push_back(CreateAutocompleteMatch( | 503 extension_suggest_matches_.push_back(CreateAutocompleteMatch( |
500 model, keyword, input, keyword.length(), | 504 model, keyword, input, keyword.length(), |
501 suggestion.content, first_relevance - (i + 1))); | 505 UTF16ToWide(suggestion.content), first_relevance - (i + 1))); |
502 | 506 |
503 AutocompleteMatch* match = &extension_suggest_matches_.back(); | 507 AutocompleteMatch* match = &extension_suggest_matches_.back(); |
504 match->contents.assign(suggestion.description); | 508 match->contents.assign(UTF16ToWide(suggestion.description)); |
505 match->contents_class = suggestion.description_styles; | 509 match->contents_class = suggestion.description_styles; |
506 match->description.clear(); | 510 match->description.clear(); |
507 match->description_class.clear(); | 511 match->description_class.clear(); |
508 } | 512 } |
509 | 513 |
510 done_ = true; | 514 done_ = true; |
511 matches_.insert(matches_.end(), extension_suggest_matches_.begin(), | 515 matches_.insert(matches_.end(), extension_suggest_matches_.begin(), |
512 extension_suggest_matches_.end()); | 516 extension_suggest_matches_.end()); |
513 listener_->OnProviderUpdate(!extension_suggest_matches_.empty()); | 517 listener_->OnProviderUpdate(!extension_suggest_matches_.empty()); |
514 return; | 518 return; |
(...skipping 15 matching lines...) Expand all Loading... |
530 } | 534 } |
531 | 535 |
532 void KeywordProvider::MaybeEndExtensionKeywordMode() { | 536 void KeywordProvider::MaybeEndExtensionKeywordMode() { |
533 if (!current_keyword_extension_id_.empty()) { | 537 if (!current_keyword_extension_id_.empty()) { |
534 ExtensionOmniboxEventRouter::OnInputCancelled( | 538 ExtensionOmniboxEventRouter::OnInputCancelled( |
535 profile_, current_keyword_extension_id_); | 539 profile_, current_keyword_extension_id_); |
536 | 540 |
537 current_keyword_extension_id_.clear(); | 541 current_keyword_extension_id_.clear(); |
538 } | 542 } |
539 } | 543 } |
OLD | NEW |