blob: 2b308a3054224414af1a06445af2fbfd43cdfa81 [file] [log] [blame]
[email protected]f90bf0d92011-01-13 02:12:441// Copyright (c) 2011 The Chromium Authors. All rights reserved.
license.botbf09a502008-08-24 00:55:552// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
initial.commit09911bf2008-07-26 23:55:294
5#include "chrome/browser/autocomplete/search_provider.h"
6
[email protected]1cb2dac2010-03-08 21:49:157#include <algorithm>
[email protected]c3a4bd992010-08-18 20:25:018#include <cmath>
[email protected]1cb2dac2010-03-08 21:49:159
[email protected]a92b8642009-05-05 23:38:5610#include "app/l10n_util.h"
[email protected]2041cf342010-02-19 03:15:5911#include "base/callback.h"
[email protected]d6e58c6e2009-10-10 20:40:5012#include "base/i18n/icu_string_conversions.h"
initial.commit09911bf2008-07-26 23:55:2913#include "base/message_loop.h"
[email protected]dc9a6762010-08-16 07:13:5314#include "base/string16.h"
[email protected]1cb2dac2010-03-08 21:49:1515#include "base/utf_string_conversions.h"
[email protected]257ab712009-04-14 17:16:2416#include "chrome/browser/autocomplete/keyword_provider.h"
[email protected]9ac40092010-10-27 23:05:2617#include "chrome/browser/autocomplete/autocomplete_match.h"
[email protected]f7578f52010-08-30 22:22:4918#include "chrome/browser/google/google_util.h"
[email protected]ce560f82009-06-03 09:39:4419#include "chrome/browser/history/history.h"
[email protected]4ab4c7c2010-11-24 04:49:3420#include "chrome/browser/instant/instant_controller.h"
[email protected]f870a322009-01-16 21:47:2721#include "chrome/browser/net/url_fixer_upper.h"
[email protected]37858e52010-08-26 00:22:0222#include "chrome/browser/prefs/pref_service.h"
[email protected]8ecad5e2010-12-02 21:18:3323#include "chrome/browser/profiles/profile.h"
[email protected]8d457132010-11-04 18:13:4024#include "chrome/browser/history/in_memory_database.h"
[email protected]d54e03a52009-01-16 00:31:0425#include "chrome/browser/search_engines/template_url_model.h"
initial.commit09911bf2008-07-26 23:55:2926#include "chrome/common/json_value_serializer.h"
initial.commit09911bf2008-07-26 23:55:2927#include "chrome/common/pref_names.h"
[email protected]dcf7d352009-02-26 01:56:0228#include "chrome/common/url_constants.h"
initial.commit09911bf2008-07-26 23:55:2929#include "googleurl/src/url_util.h"
[email protected]34ac8f32009-02-22 23:03:2730#include "grit/generated_resources.h"
initial.commit09911bf2008-07-26 23:55:2931#include "net/base/escape.h"
[email protected]319d9e6f2009-02-18 19:47:2132#include "net/http/http_response_headers.h"
33#include "net/url_request/url_request_status.h"
initial.commit09911bf2008-07-26 23:55:2934
[email protected]e1acf6f2008-10-27 20:43:3335using base::Time;
36using base::TimeDelta;
37
[email protected]b547666d2009-04-23 16:37:5838// static
39const int SearchProvider::kDefaultProviderURLFetcherID = 1;
40// static
41const int SearchProvider::kKeywordProviderURLFetcherID = 2;
42
43// static
44bool SearchProvider::query_suggest_immediately_ = false;
45
[email protected]257ab712009-04-14 17:16:2446void SearchProvider::Providers::Set(const TemplateURL* default_provider,
47 const TemplateURL* keyword_provider) {
48 // TODO(pkasting): http://b/1162970 We shouldn't need to structure-copy
49 // this. Nor should we need |default_provider_| and |keyword_provider_|
50 // just to know whether the provider changed.
51 default_provider_ = default_provider;
52 if (default_provider)
53 cached_default_provider_ = *default_provider;
54 keyword_provider_ = keyword_provider;
55 if (keyword_provider)
56 cached_keyword_provider_ = *keyword_provider;
57}
58
[email protected]601858c02010-09-01 17:08:2059SearchProvider::SearchProvider(ACProviderListener* listener, Profile* profile)
60 : AutocompleteProvider(listener, profile, "Search"),
[email protected]601858c02010-09-01 17:08:2061 suggest_results_pending_(0),
[email protected]8e5cc282010-12-05 18:11:3962 have_suggest_results_(false),
[email protected]4ab4c7c2010-11-24 04:49:3463 instant_finalized_(false) {
64}
65
[email protected]e918c112010-12-08 23:03:4966void SearchProvider::FinalizeInstantQuery(const std::wstring& input_text,
67 const std::wstring& suggest_text) {
[email protected]4ab4c7c2010-11-24 04:49:3468 if (done_ || instant_finalized_)
69 return;
70
71 instant_finalized_ = true;
72 UpdateDone();
73
[email protected]e918c112010-12-08 23:03:4974 if (input_text.empty()) {
[email protected]4ab4c7c2010-11-24 04:49:3475 // We only need to update the listener if we're actually done.
76 if (done_)
77 listener_->OnProviderUpdate(false);
78 return;
79 }
80
[email protected]9e789742011-01-10 23:27:3281 default_provider_suggest_text_ = suggest_text;
82
[email protected]e918c112010-12-08 23:03:4983 std::wstring adjusted_input_text(input_text);
84 AutocompleteInput::RemoveForcedQueryStringIfNecessary(input_.type(),
85 &adjusted_input_text);
86
87 const std::wstring text = adjusted_input_text + suggest_text;
[email protected]4ab4c7c2010-11-24 04:49:3488 // Remove any matches that are identical to |text|. We don't use the
89 // destination_url for comparison as it varies depending upon the index passed
90 // to TemplateURL::ReplaceSearchTerms.
91 for (ACMatches::iterator i = matches_.begin(); i != matches_.end();) {
[email protected]70833262011-01-05 23:40:4492 // Reset the description/description_class of all searches. We'll set the
93 // description of the new first match in the call to
94 // UpdateFirstSearchMatchDescription() below.
95 if ((i->type == AutocompleteMatch::SEARCH_HISTORY) ||
96 (i->type == AutocompleteMatch::SEARCH_SUGGEST) ||
97 (i->type == AutocompleteMatch::SEARCH_WHAT_YOU_TYPED)) {
98 i->description.clear();
99 i->description_class.clear();
100 }
101
[email protected]4ab4c7c2010-11-24 04:49:34102 if (((i->type == AutocompleteMatch::SEARCH_HISTORY) ||
103 (i->type == AutocompleteMatch::SEARCH_SUGGEST)) &&
104 (i->fill_into_edit == text)) {
[email protected]e030de62010-11-24 05:41:19105 i = matches_.erase(i);
[email protected]4ab4c7c2010-11-24 04:49:34106 } else {
107 ++i;
108 }
109 }
110
111 // Add the new suggest result. We give it a rank higher than
112 // SEARCH_WHAT_YOU_TYPED so that it gets autocompleted.
113 int did_not_accept_default_suggestion = default_suggest_results_.empty() ?
114 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE :
115 TemplateURLRef::NO_SUGGESTION_CHOSEN;
116 MatchMap match_map;
[email protected]e918c112010-12-08 23:03:49117 AddMatchToMap(text, adjusted_input_text,
118 CalculateRelevanceForWhatYouTyped() + 1,
[email protected]4ab4c7c2010-11-24 04:49:34119 AutocompleteMatch::SEARCH_SUGGEST,
[email protected]e918c112010-12-08 23:03:49120 did_not_accept_default_suggestion, false,
121 input_.initial_prevent_inline_autocomplete(), &match_map);
[email protected]4ab4c7c2010-11-24 04:49:34122 DCHECK_EQ(1u, match_map.size());
123 matches_.push_back(match_map.begin()->second);
[email protected]70833262011-01-05 23:40:44124 // Sort the results so that UpdateFirstSearchDescription does the right thing.
125 std::sort(matches_.begin(), matches_.end(), &AutocompleteMatch::MoreRelevant);
126
127 UpdateFirstSearchMatchDescription();
[email protected]4ab4c7c2010-11-24 04:49:34128
129 listener_->OnProviderUpdate(true);
[email protected]601858c02010-09-01 17:08:20130}
131
initial.commit09911bf2008-07-26 23:55:29132void SearchProvider::Start(const AutocompleteInput& input,
[email protected]8deeb952008-10-09 18:21:27133 bool minimal_changes) {
initial.commit09911bf2008-07-26 23:55:29134 matches_.clear();
135
[email protected]4ab4c7c2010-11-24 04:49:34136 instant_finalized_ = input.synchronous_only();
137
[email protected]6c85aa02009-02-27 12:08:09138 // Can't return search/suggest results for bogus input or without a profile.
initial.commit09911bf2008-07-26 23:55:29139 if (!profile_ || (input.type() == AutocompleteInput::INVALID)) {
140 Stop();
141 return;
142 }
143
[email protected]257ab712009-04-14 17:16:24144 keyword_input_text_.clear();
145 const TemplateURL* keyword_provider =
146 KeywordProvider::GetSubstitutingTemplateURLForInput(profile_, input,
147 &keyword_input_text_);
[email protected]8d457132010-11-04 18:13:40148 if (keyword_input_text_.empty())
[email protected]257ab712009-04-14 17:16:24149 keyword_provider = NULL;
[email protected]257ab712009-04-14 17:16:24150
151 const TemplateURL* default_provider =
initial.commit09911bf2008-07-26 23:55:29152 profile_->GetTemplateURLModel()->GetDefaultSearchProvider();
[email protected]257ab712009-04-14 17:16:24153 if (!TemplateURL::SupportsReplacement(default_provider))
154 default_provider = NULL;
155
156 if (keyword_provider == default_provider)
157 keyword_provider = NULL; // No use in querying the same provider twice.
158
159 if (!default_provider && !keyword_provider) {
160 // No valid providers.
initial.commit09911bf2008-07-26 23:55:29161 Stop();
162 return;
163 }
164
165 // If we're still running an old query but have since changed the query text
[email protected]257ab712009-04-14 17:16:24166 // or the providers, abort the query.
[email protected]9e789742011-01-10 23:27:32167 if (!minimal_changes ||
168 !providers_.equals(default_provider, keyword_provider)) {
169 if (done_)
170 default_provider_suggest_text_.clear();
171 else
172 Stop();
[email protected]257ab712009-04-14 17:16:24173 }
initial.commit09911bf2008-07-26 23:55:29174
[email protected]257ab712009-04-14 17:16:24175 providers_.Set(default_provider, keyword_provider);
initial.commit09911bf2008-07-26 23:55:29176
177 if (input.text().empty()) {
178 // User typed "?" alone. Give them a placeholder result indicating what
179 // this syntax does.
[email protected]257ab712009-04-14 17:16:24180 if (default_provider) {
[email protected]69c579e2010-04-23 20:01:00181 AutocompleteMatch match;
182 match.provider = this;
[email protected]d1318572010-12-29 22:37:45183 match.contents.assign(UTF16ToWideHack(
184 l10n_util::GetStringUTF16(IDS_EMPTY_KEYWORD_VALUE)));
[email protected]257ab712009-04-14 17:16:24185 match.contents_class.push_back(
[email protected]2c33dd22010-02-11 21:46:35186 ACMatchClassification(0, ACMatchClassification::NONE));
[email protected]257ab712009-04-14 17:16:24187 matches_.push_back(match);
[email protected]70833262011-01-05 23:40:44188 UpdateFirstSearchMatchDescription();
[email protected]257ab712009-04-14 17:16:24189 }
initial.commit09911bf2008-07-26 23:55:29190 Stop();
191 return;
192 }
193
194 input_ = input;
195
[email protected]8d457132010-11-04 18:13:40196 DoHistoryQuery(minimal_changes);
[email protected]8deeb952008-10-09 18:21:27197 StartOrStopSuggestQuery(minimal_changes);
initial.commit09911bf2008-07-26 23:55:29198 ConvertResultsToAutocompleteMatches();
199}
200
201void SearchProvider::Run() {
202 // Start a new request with the current input.
203 DCHECK(!done_);
[email protected]257ab712009-04-14 17:16:24204 suggest_results_pending_ = 0;
205 if (providers_.valid_suggest_for_keyword_provider()) {
206 suggest_results_pending_++;
207 keyword_fetcher_.reset(
[email protected]b547666d2009-04-23 16:37:58208 CreateSuggestFetcher(kKeywordProviderURLFetcherID,
209 providers_.keyword_provider(),
[email protected]257ab712009-04-14 17:16:24210 keyword_input_text_));
211 }
212 if (providers_.valid_suggest_for_default_provider()) {
213 suggest_results_pending_++;
214 default_fetcher_.reset(
[email protected]b547666d2009-04-23 16:37:58215 CreateSuggestFetcher(kDefaultProviderURLFetcherID,
216 providers_.default_provider(), input_.text()));
[email protected]257ab712009-04-14 17:16:24217 }
218 // We should only get here if we have a suggest url for the keyword or default
219 // providers.
[email protected]1cb2dac2010-03-08 21:49:15220 DCHECK_GT(suggest_results_pending_, 0);
initial.commit09911bf2008-07-26 23:55:29221}
222
223void SearchProvider::Stop() {
initial.commit09911bf2008-07-26 23:55:29224 StopSuggest();
225 done_ = true;
[email protected]9e789742011-01-10 23:27:32226 default_provider_suggest_text_.clear();
initial.commit09911bf2008-07-26 23:55:29227}
228
229void SearchProvider::OnURLFetchComplete(const URLFetcher* source,
230 const GURL& url,
[email protected]f90bf0d92011-01-13 02:12:44231 const net::URLRequestStatus& status,
initial.commit09911bf2008-07-26 23:55:29232 int response_code,
233 const ResponseCookies& cookie,
234 const std::string& data) {
235 DCHECK(!done_);
[email protected]257ab712009-04-14 17:16:24236 suggest_results_pending_--;
[email protected]1cb2dac2010-03-08 21:49:15237 DCHECK_GE(suggest_results_pending_, 0); // Should never go negative.
[email protected]ec9207d32008-09-26 00:51:06238 const net::HttpResponseHeaders* const response_headers =
239 source->response_headers();
240 std::string json_data(data);
[email protected]6c85aa02009-02-27 12:08:09241 // JSON is supposed to be UTF-8, but some suggest service providers send JSON
242 // files in non-UTF-8 encodings. The actual encoding is usually specified in
243 // the Content-Type header field.
[email protected]ec9207d32008-09-26 00:51:06244 if (response_headers) {
245 std::string charset;
246 if (response_headers->GetCharset(&charset)) {
247 std::wstring wide_data;
248 // TODO(jungshik): Switch to CodePageToUTF8 after it's added.
[email protected]d6e58c6e2009-10-10 20:40:50249 if (base::CodepageToWide(data, charset.c_str(),
250 base::OnStringConversionError::FAIL,
251 &wide_data))
[email protected]f0a51fb52009-03-05 12:46:38252 json_data = WideToUTF8(wide_data);
[email protected]ec9207d32008-09-26 00:51:06253 }
254 }
255
[email protected]257ab712009-04-14 17:16:24256 bool is_keyword_results = (source == keyword_fetcher_.get());
257 SuggestResults* suggest_results = is_keyword_results ?
258 &keyword_suggest_results_ : &default_suggest_results_;
259
[email protected]b4cebf82008-12-29 19:59:08260 if (status.is_success() && response_code == 200) {
261 JSONStringValueSerializer deserializer(json_data);
262 deserializer.set_allow_trailing_comma(true);
[email protected]ba399672010-04-06 15:42:39263 scoped_ptr<Value> root_val(deserializer.Deserialize(NULL, NULL));
[email protected]257ab712009-04-14 17:16:24264 const std::wstring& input_text =
265 is_keyword_results ? keyword_input_text_ : input_.text();
[email protected]b4cebf82008-12-29 19:59:08266 have_suggest_results_ =
[email protected]257ab712009-04-14 17:16:24267 root_val.get() &&
268 ParseSuggestResults(root_val.get(), is_keyword_results, input_text,
269 suggest_results);
[email protected]b4cebf82008-12-29 19:59:08270 }
271
initial.commit09911bf2008-07-26 23:55:29272 ConvertResultsToAutocompleteMatches();
[email protected]257ab712009-04-14 17:16:24273 listener_->OnProviderUpdate(!suggest_results->empty());
initial.commit09911bf2008-07-26 23:55:29274}
275
[email protected]601858c02010-09-01 17:08:20276SearchProvider::~SearchProvider() {
277}
278
[email protected]8d457132010-11-04 18:13:40279void SearchProvider::DoHistoryQuery(bool minimal_changes) {
280 // The history query results are synchronous, so if minimal_changes is true,
281 // we still have the last results and don't need to do anything.
282 if (minimal_changes)
initial.commit09911bf2008-07-26 23:55:29283 return;
284
[email protected]8d457132010-11-04 18:13:40285 keyword_history_results_.clear();
286 default_history_results_.clear();
initial.commit09911bf2008-07-26 23:55:29287
[email protected]8d457132010-11-04 18:13:40288 HistoryService* const history_service =
289 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
290 history::URLDatabase* url_db = history_service ?
291 history_service->InMemoryDatabase() : NULL;
292 if (!url_db)
initial.commit09911bf2008-07-26 23:55:29293 return;
294
[email protected]257ab712009-04-14 17:16:24295 // Request history for both the keyword and default provider.
296 if (providers_.valid_keyword_provider()) {
[email protected]8d457132010-11-04 18:13:40297 url_db->GetMostRecentKeywordSearchTerms(
298 providers_.keyword_provider().id(),
299 WideToUTF16(keyword_input_text_),
300 static_cast<int>(kMaxMatches),
301 &keyword_history_results_);
[email protected]257ab712009-04-14 17:16:24302 }
303 if (providers_.valid_default_provider()) {
[email protected]8d457132010-11-04 18:13:40304 url_db->GetMostRecentKeywordSearchTerms(
305 providers_.default_provider().id(),
306 WideToUTF16(input_.text()),
307 static_cast<int>(kMaxMatches),
308 &default_history_results_);
[email protected]257ab712009-04-14 17:16:24309 }
initial.commit09911bf2008-07-26 23:55:29310}
311
[email protected]8deeb952008-10-09 18:21:27312void SearchProvider::StartOrStopSuggestQuery(bool minimal_changes) {
[email protected]6c85aa02009-02-27 12:08:09313 // Don't send any queries to the server until some time has elapsed after
314 // the last keypress, to avoid flooding the server with requests we are
315 // likely to end up throwing away anyway.
316 static const int kQueryDelayMs = 200;
317
[email protected]83c726482008-09-10 06:36:34318 if (!IsQuerySuitableForSuggest()) {
initial.commit09911bf2008-07-26 23:55:29319 StopSuggest();
320 return;
321 }
322
323 // For the minimal_changes case, if we finished the previous query and still
324 // have its results, or are allowed to keep running it, just do that, rather
325 // than starting a new query.
326 if (minimal_changes &&
[email protected]8deeb952008-10-09 18:21:27327 (have_suggest_results_ || (!done_ && !input_.synchronous_only())))
initial.commit09911bf2008-07-26 23:55:29328 return;
329
330 // We can't keep running any previous query, so halt it.
331 StopSuggest();
332
333 // We can't start a new query if we're only allowed synchronous results.
[email protected]8deeb952008-10-09 18:21:27334 if (input_.synchronous_only())
initial.commit09911bf2008-07-26 23:55:29335 return;
336
[email protected]257ab712009-04-14 17:16:24337 // We'll have at least one pending fetch. Set it to 1 now, but the value is
338 // correctly set in Run. As Run isn't invoked immediately we need to set this
339 // now, else we won't think we're waiting on results from the server when we
340 // really are.
341 suggest_results_pending_ = 1;
342
initial.commit09911bf2008-07-26 23:55:29343 // Kick off a timer that will start the URL fetch if it completes before
344 // the user types another character.
[email protected]b547666d2009-04-23 16:37:58345 int delay = query_suggest_immediately_ ? 0 : kQueryDelayMs;
346 timer_.Start(TimeDelta::FromMilliseconds(delay), this, &SearchProvider::Run);
initial.commit09911bf2008-07-26 23:55:29347}
348
[email protected]83c726482008-09-10 06:36:34349bool SearchProvider::IsQuerySuitableForSuggest() const {
350 // Don't run Suggest when off the record, the engine doesn't support it, or
351 // the user has disabled it.
352 if (profile_->IsOffTheRecord() ||
[email protected]257ab712009-04-14 17:16:24353 (!providers_.valid_suggest_for_keyword_provider() &&
354 !providers_.valid_suggest_for_default_provider()) ||
[email protected]83c726482008-09-10 06:36:34355 !profile_->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled))
356 return false;
357
[email protected]cac59d32010-08-09 23:23:14358 // If the input type might be a URL, we take extra care so that private data
[email protected]83c726482008-09-10 06:36:34359 // isn't sent to the server.
[email protected]83c726482008-09-10 06:36:34360
[email protected]cac59d32010-08-09 23:23:14361 // FORCED_QUERY means the user is explicitly asking us to search for this, so
362 // we assume it isn't a URL and/or there isn't private data.
363 if (input_.type() == AutocompleteInput::FORCED_QUERY)
364 return true;
[email protected]83c726482008-09-10 06:36:34365
[email protected]cac59d32010-08-09 23:23:14366 // Next we check the scheme. If this is UNKNOWN/REQUESTED_URL/URL with a
367 // scheme that isn't http/https/ftp, we shouldn't send it. Sending things
368 // like file: and data: is both a waste of time and a disclosure of
369 // potentially private, local data. Other "schemes" may actually be
370 // usernames, and we don't want to send passwords. If the scheme is OK, we
371 // still need to check other cases below. If this is QUERY, then the presence
372 // of these schemes means the user explicitly typed one, and thus this is
373 // probably a URL that's being entered and happens to currently be invalid --
374 // in which case we again want to run our checks below. Other QUERY cases are
375 // less likely to be URLs and thus we assume we're OK.
376 if ((input_.scheme() != L"http") && (input_.scheme() != L"https") &&
377 (input_.scheme() != L"ftp"))
378 return (input_.type() == AutocompleteInput::QUERY);
379
380 // Don't send URLs with usernames, queries or refs. Some of these are
381 // private, and the Suggest server is unlikely to have any useful results
382 // for any of them. Also don't send URLs with ports, as we may initially
383 // think that a username + password is a host + port (and we don't want to
384 // send usernames/passwords), and even if the port really is a port, the
385 // server is once again unlikely to have and useful results.
386 const url_parse::Parsed& parts = input_.parts();
387 if (parts.username.is_nonempty() || parts.port.is_nonempty() ||
388 parts.query.is_nonempty() || parts.ref.is_nonempty())
389 return false;
390
391 // Don't send anything for https except the hostname. Hostnames are OK
392 // because they are visible when the TCP connection is established, but the
393 // specific path may reveal private information.
394 if ((input_.scheme() == L"https") && parts.path.is_nonempty())
395 return false;
[email protected]83c726482008-09-10 06:36:34396
397 return true;
398}
399
initial.commit09911bf2008-07-26 23:55:29400void SearchProvider::StopSuggest() {
[email protected]257ab712009-04-14 17:16:24401 suggest_results_pending_ = 0;
[email protected]2d316662008-09-03 18:18:14402 timer_.Stop();
[email protected]257ab712009-04-14 17:16:24403 // Stop any in-progress URL fetches.
404 keyword_fetcher_.reset();
405 default_fetcher_.reset();
406 keyword_suggest_results_.clear();
407 default_suggest_results_.clear();
408 keyword_navigation_results_.clear();
409 default_navigation_results_.clear();
initial.commit09911bf2008-07-26 23:55:29410 have_suggest_results_ = false;
initial.commit09911bf2008-07-26 23:55:29411}
412
[email protected]b547666d2009-04-23 16:37:58413URLFetcher* SearchProvider::CreateSuggestFetcher(int id,
414 const TemplateURL& provider,
[email protected]257ab712009-04-14 17:16:24415 const std::wstring& text) {
416 const TemplateURLRef* const suggestions_url = provider.suggestions_url();
417 DCHECK(suggestions_url->SupportsReplacement());
[email protected]b547666d2009-04-23 16:37:58418 URLFetcher* fetcher = URLFetcher::Create(id,
[email protected]ddd231e2010-06-29 20:35:19419 GURL(suggestions_url->ReplaceSearchTerms(
420 provider, text, TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
421 std::wstring())),
[email protected]257ab712009-04-14 17:16:24422 URLFetcher::GET, this);
423 fetcher->set_request_context(profile_->GetRequestContext());
424 fetcher->Start();
425 return fetcher;
426}
427
428bool SearchProvider::ParseSuggestResults(Value* root_val,
429 bool is_keyword,
430 const std::wstring& input_text,
431 SuggestResults* suggest_results) {
initial.commit09911bf2008-07-26 23:55:29432 if (!root_val->IsType(Value::TYPE_LIST))
433 return false;
434 ListValue* root_list = static_cast<ListValue*>(root_val);
435
436 Value* query_val;
[email protected]dc9a6762010-08-16 07:13:53437 string16 query_str;
initial.commit09911bf2008-07-26 23:55:29438 Value* result_val;
439 if ((root_list->GetSize() < 2) || !root_list->Get(0, &query_val) ||
[email protected]dc9a6762010-08-16 07:13:53440 !query_val->GetAsString(&query_str) ||
441 (query_str != WideToUTF16Hack(input_text)) ||
initial.commit09911bf2008-07-26 23:55:29442 !root_list->Get(1, &result_val) || !result_val->IsType(Value::TYPE_LIST))
443 return false;
444
445 ListValue* description_list = NULL;
446 if (root_list->GetSize() > 2) {
447 // 3rd element: Description list.
448 Value* description_val;
449 if (root_list->Get(2, &description_val) &&
450 description_val->IsType(Value::TYPE_LIST))
451 description_list = static_cast<ListValue*>(description_val);
452 }
453
454 // We don't care about the query URL list (the fourth element in the
455 // response) for now.
456
457 // Parse optional data in the results from the Suggest server if any.
458 ListValue* type_list = NULL;
459 // 5th argument: Optional key-value pairs.
460 // TODO: We may iterate the 5th+ arguments of the root_list if any other
461 // optional data are defined.
462 if (root_list->GetSize() > 4) {
463 Value* optional_val;
464 if (root_list->Get(4, &optional_val) &&
465 optional_val->IsType(Value::TYPE_DICTIONARY)) {
466 DictionaryValue* dict_val = static_cast<DictionaryValue*>(optional_val);
467
468 // Parse Google Suggest specific type extension.
[email protected]a65175d2010-08-17 04:00:57469 static const std::string kGoogleSuggestType("google:suggesttype");
initial.commit09911bf2008-07-26 23:55:29470 if (dict_val->HasKey(kGoogleSuggestType))
471 dict_val->GetList(kGoogleSuggestType, &type_list);
472 }
473 }
474
475 ListValue* result_list = static_cast<ListValue*>(result_val);
476 for (size_t i = 0; i < result_list->GetSize(); ++i) {
477 Value* suggestion_val;
[email protected]dc9a6762010-08-16 07:13:53478 string16 suggestion_str;
initial.commit09911bf2008-07-26 23:55:29479 if (!result_list->Get(i, &suggestion_val) ||
480 !suggestion_val->GetAsString(&suggestion_str))
481 return false;
482
[email protected]8e81f5092010-09-29 23:19:40483 // Google search may return empty suggestions for weird input characters,
484 // they make no sense at all and can cause problem in our code.
485 // See http://crbug.com/56214
486 if (!suggestion_str.length())
487 continue;
488
initial.commit09911bf2008-07-26 23:55:29489 Value* type_val;
[email protected]dc9a6762010-08-16 07:13:53490 std::string type_str;
initial.commit09911bf2008-07-26 23:55:29491 if (type_list && type_list->Get(i, &type_val) &&
[email protected]dc9a6762010-08-16 07:13:53492 type_val->GetAsString(&type_str) && (type_str == "NAVIGATION")) {
initial.commit09911bf2008-07-26 23:55:29493 Value* site_val;
[email protected]dc9a6762010-08-16 07:13:53494 string16 site_name;
[email protected]257ab712009-04-14 17:16:24495 NavigationResults& navigation_results =
496 is_keyword ? keyword_navigation_results_ :
497 default_navigation_results_;
[email protected]0be9b612010-05-18 01:13:41498 if ((navigation_results.size() < kMaxMatches) &&
initial.commit09911bf2008-07-26 23:55:29499 description_list && description_list->Get(i, &site_val) &&
500 site_val->IsType(Value::TYPE_STRING) &&
501 site_val->GetAsString(&site_name)) {
[email protected]16afe222009-01-08 18:57:45502 // We can't blindly trust the URL coming from the server to be valid.
[email protected]dc9a6762010-08-16 07:13:53503 GURL result_url(URLFixerUpper::FixupURL(UTF16ToUTF8(suggestion_str),
[email protected]76e7da22010-06-18 22:44:49504 std::string()));
[email protected]dc9a6762010-08-16 07:13:53505 if (result_url.is_valid()) {
506 navigation_results.push_back(NavigationResult(result_url,
507 UTF16ToWideHack(site_name)));
508 }
initial.commit09911bf2008-07-26 23:55:29509 }
510 } else {
511 // TODO(kochi): Currently we treat a calculator result as a query, but it
512 // is better to have better presentation for caluculator results.
[email protected]0be9b612010-05-18 01:13:41513 if (suggest_results->size() < kMaxMatches)
[email protected]dc9a6762010-08-16 07:13:53514 suggest_results->push_back(UTF16ToWideHack(suggestion_str));
initial.commit09911bf2008-07-26 23:55:29515 }
516 }
517
initial.commit09911bf2008-07-26 23:55:29518 return true;
519}
520
521void SearchProvider::ConvertResultsToAutocompleteMatches() {
522 // Convert all the results to matches and add them to a map, so we can keep
523 // the most relevant match for each result.
524 MatchMap map;
[email protected]257ab712009-04-14 17:16:24525 const Time no_time;
526 int did_not_accept_keyword_suggestion = keyword_suggest_results_.empty() ?
initial.commit09911bf2008-07-26 23:55:29527 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE :
528 TemplateURLRef::NO_SUGGESTION_CHOSEN;
[email protected]257ab712009-04-14 17:16:24529 // Keyword what you typed results are handled by the KeywordProvider.
initial.commit09911bf2008-07-26 23:55:29530
[email protected]257ab712009-04-14 17:16:24531 int did_not_accept_default_suggestion = default_suggest_results_.empty() ?
532 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE :
533 TemplateURLRef::NO_SUGGESTION_CHOSEN;
534 if (providers_.valid_default_provider()) {
[email protected]e918c112010-12-08 23:03:49535 AddMatchToMap(input_.text(), input_.text(),
536 CalculateRelevanceForWhatYouTyped(),
[email protected]257ab712009-04-14 17:16:24537 AutocompleteMatch::SEARCH_WHAT_YOU_TYPED,
[email protected]e918c112010-12-08 23:03:49538 did_not_accept_default_suggestion, false,
539 input_.initial_prevent_inline_autocomplete(), &map);
[email protected]9e789742011-01-10 23:27:32540 if (!default_provider_suggest_text_.empty()) {
541 AddMatchToMap(input_.text() + default_provider_suggest_text_,
542 input_.text(), CalculateRelevanceForWhatYouTyped() + 1,
543 AutocompleteMatch::SEARCH_SUGGEST,
544 did_not_accept_default_suggestion, false,
545 input_.initial_prevent_inline_autocomplete(), &map);
546 }
initial.commit09911bf2008-07-26 23:55:29547 }
548
[email protected]257ab712009-04-14 17:16:24549 AddHistoryResultsToMap(keyword_history_results_, true,
550 did_not_accept_keyword_suggestion, &map);
551 AddHistoryResultsToMap(default_history_results_, false,
552 did_not_accept_default_suggestion, &map);
553
554 AddSuggestResultsToMap(keyword_suggest_results_, true,
555 did_not_accept_keyword_suggestion, &map);
556 AddSuggestResultsToMap(default_suggest_results_, false,
557 did_not_accept_default_suggestion, &map);
initial.commit09911bf2008-07-26 23:55:29558
559 // Now add the most relevant matches from the map to |matches_|.
560 matches_.clear();
561 for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i)
562 matches_.push_back(i->second);
563
[email protected]257ab712009-04-14 17:16:24564 AddNavigationResultsToMatches(keyword_navigation_results_, true);
565 AddNavigationResultsToMatches(default_navigation_results_, false);
initial.commit09911bf2008-07-26 23:55:29566
[email protected]0be9b612010-05-18 01:13:41567 const size_t max_total_matches = kMaxMatches + 1; // 1 for "what you typed"
initial.commit09911bf2008-07-26 23:55:29568 std::partial_sort(matches_.begin(),
569 matches_.begin() + std::min(max_total_matches, matches_.size()),
570 matches_.end(), &AutocompleteMatch::MoreRelevant);
571 if (matches_.size() > max_total_matches)
[email protected]a28e95662008-11-12 19:19:02572 matches_.erase(matches_.begin() + max_total_matches, matches_.end());
initial.commit09911bf2008-07-26 23:55:29573
[email protected]70833262011-01-05 23:40:44574 UpdateFirstSearchMatchDescription();
575
[email protected]cc63dea2008-08-21 20:56:31576 UpdateStarredStateOfMatches();
577
[email protected]4ab4c7c2010-11-24 04:49:34578 UpdateDone();
[email protected]257ab712009-04-14 17:16:24579}
580
581void SearchProvider::AddNavigationResultsToMatches(
582 const NavigationResults& navigation_results,
583 bool is_keyword) {
584 if (!navigation_results.empty()) {
585 // TODO(kochi): http://b/1170574 We add only one results for navigational
586 // suggestions. If we can get more useful information about the score,
587 // consider adding more results.
[email protected]52d08b12009-10-19 18:42:36588 const size_t num_results = is_keyword ?
589 keyword_navigation_results_.size() : default_navigation_results_.size();
590 matches_.push_back(NavigationToMatch(navigation_results.front(),
591 CalculateRelevanceForNavigation(num_results, 0, is_keyword),
592 is_keyword));
[email protected]257ab712009-04-14 17:16:24593 }
594}
595
596void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results,
597 bool is_keyword,
598 int did_not_accept_suggestion,
599 MatchMap* map) {
[email protected]ab49320e2011-01-06 22:45:15600 int last_relevance = 0;
[email protected]257ab712009-04-14 17:16:24601 for (HistoryResults::const_iterator i(results.begin()); i != results.end();
602 ++i) {
[email protected]ab49320e2011-01-06 22:45:15603 // History returns results sorted for us. We force the relevance to decrease
604 // so that the sort from history is honored. We should never end up with a
605 // match having a relevance greater than the previous, but they might be
606 // equal. If we didn't force the relevance to decrease and we ended up in a
607 // situation where the relevance was equal, then which was shown first would
608 // be random.
609 // This uses >= to handle the case where 3 or more results have the same
610 // relevance.
611 int relevance = CalculateRelevanceForHistory(i->time, is_keyword);
612 if (i != results.begin() && relevance >= last_relevance)
613 relevance = last_relevance - 1;
614 last_relevance = relevance;
[email protected]e53668962010-06-23 15:35:25615 AddMatchToMap(UTF16ToWide(i->term),
[email protected]e918c112010-12-08 23:03:49616 is_keyword ? keyword_input_text_ : input_.text(),
[email protected]ab49320e2011-01-06 22:45:15617 relevance,
[email protected]257ab712009-04-14 17:16:24618 AutocompleteMatch::SEARCH_HISTORY, did_not_accept_suggestion,
[email protected]e918c112010-12-08 23:03:49619 is_keyword, input_.initial_prevent_inline_autocomplete(),
620 map);
[email protected]257ab712009-04-14 17:16:24621 }
622}
623
624void SearchProvider::AddSuggestResultsToMap(
625 const SuggestResults& suggest_results,
626 bool is_keyword,
627 int did_not_accept_suggestion,
628 MatchMap* map) {
629 for (size_t i = 0; i < suggest_results.size(); ++i) {
630 AddMatchToMap(suggest_results[i],
[email protected]e918c112010-12-08 23:03:49631 is_keyword ? keyword_input_text_ : input_.text(),
[email protected]52d08b12009-10-19 18:42:36632 CalculateRelevanceForSuggestion(suggest_results.size(), i,
[email protected]257ab712009-04-14 17:16:24633 is_keyword),
634 AutocompleteMatch::SEARCH_SUGGEST,
[email protected]e918c112010-12-08 23:03:49635 static_cast<int>(i), is_keyword,
636 input_.initial_prevent_inline_autocomplete(), map);
[email protected]257ab712009-04-14 17:16:24637 }
initial.commit09911bf2008-07-26 23:55:29638}
639
640int SearchProvider::CalculateRelevanceForWhatYouTyped() const {
[email protected]52d08b12009-10-19 18:42:36641 if (providers_.valid_keyword_provider())
642 return 250;
643
initial.commit09911bf2008-07-26 23:55:29644 switch (input_.type()) {
645 case AutocompleteInput::UNKNOWN:
[email protected]52d08b12009-10-19 18:42:36646 case AutocompleteInput::QUERY:
647 case AutocompleteInput::FORCED_QUERY:
648 return 1300;
initial.commit09911bf2008-07-26 23:55:29649
650 case AutocompleteInput::REQUESTED_URL:
[email protected]52d08b12009-10-19 18:42:36651 return 1150;
initial.commit09911bf2008-07-26 23:55:29652
653 case AutocompleteInput::URL:
[email protected]52d08b12009-10-19 18:42:36654 return 850;
initial.commit09911bf2008-07-26 23:55:29655
656 default:
657 NOTREACHED();
658 return 0;
659 }
660}
661
[email protected]257ab712009-04-14 17:16:24662int SearchProvider::CalculateRelevanceForHistory(const Time& time,
663 bool is_keyword) const {
[email protected]aa613d62010-11-09 20:40:18664 // The relevance of past searches falls off over time. There are two distinct
665 // equations used. If the first equation is used (searches to the primary
666 // provider with a type other than URL) the score starts at 1399 and falls to
667 // 1300. If the second equation is used the relevance of a search 15 minutes
668 // ago is discounted about 50 points, while the relevance of a search two
669 // weeks ago is discounted about 450 points.
670 double elapsed_time = std::max((Time::Now() - time).InSecondsF(), 0.);
671
672 if (providers_.is_primary_provider(is_keyword) &&
[email protected]bdca9beb2010-11-15 22:56:19673 input_.type() != AutocompleteInput::URL &&
674 !input_.prevent_inline_autocomplete()) {
[email protected]aa613d62010-11-09 20:40:18675 // Searches with the past two days get a different curve.
676 const double autocomplete_time= 2 * 24 * 60 * 60;
677 if (elapsed_time < autocomplete_time) {
678 return 1399 - static_cast<int>(99 *
679 std::pow(elapsed_time / autocomplete_time, 2.5));
680 }
681 elapsed_time -= autocomplete_time;
682 }
683
[email protected]c3a4bd992010-08-18 20:25:01684 const int score_discount =
685 static_cast<int>(6.5 * std::pow(elapsed_time, 0.3));
initial.commit09911bf2008-07-26 23:55:29686
[email protected]6c85aa02009-02-27 12:08:09687 // Don't let scores go below 0. Negative relevance scores are meaningful in
688 // a different way.
initial.commit09911bf2008-07-26 23:55:29689 int base_score;
[email protected]52d08b12009-10-19 18:42:36690 if (!providers_.is_primary_provider(is_keyword))
691 base_score = 200;
692 else
693 base_score = (input_.type() == AutocompleteInput::URL) ? 750 : 1050;
initial.commit09911bf2008-07-26 23:55:29694 return std::max(0, base_score - score_discount);
695}
696
[email protected]52d08b12009-10-19 18:42:36697int SearchProvider::CalculateRelevanceForSuggestion(size_t num_results,
698 size_t result_number,
699 bool is_keyword) const {
700 DCHECK(result_number < num_results);
701 int base_score;
702 if (!providers_.is_primary_provider(is_keyword))
703 base_score = 100;
704 else
705 base_score = (input_.type() == AutocompleteInput::URL) ? 300 : 600;
706 return base_score +
707 static_cast<int>(num_results - 1 - result_number);
initial.commit09911bf2008-07-26 23:55:29708}
709
[email protected]52d08b12009-10-19 18:42:36710int SearchProvider::CalculateRelevanceForNavigation(size_t num_results,
711 size_t result_number,
712 bool is_keyword) const {
713 DCHECK(result_number < num_results);
initial.commit09911bf2008-07-26 23:55:29714 // TODO(kochi): http://b/784900 Use relevance score from the NavSuggest
715 // server if possible.
[email protected]52d08b12009-10-19 18:42:36716 return (providers_.is_primary_provider(is_keyword) ? 800 : 150) +
717 static_cast<int>(num_results - 1 - result_number);
initial.commit09911bf2008-07-26 23:55:29718}
719
720void SearchProvider::AddMatchToMap(const std::wstring& query_string,
[email protected]e918c112010-12-08 23:03:49721 const std::wstring& input_text,
initial.commit09911bf2008-07-26 23:55:29722 int relevance,
[email protected]4c1fb7ec2008-11-13 00:19:00723 AutocompleteMatch::Type type,
initial.commit09911bf2008-07-26 23:55:29724 int accepted_suggestion,
[email protected]257ab712009-04-14 17:16:24725 bool is_keyword,
[email protected]e918c112010-12-08 23:03:49726 bool prevent_inline_autocomplete,
initial.commit09911bf2008-07-26 23:55:29727 MatchMap* map) {
[email protected]4c1fb7ec2008-11-13 00:19:00728 AutocompleteMatch match(this, relevance, false, type);
initial.commit09911bf2008-07-26 23:55:29729 std::vector<size_t> content_param_offsets;
[email protected]257ab712009-04-14 17:16:24730 const TemplateURL& provider = is_keyword ? providers_.keyword_provider() :
731 providers_.default_provider();
[email protected]70833262011-01-05 23:40:44732 match.contents.assign(query_string);
[email protected]fb5153c52009-07-31 19:40:33733 // We do intra-string highlighting for suggestions - the suggested segment
734 // will be highlighted, e.g. for input_text = "you" the suggestion may be
735 // "youtube", so we'll bold the "tube" section: you*tube*.
736 if (input_text != query_string) {
[email protected]fb5153c52009-07-31 19:40:33737 size_t input_position = match.contents.find(input_text);
738 if (input_position == std::wstring::npos) {
739 // The input text is not a substring of the query string, e.g. input
740 // text is "slasdot" and the query string is "slashdot", so we bold the
741 // whole thing.
742 match.contents_class.push_back(
743 ACMatchClassification(0, ACMatchClassification::MATCH));
[email protected]ec2379162009-06-09 23:58:17744 } else {
[email protected]fb5153c52009-07-31 19:40:33745 // TODO(beng): ACMatchClassification::MATCH now seems to just mean
746 // "bold" this. Consider modifying the terminology.
747 // We don't iterate over the string here annotating all matches because
748 // it looks odd to have every occurrence of a substring that may be as
749 // short as a single character highlighted in a query suggestion result,
750 // e.g. for input text "s" and query string "southwest airlines", it
751 // looks odd if both the first and last s are highlighted.
752 if (input_position != 0) {
753 match.contents_class.push_back(
754 ACMatchClassification(0, ACMatchClassification::NONE));
755 }
756 match.contents_class.push_back(
757 ACMatchClassification(input_position, ACMatchClassification::DIM));
758 size_t next_fragment_position = input_position + input_text.length();
759 if (next_fragment_position < query_string.length()) {
760 match.contents_class.push_back(
761 ACMatchClassification(next_fragment_position,
762 ACMatchClassification::NONE));
763 }
[email protected]ec2379162009-06-09 23:58:17764 }
initial.commit09911bf2008-07-26 23:55:29765 } else {
[email protected]fb5153c52009-07-31 19:40:33766 // Otherwise, we're dealing with the "default search" result which has no
[email protected]70833262011-01-05 23:40:44767 // completion.
[email protected]fb5153c52009-07-31 19:40:33768 match.contents_class.push_back(
769 ACMatchClassification(0, ACMatchClassification::NONE));
initial.commit09911bf2008-07-26 23:55:29770 }
771
772 // When the user forced a query, we need to make sure all the fill_into_edit
773 // values preserve that property. Otherwise, if the user starts editing a
774 // suggestion, non-Search results will suddenly appear.
775 size_t search_start = 0;
776 if (input_.type() == AutocompleteInput::FORCED_QUERY) {
777 match.fill_into_edit.assign(L"?");
778 ++search_start;
779 }
[email protected]c0048b42009-05-04 21:47:17780 if (is_keyword) {
781 match.fill_into_edit.append(providers_.keyword_provider().keyword() + L" ");
782 match.template_url = &providers_.keyword_provider();
783 }
initial.commit09911bf2008-07-26 23:55:29784 match.fill_into_edit.append(query_string);
[email protected]2c33dd22010-02-11 21:46:35785 // Not all suggestions start with the original input.
[email protected]e918c112010-12-08 23:03:49786 if (!prevent_inline_autocomplete &&
[email protected]257ab712009-04-14 17:16:24787 !match.fill_into_edit.compare(search_start, input_text.length(),
788 input_text))
789 match.inline_autocomplete_offset = search_start + input_text.length();
initial.commit09911bf2008-07-26 23:55:29790
[email protected]257ab712009-04-14 17:16:24791 const TemplateURLRef* const search_url = provider.url();
initial.commit09911bf2008-07-26 23:55:29792 DCHECK(search_url->SupportsReplacement());
[email protected]7b9f3672009-06-15 18:31:22793 match.destination_url =
[email protected]ddd231e2010-06-29 20:35:19794 GURL(search_url->ReplaceSearchTerms(provider,
795 query_string,
796 accepted_suggestion,
797 input_text));
initial.commit09911bf2008-07-26 23:55:29798
799 // Search results don't look like URLs.
[email protected]0bfc29a2009-04-27 16:15:44800 match.transition =
801 is_keyword ? PageTransition::KEYWORD : PageTransition::GENERATED;
initial.commit09911bf2008-07-26 23:55:29802
803 // Try to add |match| to |map|. If a match for |query_string| is already in
804 // |map|, replace it if |match| is more relevant.
805 // NOTE: Keep this ToLower() call in sync with url_database.cc.
806 const std::pair<MatchMap::iterator, bool> i = map->insert(
807 std::pair<std::wstring, AutocompleteMatch>(
[email protected]e5a8c472010-08-04 19:47:20808 UTF16ToWide(l10n_util::ToLower(WideToUTF16(query_string))), match));
initial.commit09911bf2008-07-26 23:55:29809 // NOTE: We purposefully do a direct relevance comparison here instead of
810 // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added
811 // first" rather than "items alphabetically first" when the scores are equal.
812 // The only case this matters is when a user has results with the same score
813 // that differ only by capitalization; because the history system returns
814 // results sorted by recency, this means we'll pick the most recent such
815 // result even if the precision of our relevance score is too low to
816 // distinguish the two.
817 if (!i.second && (match.relevance > i.first->second.relevance))
818 i.first->second = match;
819}
820
821AutocompleteMatch SearchProvider::NavigationToMatch(
822 const NavigationResult& navigation,
[email protected]257ab712009-04-14 17:16:24823 int relevance,
824 bool is_keyword) {
825 const std::wstring& input_text =
826 is_keyword ? keyword_input_text_ : input_.text();
[email protected]4c1fb7ec2008-11-13 00:19:00827 AutocompleteMatch match(this, relevance, false,
828 AutocompleteMatch::NAVSUGGEST);
initial.commit09911bf2008-07-26 23:55:29829 match.destination_url = navigation.url;
[email protected]76e7da22010-06-18 22:44:49830 match.contents =
831 StringForURLDisplay(navigation.url, true, !HasHTTPScheme(input_text));
[email protected]257ab712009-04-14 17:16:24832 AutocompleteMatch::ClassifyMatchInString(input_text, match.contents,
initial.commit09911bf2008-07-26 23:55:29833 ACMatchClassification::URL,
834 &match.contents_class);
835
836 match.description = navigation.site_name;
[email protected]257ab712009-04-14 17:16:24837 AutocompleteMatch::ClassifyMatchInString(input_text, navigation.site_name,
initial.commit09911bf2008-07-26 23:55:29838 ACMatchClassification::NONE,
839 &match.description_class);
840
initial.commit09911bf2008-07-26 23:55:29841 // When the user forced a query, we need to make sure all the fill_into_edit
842 // values preserve that property. Otherwise, if the user starts editing a
843 // suggestion, non-Search results will suddenly appear.
844 if (input_.type() == AutocompleteInput::FORCED_QUERY)
845 match.fill_into_edit.assign(L"?");
[email protected]79845ef2010-06-02 02:37:40846 match.fill_into_edit.append(
847 AutocompleteInput::FormattedStringWithEquivalentMeaning(navigation.url,
848 match.contents));
initial.commit09911bf2008-07-26 23:55:29849 // TODO(pkasting): http://b/1112879 These should perhaps be
850 // inline-autocompletable?
851
852 return match;
853}
[email protected]4ab4c7c2010-11-24 04:49:34854
855void SearchProvider::UpdateDone() {
856 // We're done when there are no more suggest queries pending (this is set to 1
857 // when the timer is started) and we're not waiting on instant.
858 done_ = ((suggest_results_pending_ == 0) &&
859 (instant_finalized_ || !InstantController::IsEnabled(profile_)));
860}
[email protected]70833262011-01-05 23:40:44861
862void SearchProvider::UpdateFirstSearchMatchDescription() {
863 if (!providers_.valid_default_provider() || matches_.empty())
864 return;
865
866 for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i) {
867 AutocompleteMatch& match = *i;
868 switch (match.type) {
869 case AutocompleteMatch::SEARCH_WHAT_YOU_TYPED:
870 case AutocompleteMatch::SEARCH_HISTORY:
871 case AutocompleteMatch::SEARCH_SUGGEST:
872 match.description.assign(
873 UTF16ToWideHack(l10n_util::GetStringFUTF16(
874 IDS_AUTOCOMPLETE_SEARCH_DESCRIPTION,
875 WideToUTF16Hack(providers_.default_provider().
876 AdjustedShortNameForLocaleDirection()))));
877 match.description_class.push_back(
878 ACMatchClassification(0, ACMatchClassification::DIM));
879 // Only the first search match gets a description.
880 return;
881
882 default:
883 break;
884 }
885 }
886}