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

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

Powered by Google App Engine
This is Rietveld 408576698