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