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

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

Issue 10913262: Implement Bookmark Autocomplete Provider (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: Created 8 years, 2 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
Property Changes:
Added: svn:eol-style
+ LF
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/bookmark_provider.h"
6
7 #include <algorithm>
8 #include <functional>
9 #include <vector>
10
11 #include "base/metrics/histogram.h"
12 #include "base/time.h"
13 #include "base/utf_string_conversions.h"
Mark P 2012/10/08 22:51:59 What do you need this for?
mrossetti 2012/10/10 02:26:42 Don't. Removed. On 2012/10/08 22:51:59, Mark P wr
14 #include "chrome/browser/autocomplete/autocomplete_result.h"
15 #include "chrome/browser/bookmarks/bookmark_model.h"
16 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
17 #include "chrome/browser/prefs/pref_service.h"
18 #include "chrome/browser/profiles/profile.h"
19 #include "chrome/common/pref_names.h"
20 #include "net/base/net_util.h"
21
22 typedef std::vector<bookmark_utils::TitleMatch> TitleMatches;
23
24 // BookmarkProvider ------------------------------------------------------------
25
26 BookmarkProvider::BookmarkProvider(
27 AutocompleteProviderListener* listener,
28 Profile* profile)
29 : AutocompleteProvider(listener, profile,
30 AutocompleteProvider::TYPE_BOOKMARK),
31 bookmark_model_(NULL) {
32 if (profile) {
33 bookmark_model_ = BookmarkModelFactory::GetForProfile(profile);
34 languages_ = profile_->GetPrefs()->GetString(prefs::kAcceptLanguages);
35 }
36 }
37
38 void BookmarkProvider::Start(const AutocompleteInput& input,
39 bool minimal_changes) {
40 if (minimal_changes)
41 return;
42 matches_.clear();
43
44 if (input.text().empty() ||
45 ((input.type() != AutocompleteInput::UNKNOWN) &&
46 (input.type() != AutocompleteInput::REQUESTED_URL) &&
47 (input.type() != AutocompleteInput::QUERY)) ||
48 (input.matches_requested() == AutocompleteInput::BEST_MATCH &&
49 input.prevent_inline_autocomplete()))
50 return;
51
52 base::TimeTicks start_time = base::TimeTicks::Now();
53 DoAutocomplete(input,
54 input.matches_requested() == AutocompleteInput::BEST_MATCH);
55 UMA_HISTOGRAM_TIMES("Autocomplete.BookmarkProviderMatchTime",
56 base::TimeTicks::Now() - start_time);
57 }
58
59 BookmarkProvider::~BookmarkProvider() {}
60
61 void BookmarkProvider::DoAutocomplete(const AutocompleteInput& input,
62 bool best_match) {
63 // We may not have a bookmark model for some unit tests.
64 if (!bookmark_model_)
65 return;
66
67 TitleMatches matches;
68 // Retrieve enough bookmarks so that we have a reasonable probability of
69 // retrieving the one that the user desires.
70 const size_t kMaxBookmarkMatches = 50;
71
72 // GetBookmarksWithTitlesMatching returns bookmarks matching the user's
73 // search terms using the following rules:
74 // - The search text is broken up into words. Each word is searched for
75 // separately. In the following I use the 'term' to refer to a search word.
Mark P 2012/10/08 22:51:59 Perhaps replace this bullet with - The search text
mrossetti 2012/10/10 02:26:42 Done.
76 // - Term matches are always performed against the start of a word. 'def'
77 // will match against 'define' but not against 'indefinite'.
78 // - Terms must be at least three characters in length in order to perform
79 // partial word matches. Any term of lesser length will only be used as an
80 // exact match. 'def' will match against 'define' but 'de' will not match.
81 // - A search containing multiple terms will return results with those words
82 // occuring in any order.
83 // - Terms enclosed in quotes will be used as exact matches.
84 // - Multiple terms enclosed in quotes will require those exact words in that
85 // exact order to match.
Mark P 2012/10/08 22:51:59 with no intervening words (I assume)
mrossetti 2012/10/10 02:26:42 Yes. On 2012/10/08 22:51:59, Mark P wrote:
86 // - There are no wildcards.
Mark P 2012/10/08 22:51:59 Consider dropping this bullet.
mrossetti 2012/10/10 02:26:42 Done.
87 // Please refer to the code for BookmarkIndex::GetBookmarksWithTitlesMatching
88 // in order to gain a good understanding of how title searches are performed
Mark P 2012/10/08 22:51:59 gain a good understanding of -> understand or perh
mrossetti 2012/10/10 02:26:42 Reworded. On 2012/10/08 22:51:59, Mark P wrote:
89 // against the user's bookmarks.
90 bookmark_model_->GetBookmarksWithTitlesMatching(input.text(),
91 kMaxBookmarkMatches,
92 &matches);
93 if (matches.empty())
94 return; // There were no matches.
95 for (TitleMatches::const_iterator i = matches.begin(); i != matches.end();
96 ++i) {
97 AutocompleteMatch match(TitleMatchToACMatch(*i));
98 if (match.relevance > 0)
99 matches_.push_back(match);
100 }
101
102 // Sort and clip the resulting matches.
103 size_t max_matches = best_match ? 1 : AutocompleteProvider::kMaxMatches;
104 if (matches_.size() > max_matches) {
105 std::partial_sort(matches_.begin(), matches_.end(),
106 matches_.begin() + max_matches,
107 AutocompleteMatch::MoreRelevant);
108 matches_.resize(max_matches);
109 } else {
110 std::sort(matches_.begin(), matches_.end(),
111 AutocompleteMatch::MoreRelevant);
112 }
113 }
114
115 namespace {
116
117 // for_each helper functor that calculates a match factor used to when
118 // calculating the final score.
119 //
120 // Calculate a 'factor' from 0.0 to 1.0 based on 1) how much of the bookmark's
121 // title the term matches, and 2) where the match is positioned within the
122 // bookmark's title. A full length match earns a 1.0. A half-length match earns
123 // at most a 0.5 and at least a 0.25. A single character match against a title
124 // that is 100 characters long where the match is at the first character will
125 // earn a 0.01 and at the last character will earn a 0.0001.
126 class ScoringFunctor {
127 public:
128 // |title_length| is the length of the bookmark title against which this
129 // match will be scored.
130 ScoringFunctor(size_t title_length)
131 : title_length_(static_cast<double>(title_length)),
132 scoring_factor_(0.0) {
133 }
134
135 void operator()(const Snippet::MatchPosition& match) {
136 double term_length = static_cast<double>(match.second - match.first);
137 scoring_factor_ += term_length / title_length_ *
138 (title_length_ - match.first) / title_length_;
139 }
140
141 double ScoringFactor() { return scoring_factor_; }
142
143 private:
144 double title_length_;
145 double scoring_factor_;
146 };
147
148 } // namespace
149
150 AutocompleteMatch BookmarkProvider::TitleMatchToACMatch(
151 const bookmark_utils::TitleMatch& title_match) {
152 // Compose a match that has the URL of the bookmar and the bookmark's title,
Mark P 2012/10/08 22:51:59 bookmark
mrossetti 2012/10/10 02:26:42 Oops. On 2012/10/08 22:51:59, Mark P wrote:
153 // not the URL's page title, as the description. Note that if the relevance
154 // is never changed from 0 that the match will be discarded.
155 AutocompleteMatch match(this, 0, false, AutocompleteMatch::BOOKMARK_TITLE);
Mark P 2012/10/08 22:51:59 Perhaps I wasn't clear. Please add a comment here
mrossetti 2012/10/10 02:26:42 I've added some comments. Please note that deletab
156 const string16& title(title_match.node->GetTitle());
157 if (title.empty())
Mark P 2012/10/08 22:51:59 How can this happen?
mrossetti 2012/10/10 02:26:42 It probably cannot happen. I'll change it to a DCH
158 return match;
159 const GURL& url(title_match.node->url());
160 match.destination_url = url;
161 match.contents = net::FormatUrl(url, languages_,
162 net::kFormatUrlOmitAll & net::kFormatUrlOmitHTTP,
163 net::UnescapeRule::SPACES, NULL, NULL, NULL);
164 match.contents_class.push_back(
165 ACMatchClassification(0, ACMatchClassification::NONE));
166 match.fill_into_edit =
167 AutocompleteInput::FormattedStringWithEquivalentMeaning(url,
168 match.contents);
169 match.description = title;
170 match.description_class =
171 ClassificationsFromMatch(title_match.match_positions,
172 match.description.size());
173 match.starred = true;
174
175 // Calculate the relevance based on:
176 // 1) how much of the bookmark's title has been matched by the input text,
177 // 2) where the matches occurs within the bookmark's title, and
178 // 2) how many times the bookmark's URL is referenced by other bookmarks.
Mark P 2012/10/08 22:51:59 3)
mrossetti 2012/10/10 02:26:42 I'm glad one of us can count! On 2012/10/08 22:51
179 // For each match calculate a 'factor' (see ScoringFunctor for details on
180 // how the factor is calculated), sum up those factors, then use the sum to
181 // figure out a score between the base and the maximum.
182 ScoringFunctor position_functor =
183 for_each(title_match.match_positions.begin(),
184 title_match.match_positions.end(), ScoringFunctor(title.size()));
Mark P 2012/10/08 22:51:59 Can you please comment here or elsewhere what will
mrossetti 2012/10/10 02:26:42 I don't think I'll add any consideration for repea
185 const int kBaseBookmarkScore = 900;
186 const int kMaxBookmarkScore = AutocompleteResult::kLowestDefaultScore - 1;
187 const double kBookmarkScoreRange =
188 static_cast<double>(kMaxBookmarkScore - kBaseBookmarkScore);
189 // It's not likely that GetBookmarksWithTitlesMatching will return overlapping
190 // matches but let's play it safe.
191 match.relevance = std::min(kMaxBookmarkScore,
192 static_cast<int>(position_functor.ScoringFactor() * kBookmarkScoreRange) +
193 kBaseBookmarkScore);
194 // Don't waste any time searching for additional referenced URLs if we
195 // already have a perfect title match.
196 if (match.relevance >= kMaxBookmarkScore)
197 return match;
198 // Boost the score if the bookmark's URL is referenced by other bookmarks.
199 const int kURLCountBoost[4] = { 0, 75, 125, 150 };
200 std::vector<const BookmarkNode*> nodes;
201 bookmark_model_->GetNodesByURL(url, &nodes);
202 match.relevance +=
203 kURLCountBoost[std::min(arraysize(kURLCountBoost), nodes.size()) - 1];
Mark P 2012/10/08 22:51:59 Do you want a max(, 0) in there as a precaution, o
mrossetti 2012/10/10 02:26:42 I'm sure we can trust that there will always be at
204 match.relevance = std::min(kMaxBookmarkScore, match.relevance);
205 return match;
206 }
207
208 // static
209 ACMatchClassifications BookmarkProvider::ClassificationsFromMatch(
210 const Snippet::MatchPositions& positions,
211 size_t text_length) {
212 ACMatchClassifications classifications;
213 if (positions.empty()) {
214 classifications.push_back(
215 ACMatchClassification(0, ACMatchClassification::NONE));
216 return classifications;
217 }
218
219 for (Snippet::MatchPositions::const_iterator i = positions.begin();
220 i != positions.end(); ++i) {
221 AutocompleteMatch::ACMatchClassifications new_class;
222 AutocompleteMatch::ClassifyLocationInString(i->first, i->second - i->first,
223 text_length, 0, &new_class);
224 classifications = AutocompleteMatch::MergeClassifications(
225 classifications, new_class);
226 }
227 return classifications;
228 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698