1// Copyright (c) 2011 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "base/logging.h"
6#include "chrome/browser/autocomplete/autocomplete_match.h"
7#include "grit/theme_resources.h"
8
9// AutocompleteMatch ----------------------------------------------------------
10
11AutocompleteMatch::AutocompleteMatch()
12    : provider(NULL),
13      relevance(0),
14      deletable(false),
15      inline_autocomplete_offset(string16::npos),
16      transition(PageTransition::GENERATED),
17      is_history_what_you_typed_match(false),
18      type(SEARCH_WHAT_YOU_TYPED),
19      template_url(NULL),
20      starred(false),
21      from_previous(false) {
22}
23
24AutocompleteMatch::AutocompleteMatch(AutocompleteProvider* provider,
25                                     int relevance,
26                                     bool deletable,
27                                     Type type)
28    : provider(provider),
29      relevance(relevance),
30      deletable(deletable),
31      inline_autocomplete_offset(string16::npos),
32      transition(PageTransition::TYPED),
33      is_history_what_you_typed_match(false),
34      type(type),
35      template_url(NULL),
36      starred(false),
37      from_previous(false) {
38}
39
40AutocompleteMatch::~AutocompleteMatch() {
41}
42
43// static
44std::string AutocompleteMatch::TypeToString(Type type) {
45  const char* strings[NUM_TYPES] = {
46    "url-what-you-typed",
47    "history-url",
48    "history-title",
49    "history-body",
50    "history-keyword",
51    "navsuggest",
52    "search-what-you-typed",
53    "search-history",
54    "search-suggest",
55    "search-other-engine",
56    "extension-app",
57  };
58  DCHECK(arraysize(strings) == NUM_TYPES);
59  return strings[type];
60}
61
62// static
63int AutocompleteMatch::TypeToIcon(Type type) {
64  int icons[NUM_TYPES] = {
65    IDR_OMNIBOX_HTTP,
66    IDR_OMNIBOX_HTTP,
67    IDR_OMNIBOX_HISTORY,
68    IDR_OMNIBOX_HISTORY,
69    IDR_OMNIBOX_HISTORY,
70    IDR_OMNIBOX_HTTP,
71    IDR_OMNIBOX_SEARCH,
72    IDR_OMNIBOX_SEARCH,
73    IDR_OMNIBOX_SEARCH,
74    IDR_OMNIBOX_SEARCH,
75    IDR_OMNIBOX_EXTENSION_APP,
76  };
77  DCHECK(arraysize(icons) == NUM_TYPES);
78  return icons[type];
79}
80
81// static
82bool AutocompleteMatch::MoreRelevant(const AutocompleteMatch& elem1,
83                                     const AutocompleteMatch& elem2) {
84  // For equal-relevance matches, we sort alphabetically, so that providers
85  // who return multiple elements at the same priority get a "stable" sort
86  // across multiple updates.
87  if (elem1.relevance == elem2.relevance)
88    return elem1.contents > elem2.contents;
89
90  return elem1.relevance > elem2.relevance;
91}
92
93// static
94bool AutocompleteMatch::DestinationSortFunc(const AutocompleteMatch& elem1,
95                                            const AutocompleteMatch& elem2) {
96  // Sort identical destination_urls together.  Place the most relevant matches
97  // first, so that when we call std::unique(), these are the ones that get
98  // preserved.
99  return (elem1.destination_url != elem2.destination_url) ?
100      (elem1.destination_url < elem2.destination_url) :
101      MoreRelevant(elem1, elem2);
102}
103
104// static
105bool AutocompleteMatch::DestinationsEqual(const AutocompleteMatch& elem1,
106                                          const AutocompleteMatch& elem2) {
107  return elem1.destination_url == elem2.destination_url;
108}
109
110// static
111void AutocompleteMatch::ClassifyMatchInString(
112    const string16& find_text,
113    const string16& text,
114    int style,
115    ACMatchClassifications* classification) {
116  ClassifyLocationInString(text.find(find_text), find_text.length(),
117                           text.length(), style, classification);
118}
119
120void AutocompleteMatch::ClassifyLocationInString(
121    size_t match_location,
122    size_t match_length,
123    size_t overall_length,
124    int style,
125    ACMatchClassifications* classification) {
126  classification->clear();
127
128  // Don't classify anything about an empty string
129  // (AutocompleteMatch::Validate() checks this).
130  if (overall_length == 0)
131    return;
132
133  // Mark pre-match portion of string (if any).
134  if (match_location != 0) {
135    classification->push_back(ACMatchClassification(0, style));
136  }
137
138  // Mark matching portion of string.
139  if (match_location == string16::npos) {
140    // No match, above classification will suffice for whole string.
141    return;
142  }
143  // Classifying an empty match makes no sense and will lead to validation
144  // errors later.
145  DCHECK(match_length > 0);
146  classification->push_back(ACMatchClassification(match_location,
147      (style | ACMatchClassification::MATCH) & ~ACMatchClassification::DIM));
148
149  // Mark post-match portion of string (if any).
150  const size_t after_match(match_location + match_length);
151  if (after_match < overall_length) {
152    classification->push_back(ACMatchClassification(after_match, style));
153  }
154}
155
156#ifndef NDEBUG
157void AutocompleteMatch::Validate() const {
158  ValidateClassifications(contents, contents_class);
159  ValidateClassifications(description, description_class);
160}
161
162void AutocompleteMatch::ValidateClassifications(
163    const string16& text,
164    const ACMatchClassifications& classifications) const {
165  if (text.empty()) {
166    DCHECK(classifications.size() == 0);
167    return;
168  }
169
170  // The classifications should always cover the whole string.
171  DCHECK(!classifications.empty()) << "No classification for text";
172  DCHECK(classifications[0].offset == 0) << "Classification misses beginning";
173  if (classifications.size() == 1)
174    return;
175
176  // The classifications should always be sorted.
177  size_t last_offset = classifications[0].offset;
178  for (ACMatchClassifications::const_iterator i(classifications.begin() + 1);
179       i != classifications.end(); ++i) {
180    DCHECK(i->offset > last_offset) << "Classification unsorted";
181    DCHECK(i->offset < text.length()) << "Classification out of bounds";
182    last_offset = i->offset;
183  }
184}
185#endif
186