Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(9)

Side by Side Diff: chrome/browser/autocomplete/autocomplete_result.cc

Issue 10699032: autocomplete: Extract AutocompleteResult from autocomplete.*. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: move comments Created 8 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(Empty)
1 // Copyright (c) 2012 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_result.h"
6
7 #include <algorithm>
8 #include <iterator>
9
10 #include "base/logging.h"
11 #include "chrome/browser/autocomplete/autocomplete.h"
12 #include "chrome/browser/autocomplete/autocomplete_match.h"
13
14 // static
15 const size_t AutocompleteResult::kMaxMatches = 6;
16 const int AutocompleteResult::kLowestDefaultScore = 1200;
17
18 void AutocompleteResult::Selection::Clear() {
19 destination_url = GURL();
20 provider_affinity = NULL;
21 is_history_what_you_typed_match = false;
22 }
23
24 AutocompleteResult::AutocompleteResult() {
25 // Reserve space for the max number of matches we'll show.
26 matches_.reserve(kMaxMatches);
27
28 // It's probably safe to do this in the initializer list, but there's little
29 // penalty to doing it here and it ensures our object is fully constructed
30 // before calling member functions.
31 default_match_ = end();
32 }
33
34 AutocompleteResult::~AutocompleteResult() {}
35
36 void AutocompleteResult::CopyFrom(const AutocompleteResult& rhs) {
37 if (this == &rhs)
38 return;
39
40 matches_ = rhs.matches_;
41 // Careful! You can't just copy iterators from another container, you have to
42 // reconstruct them.
43 default_match_ = (rhs.default_match_ == rhs.end()) ?
44 end() : (begin() + (rhs.default_match_ - rhs.begin()));
45
46 alternate_nav_url_ = rhs.alternate_nav_url_;
47 }
48
49 void AutocompleteResult::CopyOldMatches(const AutocompleteInput& input,
50 const AutocompleteResult& old_matches) {
51 if (old_matches.empty())
52 return;
53
54 if (empty()) {
55 // If we've got no matches we can copy everything from the last result.
56 CopyFrom(old_matches);
57 for (ACMatches::iterator i(begin()); i != end(); ++i)
58 i->from_previous = true;
59 return;
60 }
61
62 // In hopes of providing a stable popup we try to keep the number of matches
63 // per provider consistent. Other schemes (such as blindly copying the most
64 // relevant matches) typically result in many successive 'What You Typed'
65 // results filling all the matches, which looks awful.
66 //
67 // Instead of starting with the current matches and then adding old matches
68 // until we hit our overall limit, we copy enough old matches so that each
69 // provider has at least as many as before, and then use SortAndCull() to
70 // clamp globally. This way, old high-relevance matches will starve new
71 // low-relevance matches, under the assumption that the new matches will
72 // ultimately be similar. If the assumption holds, this prevents seeing the
73 // new low-relevance match appear and then quickly get pushed off the bottom;
74 // if it doesn't, then once the providers are done and we expire the old
75 // matches, the new ones will all become visible, so we won't have lost
76 // anything permanently.
77 ProviderToMatches matches_per_provider, old_matches_per_provider;
78 BuildProviderToMatches(&matches_per_provider);
79 old_matches.BuildProviderToMatches(&old_matches_per_provider);
80 for (ProviderToMatches::const_iterator i(old_matches_per_provider.begin());
81 i != old_matches_per_provider.end(); ++i) {
82 MergeMatchesByProvider(i->second, matches_per_provider[i->first]);
83 }
84
85 SortAndCull(input);
86 }
87
88 void AutocompleteResult::AppendMatches(const ACMatches& matches) {
89 #ifndef NDEBUG
90 for (ACMatches::const_iterator i(matches.begin()); i != matches.end(); ++i) {
91 DCHECK_EQ(AutocompleteMatch::SanitizeString(i->contents), i->contents);
92 DCHECK_EQ(AutocompleteMatch::SanitizeString(i->description),
93 i->description);
94 }
95 #endif
96 std::copy(matches.begin(), matches.end(), std::back_inserter(matches_));
97 default_match_ = end();
98 alternate_nav_url_ = GURL();
99 }
100
101 void AutocompleteResult::AddMatch(const AutocompleteMatch& match) {
102 DCHECK(default_match_ != end());
103 DCHECK_EQ(AutocompleteMatch::SanitizeString(match.contents), match.contents);
104 DCHECK_EQ(AutocompleteMatch::SanitizeString(match.description),
105 match.description);
106 ACMatches::iterator insertion_point =
107 std::upper_bound(begin(), end(), match, &AutocompleteMatch::MoreRelevant);
108 matches_difference_type default_offset = default_match_ - begin();
109 if ((insertion_point - begin()) <= default_offset)
110 ++default_offset;
111 matches_.insert(insertion_point, match);
112 default_match_ = begin() + default_offset;
113 }
114
115 void AutocompleteResult::SortAndCull(const AutocompleteInput& input) {
116 for (ACMatches::iterator i(matches_.begin()); i != matches_.end(); ++i)
117 i->ComputeStrippedDestinationURL();
118
119 // Remove duplicates.
120 std::sort(matches_.begin(), matches_.end(),
121 &AutocompleteMatch::DestinationSortFunc);
122 matches_.erase(std::unique(matches_.begin(), matches_.end(),
123 &AutocompleteMatch::DestinationsEqual),
124 matches_.end());
125
126 // Sort and trim to the most relevant kMaxMatches matches.
127 const size_t num_matches = std::min(kMaxMatches, matches_.size());
128 std::partial_sort(matches_.begin(), matches_.begin() + num_matches,
129 matches_.end(), &AutocompleteMatch::MoreRelevant);
130 matches_.resize(num_matches);
131
132 default_match_ = begin();
133
134 // Set the alternate nav URL.
135 alternate_nav_url_ = GURL();
136 if (((input.type() == AutocompleteInput::UNKNOWN) ||
137 (input.type() == AutocompleteInput::REQUESTED_URL)) &&
138 (default_match_ != end()) &&
139 (default_match_->transition != content::PAGE_TRANSITION_TYPED) &&
140 (default_match_->transition != content::PAGE_TRANSITION_KEYWORD) &&
141 (input.canonicalized_url() != default_match_->destination_url))
142 alternate_nav_url_ = input.canonicalized_url();
143 }
144
145 bool AutocompleteResult::HasCopiedMatches() const {
146 for (ACMatches::const_iterator i(begin()); i != end(); ++i) {
147 if (i->from_previous)
148 return true;
149 }
150 return false;
151 }
152
153 size_t AutocompleteResult::size() const {
154 return matches_.size();
155 }
156
157 bool AutocompleteResult::empty() const {
158 return matches_.empty();
159 }
160
161 AutocompleteResult::const_iterator AutocompleteResult::begin() const {
162 return matches_.begin();
163 }
164
165 AutocompleteResult::iterator AutocompleteResult::begin() {
166 return matches_.begin();
167 }
168
169 AutocompleteResult::const_iterator AutocompleteResult::end() const {
170 return matches_.end();
171 }
172
173 AutocompleteResult::iterator AutocompleteResult::end() {
174 return matches_.end();
175 }
176
177 // Returns the match at the given index.
178 const AutocompleteMatch& AutocompleteResult::match_at(size_t index) const {
179 DCHECK_LT(index, matches_.size());
180 return matches_[index];
181 }
182
183 AutocompleteMatch* AutocompleteResult::match_at(size_t index) {
184 DCHECK_LT(index, matches_.size());
185 return &matches_[index];
186 }
187
188 void AutocompleteResult::Reset() {
189 matches_.clear();
190 default_match_ = end();
191 }
192
193 void AutocompleteResult::Swap(AutocompleteResult* other) {
194 const size_t default_match_offset = default_match_ - begin();
195 const size_t other_default_match_offset =
196 other->default_match_ - other->begin();
197 matches_.swap(other->matches_);
198 default_match_ = begin() + other_default_match_offset;
199 other->default_match_ = other->begin() + default_match_offset;
200 alternate_nav_url_.Swap(&(other->alternate_nav_url_));
201 }
202
203 #ifndef NDEBUG
204 void AutocompleteResult::Validate() const {
205 for (const_iterator i(begin()); i != end(); ++i)
206 i->Validate();
207 }
208 #endif
209
210 void AutocompleteResult::BuildProviderToMatches(
211 ProviderToMatches* provider_to_matches) const {
212 for (ACMatches::const_iterator i(begin()); i != end(); ++i)
213 (*provider_to_matches)[i->provider].push_back(*i);
214 }
215
216 // static
217 bool AutocompleteResult::HasMatchByDestination(const AutocompleteMatch& match,
218 const ACMatches& matches) {
219 for (ACMatches::const_iterator i(matches.begin()); i != matches.end(); ++i) {
220 if (i->destination_url == match.destination_url)
221 return true;
222 }
223 return false;
224 }
225
226 void AutocompleteResult::MergeMatchesByProvider(const ACMatches& old_matches,
227 const ACMatches& new_matches) {
228 if (new_matches.size() >= old_matches.size())
229 return;
230
231 size_t delta = old_matches.size() - new_matches.size();
232 const int max_relevance = (new_matches.empty() ?
233 matches_.front().relevance : new_matches[0].relevance) - 1;
234 // Because the goal is a visibly-stable popup, rather than one that preserves
235 // the highest-relevance matches, we copy in the lowest-relevance matches
236 // first. This means that within each provider's "group" of matches, any
237 // synchronous matches (which tend to have the highest scores) will
238 // "overwrite" the initial matches from that provider's previous results,
239 // minimally disturbing the rest of the matches.
240 for (ACMatches::const_reverse_iterator i(old_matches.rbegin());
241 i != old_matches.rend() && delta > 0; ++i) {
242 if (!HasMatchByDestination(*i, new_matches)) {
243 AutocompleteMatch match = *i;
244 match.relevance = std::min(max_relevance, match.relevance);
245 match.from_previous = true;
246 AddMatch(match);
247 delta--;
248 }
249 }
250 }
OLDNEW
« no previous file with comments | « chrome/browser/autocomplete/autocomplete_result.h ('k') | chrome/browser/autocomplete/autocomplete_result_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698