autocomplete.cc revision 201ade2fbba22bfb27ae029f4d23fca6ded109a0
1// Copyright (c) 2010 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/autocomplete/autocomplete.h"
6
7#include <algorithm>
8
9#include "app/l10n_util.h"
10#include "base/basictypes.h"
11#include "base/command_line.h"
12#include "base/i18n/number_formatting.h"
13#include "base/string_number_conversions.h"
14#include "base/string_util.h"
15#include "base/utf_string_conversions.h"
16#include "chrome/browser/autocomplete/autocomplete_match.h"
17#include "chrome/browser/autocomplete/history_quick_provider.h"
18#include "chrome/browser/autocomplete/history_url_provider.h"
19#include "chrome/browser/autocomplete/history_contents_provider.h"
20#include "chrome/browser/autocomplete/keyword_provider.h"
21#include "chrome/browser/autocomplete/search_provider.h"
22#include "chrome/browser/bookmarks/bookmark_model.h"
23#include "chrome/browser/dom_ui/history_ui.h"
24#include "chrome/browser/external_protocol_handler.h"
25#include "chrome/browser/net/url_fixer_upper.h"
26#include "chrome/browser/prefs/pref_service.h"
27#include "chrome/browser/profile.h"
28#include "chrome/common/chrome_switches.h"
29#include "chrome/common/notification_service.h"
30#include "chrome/common/pref_names.h"
31#include "chrome/common/url_constants.h"
32#include "googleurl/src/gurl.h"
33#include "googleurl/src/url_canon_ip.h"
34#include "googleurl/src/url_util.h"
35#include "grit/generated_resources.h"
36#include "grit/theme_resources.h"
37#include "net/base/net_util.h"
38#include "net/base/registry_controlled_domain.h"
39#include "net/url_request/url_request.h"
40
41using base::TimeDelta;
42
43// AutocompleteInput ----------------------------------------------------------
44
45AutocompleteInput::AutocompleteInput()
46  : type_(INVALID),
47    initial_prevent_inline_autocomplete_(false),
48    prevent_inline_autocomplete_(false),
49    prefer_keyword_(false),
50    allow_exact_keyword_match_(true),
51    synchronous_only_(false) {
52}
53
54AutocompleteInput::AutocompleteInput(const std::wstring& text,
55                                     const std::wstring& desired_tld,
56                                     bool prevent_inline_autocomplete,
57                                     bool prefer_keyword,
58                                     bool allow_exact_keyword_match,
59                                     bool synchronous_only)
60    : desired_tld_(desired_tld),
61      initial_prevent_inline_autocomplete_(prevent_inline_autocomplete),
62      prevent_inline_autocomplete_(prevent_inline_autocomplete),
63      prefer_keyword_(prefer_keyword),
64      allow_exact_keyword_match_(allow_exact_keyword_match),
65      synchronous_only_(synchronous_only) {
66  // Trim whitespace from edges of input; don't inline autocomplete if there
67  // was trailing whitespace.
68  if (TrimWhitespace(text, TRIM_ALL, &text_) & TRIM_TRAILING)
69    prevent_inline_autocomplete_ = true;
70
71  type_ = Parse(text_, desired_tld, &parts_, &scheme_);
72
73  if (type_ == INVALID)
74    return;
75
76  if ((type_ == UNKNOWN) || (type_ == REQUESTED_URL) || (type_ == URL)) {
77    GURL canonicalized_url(URLFixerUpper::FixupURL(WideToUTF8(text_),
78                                                   WideToUTF8(desired_tld_)));
79    if (canonicalized_url.is_valid() &&
80        (!canonicalized_url.IsStandard() || canonicalized_url.SchemeIsFile() ||
81         !canonicalized_url.host().empty()))
82      canonicalized_url_ = canonicalized_url;
83  }
84
85  RemoveForcedQueryStringIfNecessary(type_, &text_);
86}
87
88AutocompleteInput::~AutocompleteInput() {
89}
90
91// static
92void AutocompleteInput::RemoveForcedQueryStringIfNecessary(Type type,
93                                                           std::wstring* text) {
94  if (type == FORCED_QUERY && !text->empty() && (*text)[0] == L'?')
95    text->erase(0, 1);
96}
97
98// static
99std::string AutocompleteInput::TypeToString(Type type) {
100  switch (type) {
101    case INVALID:       return "invalid";
102    case UNKNOWN:       return "unknown";
103    case REQUESTED_URL: return "requested-url";
104    case URL:           return "url";
105    case QUERY:         return "query";
106    case FORCED_QUERY:  return "forced-query";
107
108    default:
109      NOTREACHED();
110      return std::string();
111  }
112}
113
114// static
115AutocompleteInput::Type AutocompleteInput::Parse(
116    const std::wstring& text,
117    const std::wstring& desired_tld,
118    url_parse::Parsed* parts,
119    std::wstring* scheme) {
120  const size_t first_non_white = text.find_first_not_of(kWhitespaceWide, 0);
121  if (first_non_white == std::wstring::npos)
122    return INVALID;  // All whitespace.
123
124  if (text.at(first_non_white) == L'?') {
125    // If the first non-whitespace character is a '?', we magically treat this
126    // as a query.
127    return FORCED_QUERY;
128  }
129
130  // Ask our parsing back-end to help us understand what the user typed.  We
131  // use the URLFixerUpper here because we want to be smart about what we
132  // consider a scheme.  For example, we shouldn't consider www.google.com:80
133  // to have a scheme.
134  url_parse::Parsed local_parts;
135  if (!parts)
136    parts = &local_parts;
137  const std::wstring parsed_scheme(URLFixerUpper::SegmentURL(text, parts));
138  if (scheme)
139    *scheme = parsed_scheme;
140
141  if (parsed_scheme == L"file") {
142    // A user might or might not type a scheme when entering a file URL.  In
143    // either case, |parsed_scheme| will tell us that this is a file URL, but
144    // |parts->scheme| might be empty, e.g. if the user typed "C:\foo".
145    return URL;
146  }
147
148  // If the user typed a scheme, and it's HTTP or HTTPS, we know how to parse it
149  // well enough that we can fall through to the heuristics below.  If it's
150  // something else, we can just determine our action based on what we do with
151  // any input of this scheme.  In theory we could do better with some schemes
152  // (e.g. "ftp" or "view-source") but I'll wait to spend the effort on that
153  // until I run into some cases that really need it.
154  if (parts->scheme.is_nonempty() &&
155      (parsed_scheme != L"http") && (parsed_scheme != L"https")) {
156    // See if we know how to handle the URL internally.
157    if (URLRequest::IsHandledProtocol(WideToASCII(parsed_scheme)))
158      return URL;
159
160    // There are also some schemes that we convert to other things before they
161    // reach the renderer or else the renderer handles internally without
162    // reaching the URLRequest logic.  We thus won't catch these above, but we
163    // should still claim to handle them.
164    if (LowerCaseEqualsASCII(parsed_scheme, chrome::kViewSourceScheme) ||
165        LowerCaseEqualsASCII(parsed_scheme, chrome::kJavaScriptScheme) ||
166        LowerCaseEqualsASCII(parsed_scheme, chrome::kDataScheme))
167      return URL;
168
169    // Finally, check and see if the user has explicitly opened this scheme as
170    // a URL before.  We need to do this last because some schemes may be in
171    // here as "blocked" (e.g. "javascript") because we don't want pages to open
172    // them, but users still can.
173    // TODO(viettrungluu): get rid of conversion.
174    switch (ExternalProtocolHandler::GetBlockState(WideToUTF8(parsed_scheme))) {
175      case ExternalProtocolHandler::DONT_BLOCK:
176        return URL;
177
178      case ExternalProtocolHandler::BLOCK:
179        // If we don't want the user to open the URL, don't let it be navigated
180        // to at all.
181        return QUERY;
182
183      default:
184        // We don't know about this scheme.  It's likely to be a search operator
185        // like "site:" or "link:".  We classify it as UNKNOWN so the user has
186        // the option of treating it as a URL if we're wrong.
187        // Note that SegmentURL() is smart so we aren't tricked by "c:\foo" or
188        // "www.example.com:81" in this case.
189        return UNKNOWN;
190    }
191  }
192
193  // Either the user didn't type a scheme, in which case we need to distinguish
194  // between an HTTP URL and a query, or the scheme is HTTP or HTTPS, in which
195  // case we should reject invalid formulations.
196
197  // If we have an empty host it can't be a URL.
198  if (!parts->host.is_nonempty())
199    return QUERY;
200
201  // Likewise, the RCDS can reject certain obviously-invalid hosts.  (We also
202  // use the registry length later below.)
203  const std::wstring host(text.substr(parts->host.begin, parts->host.len));
204  const size_t registry_length =
205      net::RegistryControlledDomainService::GetRegistryLength(host, false);
206  if (registry_length == std::wstring::npos) {
207    // Try to append the desired_tld.
208    if (!desired_tld.empty()) {
209      std::wstring host_with_tld(host);
210      if (host[host.length() - 1] != '.')
211        host_with_tld += '.';
212      host_with_tld += desired_tld;
213      if (net::RegistryControlledDomainService::GetRegistryLength(
214          host_with_tld, false) != std::wstring::npos)
215        return REQUESTED_URL;  // Something like "99999999999" that looks like a
216                               // bad IP address, but becomes valid on attaching
217                               // a TLD.
218    }
219    return QUERY;  // Could be a broken IP address, etc.
220  }
221
222
223  // See if the hostname is valid.  While IE and GURL allow hostnames to contain
224  // many other characters (perhaps for weird intranet machines), it's extremely
225  // unlikely that a user would be trying to type those in for anything other
226  // than a search query.
227  url_canon::CanonHostInfo host_info;
228  const std::string canonicalized_host(net::CanonicalizeHost(host, &host_info));
229  if ((host_info.family == url_canon::CanonHostInfo::NEUTRAL) &&
230      !net::IsCanonicalizedHostCompliant(canonicalized_host,
231                                         WideToUTF8(desired_tld))) {
232    // Invalid hostname.  There are several possible cases:
233    // * Our checker is too strict and the user pasted in a real-world URL
234    //   that's "invalid" but resolves.  To catch these, we return UNKNOWN when
235    //   the user explicitly typed a scheme, so we'll still search by default
236    //   but we'll show the accidental search infobar if necessary.
237    // * The user is typing a multi-word query.  If we see a space anywhere in
238    //   the hostname we assume this is a search and return QUERY.
239    // * Our checker is too strict and the user is typing a real-world hostname
240    //   that's "invalid" but resolves.  We return UNKNOWN if the TLD is known.
241    //   Note that we explicitly excluded hosts with spaces above so that
242    //   "toys at amazon.com" will be treated as a search.
243    // * The user is typing some garbage string.  Return QUERY.
244    //
245    // Thus we fall down in the following cases:
246    // * Trying to navigate to a hostname with spaces
247    // * Trying to navigate to a hostname with invalid characters and an unknown
248    //   TLD
249    // These are rare, though probably possible in intranets.
250    return (parts->scheme.is_nonempty() ||
251           ((registry_length != 0) && (host.find(' ') == std::wstring::npos))) ?
252        UNKNOWN : QUERY;
253  }
254
255  // A port number is a good indicator that this is a URL.  However, it might
256  // also be a query like "1.66:1" that looks kind of like an IP address and
257  // port number. So here we only check for "port numbers" that are illegal and
258  // thus mean this can't be navigated to (e.g. "1.2.3.4:garbage"), and we save
259  // handling legal port numbers until after the "IP address" determination
260  // below.
261  if (parts->port.is_nonempty()) {
262    int port;
263    if (!base::StringToInt(WideToUTF8(
264            text.substr(parts->port.begin, parts->port.len)), &port) ||
265        (port < 0) || (port > 65535))
266      return QUERY;
267  }
268
269  // Now that we've ruled out all schemes other than http or https and done a
270  // little more sanity checking, the presence of a scheme means this is likely
271  // a URL.
272  if (parts->scheme.is_nonempty())
273    return URL;
274
275  // See if the host is an IP address.
276  if (host_info.family == url_canon::CanonHostInfo::IPV4) {
277    // If the user originally typed a host that looks like an IP address (a
278    // dotted quad), they probably want to open it.  If the original input was
279    // something else (like a single number), they probably wanted to search for
280    // it, unless they explicitly typed a scheme.  This is true even if the URL
281    // appears to have a path: "1.2/45" is more likely a search (for the answer
282    // to a math problem) than a URL.
283    if (host_info.num_ipv4_components == 4)
284      return URL;
285    return desired_tld.empty() ? UNKNOWN : REQUESTED_URL;
286  }
287  if (host_info.family == url_canon::CanonHostInfo::IPV6)
288    return URL;
289
290  // Now that we've ruled out invalid ports and queries that look like they have
291  // a port, the presence of a port means this is likely a URL.
292  if (parts->port.is_nonempty())
293    return URL;
294
295  // Presence of a password means this is likely a URL.  Note that unless the
296  // user has typed an explicit "http://" or similar, we'll probably think that
297  // the username is some unknown scheme, and bail out in the scheme-handling
298  // code above.
299  if (parts->password.is_nonempty())
300    return URL;
301
302  // The host doesn't look like a number, so see if the user's given us a path.
303  if (parts->path.is_nonempty()) {
304    // Most inputs with paths are URLs, even ones without known registries (e.g.
305    // intranet URLs).  However, if there's no known registry and the path has
306    // a space, this is more likely a query with a slash in the first term
307    // (e.g. "ps/2 games") than a URL.  We can still open URLs with spaces in
308    // the path by escaping the space, and we will still inline autocomplete
309    // them if users have typed them in the past, but we default to searching
310    // since that's the common case.
311    return ((registry_length == 0) &&
312            (text.substr(parts->path.begin, parts->path.len).find(' ') !=
313                std::wstring::npos)) ? UNKNOWN : URL;
314  }
315
316  // If we reach here with a username, our input looks like "user@host".
317  // Because there is no scheme explicitly specified, we think this is more
318  // likely an email address than an HTTP auth attempt.  Hence, we search by
319  // default and let users correct us on a case-by-case basis.
320  if (parts->username.is_nonempty())
321    return UNKNOWN;
322
323  // We have a bare host string.  If it has a known TLD, it's probably a URL.
324  if (registry_length != 0)
325    return URL;
326
327  // No TLD that we know about.  This could be:
328  // * A string that the user wishes to add a desired_tld to to get a URL.  If
329  //   we reach this point, we know there's no known TLD on the string, so the
330  //   fixup code will be willing to add one; thus this is a URL.
331  // * A single word "foo"; possibly an intranet site, but more likely a search.
332  //   This is ideally an UNKNOWN, and we can let the Alternate Nav URL code
333  //   catch our mistakes.
334  // * A URL with a valid TLD we don't know about yet.  If e.g. a registrar adds
335  //   "xxx" as a TLD, then until we add it to our data file, Chrome won't know
336  //   "foo.xxx" is a real URL.  So ideally this is a URL, but we can't really
337  //   distinguish this case from:
338  // * A "URL-like" string that's not really a URL (like
339  //   "browser.tabs.closeButtons" or "java.awt.event.*").  This is ideally a
340  //   QUERY.  Since the above case and this one are indistinguishable, and this
341  //   case is likely to be much more common, just say these are both UNKNOWN,
342  //   which should default to the right thing and let users correct us on a
343  //   case-by-case basis.
344  return desired_tld.empty() ? UNKNOWN : REQUESTED_URL;
345}
346
347// static
348void AutocompleteInput::ParseForEmphasizeComponents(
349    const std::wstring& text,
350    const std::wstring& desired_tld,
351    url_parse::Component* scheme,
352    url_parse::Component* host) {
353  url_parse::Parsed parts;
354  std::wstring scheme_str;
355  Parse(text, desired_tld, &parts, &scheme_str);
356
357  *scheme = parts.scheme;
358  *host = parts.host;
359
360  int after_scheme_and_colon = parts.scheme.end() + 1;
361  // For the view-source scheme, we should emphasize the scheme and host of the
362  // URL qualified by the view-source prefix.
363  if (LowerCaseEqualsASCII(scheme_str, chrome::kViewSourceScheme) &&
364      (static_cast<int>(text.length()) > after_scheme_and_colon)) {
365    // Obtain the URL prefixed by view-source and parse it.
366    std::wstring real_url(text.substr(after_scheme_and_colon));
367    url_parse::Parsed real_parts;
368    AutocompleteInput::Parse(real_url, desired_tld, &real_parts, NULL);
369    if (real_parts.scheme.is_nonempty() || real_parts.host.is_nonempty()) {
370      if (real_parts.scheme.is_nonempty()) {
371        *scheme = url_parse::Component(
372            after_scheme_and_colon + real_parts.scheme.begin,
373            real_parts.scheme.len);
374      } else {
375        scheme->reset();
376      }
377      if (real_parts.host.is_nonempty()) {
378        *host = url_parse::Component(
379            after_scheme_and_colon + real_parts.host.begin,
380            real_parts.host.len);
381      } else {
382        host->reset();
383      }
384    }
385  }
386}
387
388// static
389std::wstring AutocompleteInput::FormattedStringWithEquivalentMeaning(
390    const GURL& url,
391    const std::wstring& formatted_url) {
392  if (!net::CanStripTrailingSlash(url))
393    return formatted_url;
394  const std::wstring url_with_path(formatted_url + L"/");
395  return (AutocompleteInput::Parse(formatted_url, std::wstring(), NULL, NULL) ==
396          AutocompleteInput::Parse(url_with_path, std::wstring(), NULL, NULL)) ?
397      formatted_url : url_with_path;
398}
399
400
401bool AutocompleteInput::Equals(const AutocompleteInput& other) const {
402  return (text_ == other.text_) &&
403         (type_ == other.type_) &&
404         (desired_tld_ == other.desired_tld_) &&
405         (scheme_ == other.scheme_) &&
406         (prevent_inline_autocomplete_ == other.prevent_inline_autocomplete_) &&
407         (prefer_keyword_ == other.prefer_keyword_) &&
408         (synchronous_only_ == other.synchronous_only_);
409}
410
411void AutocompleteInput::Clear() {
412  text_.clear();
413  type_ = INVALID;
414  parts_ = url_parse::Parsed();
415  scheme_.clear();
416  desired_tld_.clear();
417  prevent_inline_autocomplete_ = false;
418  prefer_keyword_ = false;
419}
420
421// AutocompleteProvider -------------------------------------------------------
422
423// static
424const size_t AutocompleteProvider::kMaxMatches = 3;
425
426AutocompleteProvider::ACProviderListener::~ACProviderListener() {
427}
428
429AutocompleteProvider::AutocompleteProvider(ACProviderListener* listener,
430                                           Profile* profile,
431                                           const char* name)
432    : profile_(profile),
433      listener_(listener),
434      done_(true),
435      name_(name) {
436}
437
438void AutocompleteProvider::SetProfile(Profile* profile) {
439  DCHECK(profile);
440  DCHECK(done_);  // The controller should have already stopped us.
441  profile_ = profile;
442}
443
444void AutocompleteProvider::Stop() {
445  done_ = true;
446}
447
448void AutocompleteProvider::DeleteMatch(const AutocompleteMatch& match) {
449}
450
451AutocompleteProvider::~AutocompleteProvider() {
452  Stop();
453}
454
455// static
456bool AutocompleteProvider::HasHTTPScheme(const std::wstring& input) {
457  std::string utf8_input(WideToUTF8(input));
458  url_parse::Component scheme;
459  if (url_util::FindAndCompareScheme(utf8_input, chrome::kViewSourceScheme,
460                                     &scheme))
461    utf8_input.erase(0, scheme.end() + 1);
462  return url_util::FindAndCompareScheme(utf8_input, chrome::kHttpScheme, NULL);
463}
464
465void AutocompleteProvider::UpdateStarredStateOfMatches() {
466  if (matches_.empty())
467    return;
468
469  if (!profile_)
470    return;
471  BookmarkModel* bookmark_model = profile_->GetBookmarkModel();
472  if (!bookmark_model || !bookmark_model->IsLoaded())
473    return;
474
475  for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i)
476    i->starred = bookmark_model->IsBookmarked(GURL(i->destination_url));
477}
478
479std::wstring AutocompleteProvider::StringForURLDisplay(const GURL& url,
480                                                       bool check_accept_lang,
481                                                       bool trim_http) const {
482  std::string languages = (check_accept_lang && profile_) ?
483      profile_->GetPrefs()->GetString(prefs::kAcceptLanguages) : std::string();
484  return UTF16ToWideHack(net::FormatUrl(
485      url,
486      languages,
487      net::kFormatUrlOmitAll & ~(trim_http ? 0 : net::kFormatUrlOmitHTTP),
488      UnescapeRule::SPACES, NULL, NULL, NULL));
489}
490
491// AutocompleteResult ---------------------------------------------------------
492
493// static
494const size_t AutocompleteResult::kMaxMatches = 6;
495
496void AutocompleteResult::Selection::Clear() {
497  destination_url = GURL();
498  provider_affinity = NULL;
499  is_history_what_you_typed_match = false;
500}
501
502AutocompleteResult::AutocompleteResult() {
503  // Reserve space for the max number of matches we'll show. The +1 accounts
504  // for the history shortcut match as it isn't included in max_matches.
505  matches_.reserve(kMaxMatches + 1);
506
507  // It's probably safe to do this in the initializer list, but there's little
508  // penalty to doing it here and it ensures our object is fully constructed
509  // before calling member functions.
510  default_match_ = end();
511}
512
513AutocompleteResult::~AutocompleteResult() {}
514
515void AutocompleteResult::CopyFrom(const AutocompleteResult& rhs) {
516  if (this == &rhs)
517    return;
518
519  matches_ = rhs.matches_;
520  // Careful!  You can't just copy iterators from another container, you have to
521  // reconstruct them.
522  default_match_ = (rhs.default_match_ == rhs.end()) ?
523      end() : (begin() + (rhs.default_match_ - rhs.begin()));
524
525  alternate_nav_url_ = rhs.alternate_nav_url_;
526}
527
528void AutocompleteResult::AppendMatches(const ACMatches& matches) {
529  std::copy(matches.begin(), matches.end(), std::back_inserter(matches_));
530  default_match_ = end();
531  alternate_nav_url_ = GURL();
532}
533
534void AutocompleteResult::AddMatch(const AutocompleteMatch& match) {
535  DCHECK(default_match_ != end());
536  ACMatches::iterator insertion_point =
537      std::upper_bound(begin(), end(), match, &AutocompleteMatch::MoreRelevant);
538  ACMatches::iterator::difference_type default_offset =
539      default_match_ - begin();
540  if ((insertion_point - begin()) <= default_offset)
541    ++default_offset;
542  matches_.insert(insertion_point, match);
543  default_match_ = begin() + default_offset;
544}
545
546void AutocompleteResult::SortAndCull(const AutocompleteInput& input) {
547  // Remove duplicates.
548  std::sort(matches_.begin(), matches_.end(),
549            &AutocompleteMatch::DestinationSortFunc);
550  matches_.erase(std::unique(matches_.begin(), matches_.end(),
551                             &AutocompleteMatch::DestinationsEqual),
552                 matches_.end());
553
554  // Find the top |kMaxMatches| matches.
555  if (matches_.size() > kMaxMatches) {
556    std::partial_sort(matches_.begin(), matches_.begin() + kMaxMatches,
557                      matches_.end(), &AutocompleteMatch::MoreRelevant);
558    matches_.erase(matches_.begin() + kMaxMatches, matches_.end());
559  }
560
561  // HistoryContentsProvider uses a negative relevance as a way to avoid
562  // starving out other provider matches, yet we may end up using this match. To
563  // make sure such matches are sorted correctly we search for all
564  // relevances < 0 and negate them. If we change our relevance algorithm to
565  // properly mix different providers' matches, this can go away.
566  for (ACMatches::iterator i = matches_.begin(); i != matches_.end(); ++i) {
567    if (i->relevance < 0)
568      i->relevance = -i->relevance;
569  }
570
571  // Put the final result set in order.
572  std::sort(matches_.begin(), matches_.end(), &AutocompleteMatch::MoreRelevant);
573  default_match_ = begin();
574
575  // Set the alternate nav URL.
576  alternate_nav_url_ = GURL();
577  if (((input.type() == AutocompleteInput::UNKNOWN) ||
578       (input.type() == AutocompleteInput::REQUESTED_URL)) &&
579      (default_match_ != end()) &&
580      (default_match_->transition != PageTransition::TYPED) &&
581      (default_match_->transition != PageTransition::KEYWORD) &&
582      (input.canonicalized_url() != default_match_->destination_url))
583    alternate_nav_url_ = input.canonicalized_url();
584}
585
586size_t AutocompleteResult::size() const {
587  return matches_.size();
588}
589
590bool AutocompleteResult::empty() const {
591  return matches_.empty();
592}
593
594AutocompleteResult::const_iterator AutocompleteResult::begin() const {
595  return matches_.begin();
596}
597
598AutocompleteResult::iterator AutocompleteResult::begin() {
599  return matches_.begin();
600}
601
602AutocompleteResult::const_iterator AutocompleteResult::end() const {
603  return matches_.end();
604}
605
606AutocompleteResult::iterator AutocompleteResult::end() {
607  return matches_.end();
608}
609
610// Returns the match at the given index.
611const AutocompleteMatch& AutocompleteResult::match_at(size_t index) const {
612  DCHECK(index < matches_.size());
613  return matches_[index];
614}
615
616void AutocompleteResult::Reset() {
617  matches_.clear();
618  default_match_ = end();
619}
620
621#ifndef NDEBUG
622void AutocompleteResult::Validate() const {
623  for (const_iterator i(begin()); i != end(); ++i)
624    i->Validate();
625}
626#endif
627
628// AutocompleteController -----------------------------------------------------
629
630const int AutocompleteController::kNoItemSelected = -1;
631
632namespace {
633// The time we'll wait between sending updates to our observers (balances
634// flicker against lag).
635const int kUpdateDelayMs = 350;
636};
637
638AutocompleteController::AutocompleteController(Profile* profile)
639    : updated_latest_result_(false),
640      delay_interval_has_passed_(false),
641      have_committed_during_this_query_(false),
642      done_(true) {
643  search_provider_ = new SearchProvider(this, profile);
644  providers_.push_back(search_provider_);
645  if (!CommandLine::ForCurrentProcess()->HasSwitch(
646      switches::kDisableHistoryQuickProvider))
647    providers_.push_back(new HistoryQuickProvider(this, profile));
648  if (!CommandLine::ForCurrentProcess()->HasSwitch(
649      switches::kDisableHistoryURLProvider))
650    providers_.push_back(new HistoryURLProvider(this, profile));
651  providers_.push_back(new KeywordProvider(this, profile));
652  history_contents_provider_ = new HistoryContentsProvider(this, profile);
653  providers_.push_back(history_contents_provider_);
654  for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i)
655    (*i)->AddRef();
656}
657
658AutocompleteController::~AutocompleteController() {
659  // The providers may have tasks outstanding that hold refs to them.  We need
660  // to ensure they won't call us back if they outlive us.  (Practically,
661  // calling Stop() should also cancel those tasks and make it so that we hold
662  // the only refs.)  We also don't want to bother notifying anyone of our
663  // result changes here, because the notification observer is in the midst of
664  // shutdown too, so we don't ask Stop() to clear |result_| (and notify).
665  result_.Reset();  // Not really necessary.
666  Stop(false);
667
668  for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i)
669    (*i)->Release();
670
671  providers_.clear();  // Not really necessary.
672}
673
674void AutocompleteController::SetProfile(Profile* profile) {
675  Stop(true);
676  for (ACProviders::iterator i(providers_.begin()); i != providers_.end(); ++i)
677    (*i)->SetProfile(profile);
678  input_.Clear();  // Ensure we don't try to do a "minimal_changes" query on a
679                   // different profile.
680}
681
682void AutocompleteController::Start(const std::wstring& text,
683                                   const std::wstring& desired_tld,
684                                   bool prevent_inline_autocomplete,
685                                   bool prefer_keyword,
686                                   bool allow_exact_keyword_match,
687                                   bool synchronous_only) {
688  const std::wstring old_input_text(input_.text());
689  const bool old_synchronous_only = input_.synchronous_only();
690  input_ = AutocompleteInput(text, desired_tld, prevent_inline_autocomplete,
691      prefer_keyword, allow_exact_keyword_match, synchronous_only);
692
693  // See if we can avoid rerunning autocomplete when the query hasn't changed
694  // much.  When the user presses or releases the ctrl key, the desired_tld
695  // changes, and when the user finishes an IME composition, inline autocomplete
696  // may no longer be prevented.  In both these cases the text itself hasn't
697  // changed since the last query, and some providers can do much less work (and
698  // get matches back more quickly).  Taking advantage of this reduces flicker.
699  //
700  // NOTE: This comes after constructing |input_| above since that construction
701  // can change the text string (e.g. by stripping off a leading '?').
702  const bool minimal_changes = (input_.text() == old_input_text) &&
703      (input_.synchronous_only() == old_synchronous_only);
704
705  // If we're interrupting an old query, and committing its result won't shrink
706  // the visible set (which would probably re-expand soon, thus looking very
707  // flickery), then go ahead and commit what we've got, in order to feel more
708  // responsive when the user is typing rapidly.  In this case it's important
709  // that we don't update the edit, as the user has already changed its contents
710  // and anything we might do with it (e.g. inline autocomplete) likely no
711  // longer applies.
712  if (!minimal_changes && !done_ && (latest_result_.size() >= result_.size()))
713    CommitResult(false);
714
715  // If the timer is already running, it could fire shortly after starting this
716  // query, when we're likely to only have the synchronous results back, thus
717  // almost certainly causing flicker.  Reset it, except when we haven't
718  // committed anything for the past query, in which case the user is typing
719  // quickly and we need to keep running the timer lest we lag too far behind.
720  if (have_committed_during_this_query_) {
721    update_delay_timer_.Stop();
722    delay_interval_has_passed_ = false;
723  }
724
725  // Start the new query.
726  have_committed_during_this_query_ = false;
727  for (ACProviders::iterator i(providers_.begin()); i != providers_.end();
728       ++i) {
729    (*i)->Start(input_, minimal_changes);
730    if (synchronous_only)
731      DCHECK((*i)->done());
732  }
733  CheckIfDone();
734  UpdateLatestResult(true);
735}
736
737void AutocompleteController::Stop(bool clear_result) {
738  for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end();
739       ++i) {
740    (*i)->Stop();
741  }
742
743  update_delay_timer_.Stop();
744  updated_latest_result_ = false;
745  delay_interval_has_passed_ = false;
746  done_ = true;
747  if (clear_result && !result_.empty()) {
748    result_.Reset();
749    NotificationService::current()->Notify(
750        NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED,
751        Source<AutocompleteController>(this),
752        Details<const AutocompleteResult>(&result_));
753    // NOTE: We don't notify AUTOCOMPLETE_CONTROLLER_DEFAULT_MATCH_UPDATED since
754    // we're trying to only clear the popup, not touch the edit... this is all
755    // a mess and should be cleaned up :(
756  }
757  latest_result_.CopyFrom(result_);
758}
759
760void AutocompleteController::DeleteMatch(const AutocompleteMatch& match) {
761  DCHECK(match.deletable);
762  match.provider->DeleteMatch(match);  // This may synchronously call back to
763                                       // OnProviderUpdate().
764  CommitResult(true);  // Ensure any new result gets committed immediately.  If
765                       // it was committed already or hasn't been modified, this
766                       // is harmless.
767}
768
769void AutocompleteController::CommitIfQueryHasNeverBeenCommitted() {
770  if (!have_committed_during_this_query_)
771    CommitResult(true);
772}
773
774void AutocompleteController::OnProviderUpdate(bool updated_matches) {
775  CheckIfDone();
776  if (updated_matches || done_)
777    UpdateLatestResult(false);
778}
779
780void AutocompleteController::UpdateLatestResult(bool is_synchronous_pass) {
781  // Add all providers' matches.
782  latest_result_.Reset();
783  for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end();
784       ++i)
785    latest_result_.AppendMatches((*i)->matches());
786  updated_latest_result_ = true;
787
788  // Sort the matches and trim to a small number of "best" matches.
789  latest_result_.SortAndCull(input_);
790
791  if (history_contents_provider_)
792    AddHistoryContentsShortcut();
793
794#ifndef NDEBUG
795  latest_result_.Validate();
796#endif
797
798  if (is_synchronous_pass) {
799    if (!update_delay_timer_.IsRunning()) {
800      update_delay_timer_.Start(
801          TimeDelta::FromMilliseconds(kUpdateDelayMs),
802          this, &AutocompleteController::DelayTimerFired);
803    }
804
805    NotificationService::current()->Notify(
806        NotificationType::AUTOCOMPLETE_CONTROLLER_DEFAULT_MATCH_UPDATED,
807        Source<AutocompleteController>(this),
808        Details<const AutocompleteResult>(&latest_result_));
809  }
810
811  // If nothing is visible, commit immediately so that the first character the
812  // user types produces an instant response.  If the query has finished and we
813  // haven't ever committed a result set, commit immediately to minimize lag.
814  // Otherwise, only commit when it's been at least one delay interval since the
815  // last commit, to minimize flicker.
816  if (result_.empty() || (done_ && !have_committed_during_this_query_) ||
817      delay_interval_has_passed_)
818    CommitResult(true);
819}
820
821void AutocompleteController::DelayTimerFired() {
822  delay_interval_has_passed_ = true;
823  CommitResult(true);
824}
825
826void AutocompleteController::CommitResult(bool notify_default_match) {
827  if (done_) {
828    update_delay_timer_.Stop();
829    delay_interval_has_passed_ = false;
830  }
831
832  // Don't send update notifications when nothing's actually changed.
833  if (!updated_latest_result_)
834    return;
835
836  updated_latest_result_ = false;
837  delay_interval_has_passed_ = false;
838  have_committed_during_this_query_ = true;
839  result_.CopyFrom(latest_result_);
840  NotificationService::current()->Notify(
841      NotificationType::AUTOCOMPLETE_CONTROLLER_RESULT_UPDATED,
842      Source<AutocompleteController>(this),
843      Details<const AutocompleteResult>(&result_));
844  if (notify_default_match) {
845    // This notification must be sent after the other so the popup has time to
846    // update its state before the edit calls into it.
847    // TODO(pkasting): Eliminate this ordering requirement.
848    NotificationService::current()->Notify(
849        NotificationType::AUTOCOMPLETE_CONTROLLER_DEFAULT_MATCH_UPDATED,
850        Source<AutocompleteController>(this),
851        Details<const AutocompleteResult>(&result_));
852  }
853  if (!done_)
854    update_delay_timer_.Reset();
855}
856
857ACMatches AutocompleteController::GetMatchesNotInLatestResult(
858    const AutocompleteProvider* provider) const {
859  DCHECK(provider);
860
861  // Determine the set of destination URLs.
862  std::set<GURL> destination_urls;
863  for (AutocompleteResult::const_iterator i(latest_result_.begin());
864       i != latest_result_.end(); ++i)
865    destination_urls.insert(i->destination_url);
866
867  ACMatches matches;
868  const ACMatches& provider_matches = provider->matches();
869  for (ACMatches::const_iterator i = provider_matches.begin();
870       i != provider_matches.end(); ++i) {
871    if (destination_urls.find(i->destination_url) == destination_urls.end())
872      matches.push_back(*i);
873  }
874
875  return matches;
876}
877
878void AutocompleteController::AddHistoryContentsShortcut() {
879  DCHECK(history_contents_provider_);
880  // Only check the history contents provider if the history contents provider
881  // is done and has matches.
882  if (!history_contents_provider_->done() ||
883      !history_contents_provider_->db_match_count()) {
884    return;
885  }
886
887  if ((history_contents_provider_->db_match_count() <=
888          (latest_result_.size() + 1)) ||
889      (history_contents_provider_->db_match_count() == 1)) {
890    // We only want to add a shortcut if we're not already showing the matches.
891    ACMatches matches(GetMatchesNotInLatestResult(history_contents_provider_));
892    if (matches.empty())
893      return;
894    if (matches.size() == 1) {
895      // Only one match not shown, add it. The relevance may be negative,
896      // which means we need to negate it to get the true relevance.
897      AutocompleteMatch& match = matches.front();
898      if (match.relevance < 0)
899        match.relevance = -match.relevance;
900      latest_result_.AddMatch(match);
901      return;
902    } // else, fall through and add item.
903  }
904
905  AutocompleteMatch match(NULL, 0, false, AutocompleteMatch::OPEN_HISTORY_PAGE);
906  match.fill_into_edit = input_.text();
907
908  // Mark up the text such that the user input text is bold.
909  size_t keyword_offset = std::wstring::npos;  // Offset into match.contents.
910  if (history_contents_provider_->db_match_count() ==
911      history_contents_provider_->kMaxMatchCount) {
912    // History contents searcher has maxed out.
913    match.contents = l10n_util::GetStringF(IDS_OMNIBOX_RECENT_HISTORY_MANY,
914                                           input_.text(),
915                                           &keyword_offset);
916  } else {
917    // We can report exact matches when there aren't too many.
918    std::vector<size_t> content_param_offsets;
919    match.contents = l10n_util::GetStringF(
920        IDS_OMNIBOX_RECENT_HISTORY,
921        UTF16ToWide(base::FormatNumber(history_contents_provider_->
922                                           db_match_count())),
923        input_.text(),
924        &content_param_offsets);
925
926    // content_param_offsets is ordered based on supplied params, we expect
927    // that the second one contains the query (first is the number).
928    if (content_param_offsets.size() == 2) {
929      keyword_offset = content_param_offsets[1];
930    } else {
931      // See comments on an identical NOTREACHED() in search_provider.cc.
932      NOTREACHED();
933    }
934  }
935
936  // NOTE: This comparison succeeds when keyword_offset == std::wstring::npos.
937  if (keyword_offset > 0) {
938    match.contents_class.push_back(
939        ACMatchClassification(0, ACMatchClassification::NONE));
940  }
941  match.contents_class.push_back(
942      ACMatchClassification(keyword_offset, ACMatchClassification::MATCH));
943  if (keyword_offset + input_.text().size() < match.contents.size()) {
944    match.contents_class.push_back(
945        ACMatchClassification(keyword_offset + input_.text().size(),
946                              ACMatchClassification::NONE));
947  }
948  match.destination_url =
949      HistoryUI::GetHistoryURLWithSearchText(WideToUTF16(input_.text()));
950  match.transition = PageTransition::AUTO_BOOKMARK;
951  match.provider = history_contents_provider_;
952  latest_result_.AddMatch(match);
953}
954
955void AutocompleteController::CheckIfDone() {
956  for (ACProviders::const_iterator i(providers_.begin()); i != providers_.end();
957       ++i) {
958    if (!(*i)->done()) {
959      done_ = false;
960      return;
961    }
962  }
963  done_ = true;
964}
965