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

Side by Side Diff: chrome/browser/search_engines/util.cc

Issue 10409002: Make prepopulated-TemplateURL-de-duper heuristic smarter. Instead of blindly preserving the first U… (Closed) Base URL: http://git.chromium.org/chromium/src.git@master
Patch Set: Steve's initial changes + tests Created 8 years, 7 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
OLDNEW
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. 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 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "chrome/browser/search_engines/util.h" 5 #include "chrome/browser/search_engines/util.h"
6 6
7 #include <set> 7 #include <map>
8 #include <vector> 8 #include <vector>
9 9
10 #include "base/logging.h" 10 #include "base/logging.h"
11 #include "chrome/browser/profiles/profile.h" 11 #include "chrome/browser/profiles/profile.h"
12 #include "chrome/browser/search_engines/template_url.h" 12 #include "chrome/browser/search_engines/template_url.h"
13 #include "chrome/browser/search_engines/template_url_prepopulate_data.h" 13 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
14 #include "chrome/browser/search_engines/template_url_service.h" 14 #include "chrome/browser/search_engines/template_url_service.h"
15 #include "chrome/browser/search_engines/template_url_service_factory.h" 15 #include "chrome/browser/search_engines/template_url_service_factory.h"
16 #include "content/public/browser/browser_thread.h" 16 #include "content/public/browser/browser_thread.h"
17 17
18 using content::BrowserThread; 18 using content::BrowserThread;
19 19
20 string16 GetDefaultSearchEngineName(Profile* profile) { 20 string16 GetDefaultSearchEngineName(Profile* profile) {
21 if (!profile) { 21 if (!profile) {
22 NOTREACHED(); 22 NOTREACHED();
23 return string16(); 23 return string16();
24 } 24 }
25 const TemplateURL* const default_provider = 25 const TemplateURL* const default_provider =
26 TemplateURLServiceFactory::GetForProfile(profile)-> 26 TemplateURLServiceFactory::GetForProfile(profile)->
27 GetDefaultSearchProvider(); 27 GetDefaultSearchProvider();
28 if (!default_provider) { 28 if (!default_provider) {
29 // TODO(cpu): bug 1187517. It is possible to have no default provider. 29 // TODO(cpu): bug 1187517. It is possible to have no default provider.
30 // returning an empty string is a stopgap measure for the crash 30 // returning an empty string is a stopgap measure for the crash
31 // http://code.google.com/p/chromium/issues/detail?id=2573 31 // http://code.google.com/p/chromium/issues/detail?id=2573
32 return string16(); 32 return string16();
33 } 33 }
34 return default_provider->short_name(); 34 return default_provider->short_name();
35 } 35 }
36 36
37 // Removes (and deletes) TemplateURLs from |urls| that have duplicate 37 // Removes (and deletes) TemplateURLs from |template_urls| and |service| if they
38 // prepopulate ids. Duplicate prepopulate ids are not allowed, but due to a 38 // have duplicate prepopulate ids.
39 // bug it was possible get dups. This step is only called when the version 39 void RemoveDuplicatePrepopulateIDs(
40 // number changes. Only pass in a non-NULL value for |service| if the removed 40 WebDataService* service,
41 // items should be removed from the DB. 41 const std::vector<TemplateURL*>& prepopulated_urls,
42 static void RemoveDuplicatePrepopulateIDs( 42 TemplateURL* default_search_provider,
43 std::vector<TemplateURL*>* template_urls, 43 TemplateURLService::TemplateURLVector* template_urls) {
44 WebDataService* service) { 44 DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI));
45 DCHECK(template_urls); 45 DCHECK(template_urls);
46 DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI));
47 46
48 std::set<int> ids; 47 // For convenience construct an ID->TemplateURL* map from |prepopulated_urls|.
49 for (std::vector<TemplateURL*>::iterator i = template_urls->begin(); 48 typedef std::map<int, TemplateURL*> PrepopulatedURLMap;
50 i != template_urls->end(); ) { 49 PrepopulatedURLMap prepopulated_url_map;
51 int prepopulate_id = (*i)->prepopulate_id(); 50 for (std::vector<TemplateURL*>::const_iterator i(prepopulated_urls.begin());
52 if (prepopulate_id) { 51 i != prepopulated_urls.end(); ++i)
53 if (ids.find(prepopulate_id) != ids.end()) { 52 prepopulated_url_map[(*i)->prepopulate_id()] = *i;
54 if (service) 53
55 service->RemoveKeyword((*i)->id()); 54 // Separate |template_urls| into prepopulated and non-prepopulated groups.
56 delete *i; 55 typedef std::multimap<int, TemplateURL*> UncheckedURLMap;
57 i = template_urls->erase(i); 56 UncheckedURLMap unchecked_urls;
58 } else { 57 TemplateURLService::TemplateURLVector checked_urls;
59 ids.insert(prepopulate_id); 58 for (TemplateURLService::TemplateURLVector::iterator i(
60 ++i; 59 template_urls->begin()); i != template_urls->end(); ++i) {
60 TemplateURL* turl = *i;
61 int prepopulate_id = turl->prepopulate_id();
62 if (prepopulate_id)
63 unchecked_urls.insert(std::make_pair(prepopulate_id, turl));
64 else
65 checked_urls.push_back(turl);
66 }
67
68 // For each group of prepopulated URLs with one ID, find the best URL to use
69 // and add it to the (initially all non-prepopulated) URLs we've already OKed.
70 // Delete the others from the service and from memory.
71 while (!unchecked_urls.empty()) {
72 // Find the best URL.
73 int prepopulate_id = unchecked_urls.begin()->first;
74 PrepopulatedURLMap::const_iterator prepopulated_url =
75 prepopulated_url_map.find(prepopulate_id);
76 UncheckedURLMap::iterator end = unchecked_urls.upper_bound(prepopulate_id);
77 UncheckedURLMap::iterator best = unchecked_urls.begin();
78 bool matched_keyword = false;
79 for (UncheckedURLMap::iterator i = unchecked_urls.begin(); i != end; ++i) {
80 // A URL is automatically the best if it's the default search engine.
81 if (i->second == default_search_provider) {
82 best = i;
83 break;
61 } 84 }
62 } else { 85
63 ++i; 86 // Otherwise, a URL is best if it matches the prepopulated data's keyword;
87 // if none match, just fall back to using the one with the lowest ID.
88 if (matched_keyword)
89 continue;
90 if ((prepopulated_url != prepopulated_url_map.end()) &&
91 i->second->HasSameKeywordAs(*prepopulated_url->second)) {
92 best = i;
93 matched_keyword = true;
94 } else if (i->second->id() < best->second->id()) {
95 best = i;
96 }
64 } 97 }
98
99 // Add it to the checked group; remove it from elsewhere.
Peter Kasting 2012/05/17 20:12:17 Nit: Let's just say "Add the best URL to the check
SteveT 2012/05/17 21:27:22 Done.
100 checked_urls.push_back(best->second);
101 unchecked_urls.erase(best);
Peter Kasting 2012/05/17 20:12:17 Instead of this, let's put an "if (i == best) cont
SteveT 2012/05/17 21:27:22 Makes sense. Done.
102 for (UncheckedURLMap::iterator i = unchecked_urls.begin(); i != end; ++i) {
103 if (service)
SteveT 2012/05/17 13:34:54 Since we're allowing the service to be NULL (as re
104 service->RemoveKeyword(i->second->id());
105 delete i->second;
106 }
107
108 // Done with this group.
109 unchecked_urls.erase(unchecked_urls.begin(), end);
65 } 110 }
111
112 // Return the checked URLs.
113 template_urls->swap(checked_urls);
66 } 114 }
67 115
68 // Returns the TemplateURL with id specified from the list of TemplateURLs. 116 // Returns the TemplateURL with id specified from the list of TemplateURLs.
69 // If not found, returns NULL. 117 // If not found, returns NULL.
70 TemplateURL* GetTemplateURLByID( 118 TemplateURL* GetTemplateURLByID(
71 const std::vector<TemplateURL*>& template_urls, 119 const TemplateURLService::TemplateURLVector& template_urls,
72 int64 id) { 120 int64 id) {
73 for (std::vector<TemplateURL*>::const_iterator i = template_urls.begin(); 121 for (TemplateURLService::TemplateURLVector::const_iterator i(
74 i != template_urls.end(); ++i) { 122 template_urls.begin()); i != template_urls.end(); ++i) {
75 if ((*i)->id() == id) { 123 if ((*i)->id() == id) {
76 return *i; 124 return *i;
77 } 125 }
78 } 126 }
79 return NULL; 127 return NULL;
80 } 128 }
81 129
82 // Loads engines from prepopulate data and merges them in with the existing 130 // Loads engines from prepopulate data and merges them in with the existing
83 // engines. This is invoked when the version of the prepopulate data changes. 131 // engines. This is invoked when the version of the prepopulate data changes.
84 void MergeEnginesFromPrepopulateData( 132 void MergeEnginesFromPrepopulateData(
85 Profile* profile, 133 Profile* profile,
86 WebDataService* service, 134 WebDataService* service,
87 std::vector<TemplateURL*>* template_urls, 135 const std::vector<TemplateURL*>& prepopulated_urls,
136 size_t default_search_index,
137 TemplateURLService::TemplateURLVector* template_urls,
88 TemplateURL** default_search_provider) { 138 TemplateURL** default_search_provider) {
89 DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI)); 139 DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI));
90 DCHECK(template_urls); 140 DCHECK(template_urls);
91 DCHECK(default_search_provider); 141 DCHECK(default_search_provider);
92 142
93 // Create a map to hold all provided |template_urls| that originally came from 143 // Create a map to hold all provided |template_urls| that originally came from
94 // prepopulate data (i.e. have a non-zero prepopulate_id()). 144 // prepopulate data (i.e. have a non-zero prepopulate_id()).
95 typedef std::map<int, TemplateURL*> IDMap; 145 typedef std::map<int, TemplateURL*> IDMap;
96 IDMap id_to_turl; 146 IDMap id_to_turl;
97 for (std::vector<TemplateURL*>::iterator i(template_urls->begin()); 147 for (TemplateURLService::TemplateURLVector::iterator i(
98 i != template_urls->end(); ++i) { 148 template_urls->begin()); i != template_urls->end(); ++i) {
99 int prepopulate_id = (*i)->prepopulate_id(); 149 int prepopulate_id = (*i)->prepopulate_id();
100 if (prepopulate_id > 0) 150 if (prepopulate_id > 0)
101 id_to_turl[prepopulate_id] = *i; 151 id_to_turl[prepopulate_id] = *i;
102 } 152 }
103 153
104 // Get the current set of prepopulatd URLs.
105 std::vector<TemplateURL*> prepopulated_urls;
106 size_t default_search_index;
107 TemplateURLPrepopulateData::GetPrepopulatedEngines(profile,
108 &prepopulated_urls, &default_search_index);
109
110 // For each current prepopulated URL, check whether |template_urls| contained 154 // For each current prepopulated URL, check whether |template_urls| contained
111 // a matching prepopulated URL. If so, update the passed-in URL to match the 155 // a matching prepopulated URL. If so, update the passed-in URL to match the
112 // current data. (If the passed-in URL was user-edited, we persist the user's 156 // current data. (If the passed-in URL was user-edited, we persist the user's
113 // name and keyword.) If not, add the prepopulated URL to |template_urls|. 157 // name and keyword.) If not, add the prepopulated URL to |template_urls|.
114 // Along the way, point |default_search_provider| at the default prepopulated 158 // Along the way, point |default_search_provider| at the default prepopulated
115 // URL, if the user hasn't already set another URL as default. 159 // URL, if the user hasn't already set another URL as default.
116 for (size_t i = 0; i < prepopulated_urls.size(); ++i) { 160 for (size_t i = 0; i < prepopulated_urls.size(); ++i) {
117 // We take ownership of |prepopulated_urls[i]|. 161 // We take ownership of |prepopulated_urls[i]|.
118 scoped_ptr<TemplateURL> prepopulated_url(prepopulated_urls[i]); 162 scoped_ptr<TemplateURL> prepopulated_url(prepopulated_urls[i]);
119 const int prepopulated_id = prepopulated_url->prepopulate_id(); 163 const int prepopulated_id = prepopulated_url->prepopulate_id();
(...skipping 10 matching lines...) Expand all
130 if (!existing_url->safe_for_autoreplace()) { 174 if (!existing_url->safe_for_autoreplace()) {
131 data.safe_for_autoreplace = false; 175 data.safe_for_autoreplace = false;
132 data.SetKeyword(existing_url->keyword()); 176 data.SetKeyword(existing_url->keyword());
133 data.short_name = existing_url->short_name(); 177 data.short_name = existing_url->short_name();
134 } 178 }
135 data.id = existing_url->id(); 179 data.id = existing_url->id();
136 if (service) 180 if (service)
137 service->UpdateKeyword(data); 181 service->UpdateKeyword(data);
138 182
139 // Replace the entry in |template_urls| with the updated one. 183 // Replace the entry in |template_urls| with the updated one.
140 std::vector<TemplateURL*>::iterator j = std::find(template_urls->begin(), 184 TemplateURLService::TemplateURLVector::iterator j = std::find(
141 template_urls->end(), existing_url.get()); 185 template_urls->begin(), template_urls->end(), existing_url.get());
142 *j = new TemplateURL(profile, data); 186 *j = new TemplateURL(profile, data);
143 url_in_vector = *j; 187 url_in_vector = *j;
144 if (*default_search_provider == existing_url.get()) 188 if (*default_search_provider == existing_url.get())
145 *default_search_provider = url_in_vector; 189 *default_search_provider = url_in_vector;
146 } else { 190 } else {
147 template_urls->push_back(prepopulated_url.release()); 191 template_urls->push_back(prepopulated_url.release());
148 url_in_vector = template_urls->back(); 192 url_in_vector = template_urls->back();
149 } 193 }
150 DCHECK(url_in_vector); 194 DCHECK(url_in_vector);
151 if (i == default_search_index && !*default_search_provider) 195 if (i == default_search_index && !*default_search_provider)
152 *default_search_provider = url_in_vector; 196 *default_search_provider = url_in_vector;
153 } 197 }
154 198
155 // The block above removed all the URLs from the |id_to_turl| map that were 199 // The block above removed all the URLs from the |id_to_turl| map that were
156 // found in the prepopulate data. Any remaining URLs that haven't been 200 // found in the prepopulate data. Any remaining URLs that haven't been
157 // user-edited or made default can be removed from the data store. 201 // user-edited or made default can be removed from the data store.
158 for (IDMap::iterator i(id_to_turl.begin()); i != id_to_turl.end(); ++i) { 202 for (IDMap::iterator i(id_to_turl.begin()); i != id_to_turl.end(); ++i) {
159 const TemplateURL* template_url = i->second; 203 const TemplateURL* template_url = i->second;
160 if ((template_url->safe_for_autoreplace()) && 204 if ((template_url->safe_for_autoreplace()) &&
161 (template_url != *default_search_provider)) { 205 (template_url != *default_search_provider)) {
162 std::vector<TemplateURL*>::iterator j = 206 TemplateURLService::TemplateURLVector::iterator j =
163 std::find(template_urls->begin(), template_urls->end(), template_url); 207 std::find(template_urls->begin(), template_urls->end(), template_url);
164 DCHECK(j != template_urls->end()); 208 DCHECK(j != template_urls->end());
165 template_urls->erase(j); 209 template_urls->erase(j);
166 if (service) 210 if (service)
167 service->RemoveKeyword(template_url->id()); 211 service->RemoveKeyword(template_url->id());
168 delete template_url; 212 delete template_url;
169 } 213 }
170 } 214 }
171 } 215 }
172 216
173 void GetSearchProvidersUsingKeywordResult( 217 void GetSearchProvidersUsingKeywordResult(
174 const WDTypedResult& result, 218 const WDTypedResult& result,
175 WebDataService* service, 219 WebDataService* service,
176 Profile* profile, 220 Profile* profile,
177 std::vector<TemplateURL*>* template_urls, 221 TemplateURLService::TemplateURLVector* template_urls,
178 TemplateURL** default_search_provider, 222 TemplateURL** default_search_provider,
179 int* new_resource_keyword_version) { 223 int* new_resource_keyword_version) {
180 DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI)); 224 DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI));
181 DCHECK(template_urls); 225 DCHECK(template_urls);
182 DCHECK(template_urls->empty()); 226 DCHECK(template_urls->empty());
183 DCHECK(default_search_provider); 227 DCHECK(default_search_provider);
184 DCHECK(*default_search_provider == NULL); 228 DCHECK(*default_search_provider == NULL);
185 DCHECK_EQ(result.GetType(), KEYWORDS_RESULT); 229 DCHECK_EQ(result.GetType(), KEYWORDS_RESULT);
186 DCHECK(new_resource_keyword_version); 230 DCHECK(new_resource_keyword_version);
187 231
188 *new_resource_keyword_version = 0; 232 *new_resource_keyword_version = 0;
189 WDKeywordsResult keyword_result = reinterpret_cast< 233 WDKeywordsResult keyword_result = reinterpret_cast<
190 const WDResult<WDKeywordsResult>*>(&result)->GetValue(); 234 const WDResult<WDKeywordsResult>*>(&result)->GetValue();
191 235
192 for (KeywordTable::Keywords::const_iterator i( 236 for (KeywordTable::Keywords::const_iterator i(
193 keyword_result.keywords.begin()); i != keyword_result.keywords.end(); 237 keyword_result.keywords.begin()); i != keyword_result.keywords.end();
194 ++i) 238 ++i)
195 template_urls->push_back(new TemplateURL(profile, *i)); 239 template_urls->push_back(new TemplateURL(profile, *i));
196 240
197 const int resource_keyword_version =
198 TemplateURLPrepopulateData::GetDataVersion(
199 profile ? profile->GetPrefs() : NULL);
200 if (keyword_result.builtin_keyword_version != resource_keyword_version) {
201 // There should never be duplicate TemplateURLs. We had a bug such that
202 // duplicate TemplateURLs existed for one locale. As such we invoke
203 // RemoveDuplicatePrepopulateIDs to nuke the duplicates.
204 RemoveDuplicatePrepopulateIDs(template_urls, service);
205 }
206
207 int64 default_search_provider_id = keyword_result.default_search_provider_id; 241 int64 default_search_provider_id = keyword_result.default_search_provider_id;
208 if (default_search_provider_id) { 242 if (default_search_provider_id) {
209 *default_search_provider = 243 *default_search_provider =
210 GetTemplateURLByID(*template_urls, default_search_provider_id); 244 GetTemplateURLByID(*template_urls, default_search_provider_id);
211 } 245 }
212 246
247 std::vector<TemplateURL*> prepopulated_urls;
248 size_t default_search_index;
249 TemplateURLPrepopulateData::GetPrepopulatedEngines(profile,
250 &prepopulated_urls, &default_search_index);
251 RemoveDuplicatePrepopulateIDs(service, prepopulated_urls,
252 *default_search_provider, template_urls);
253
254 const int resource_keyword_version =
255 TemplateURLPrepopulateData::GetDataVersion(
256 profile ? profile->GetPrefs() : NULL);
213 if (keyword_result.builtin_keyword_version != resource_keyword_version) { 257 if (keyword_result.builtin_keyword_version != resource_keyword_version) {
214 MergeEnginesFromPrepopulateData(profile, service, template_urls, 258 MergeEnginesFromPrepopulateData(profile, service, prepopulated_urls,
215 default_search_provider); 259 default_search_index, template_urls, default_search_provider);
216 *new_resource_keyword_version = resource_keyword_version; 260 *new_resource_keyword_version = resource_keyword_version;
217 } 261 }
218 } 262 }
219 263
220 bool DidDefaultSearchProviderChange( 264 bool DidDefaultSearchProviderChange(
221 const WDTypedResult& result, 265 const WDTypedResult& result,
222 Profile* profile, 266 Profile* profile,
223 scoped_ptr<TemplateURL>* backup_default_search_provider) { 267 scoped_ptr<TemplateURL>* backup_default_search_provider) {
224 DCHECK(backup_default_search_provider); 268 DCHECK(backup_default_search_provider);
225 DCHECK(!backup_default_search_provider->get()); 269 DCHECK(!backup_default_search_provider->get());
226 DCHECK_EQ(result.GetType(), KEYWORDS_RESULT); 270 DCHECK_EQ(result.GetType(), KEYWORDS_RESULT);
227 271
228 WDKeywordsResult keyword_result = reinterpret_cast< 272 WDKeywordsResult keyword_result = reinterpret_cast<
229 const WDResult<WDKeywordsResult>*>(&result)->GetValue(); 273 const WDResult<WDKeywordsResult>*>(&result)->GetValue();
230 274
231 if (!keyword_result.did_default_search_provider_change) 275 if (!keyword_result.did_default_search_provider_change)
232 return false; 276 return false;
233 277
234 if (keyword_result.backup_valid) { 278 if (keyword_result.backup_valid) {
235 backup_default_search_provider->reset(new TemplateURL(profile, 279 backup_default_search_provider->reset(new TemplateURL(profile,
236 keyword_result.default_search_provider_backup)); 280 keyword_result.default_search_provider_backup));
237 } 281 }
238 return true; 282 return true;
239 } 283 }
240 284
285 namespace testing {
286
287 void TestRemoveDuplicatePrepopulateIDs(
288 WebDataService* service,
289 const std::vector<TemplateURL*>& prepopulated_urls,
290 TemplateURL* default_search_provider,
291 TemplateURLService::TemplateURLVector* template_urls) {
292 RemoveDuplicatePrepopulateIDs(service, prepopulated_urls,
293 default_search_provider, template_urls);
294 }
295
296 } // namespace testing
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698