1 // Copyright 2014 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.
5 #include "components/search_engines/util.h"
12 #include "base/logging.h"
13 #include "base/memory/scoped_vector.h"
14 #include "base/prefs/pref_service.h"
15 #include "base/time/time.h"
16 #include "components/search_engines/template_url.h"
17 #include "components/search_engines/template_url_prepopulate_data.h"
18 #include "components/search_engines/template_url_service.h"
20 base::string16
GetDefaultSearchEngineName(TemplateURLService
* service
) {
22 const TemplateURL
* const default_provider
=
23 service
->GetDefaultSearchProvider();
24 if (!default_provider
) {
25 // TODO(cpu): bug 1187517. It is possible to have no default provider.
26 // returning an empty string is a stopgap measure for the crash
27 // http://code.google.com/p/chromium/issues/detail?id=2573
28 return base::string16();
30 return default_provider
->short_name();
33 GURL
GetDefaultSearchURLForSearchTerms(TemplateURLService
* service
,
34 const base::string16
& terms
) {
36 const TemplateURL
* default_provider
= service
->GetDefaultSearchProvider();
37 if (!default_provider
)
39 const TemplateURLRef
& search_url
= default_provider
->url_ref();
40 DCHECK(search_url
.SupportsReplacement(service
->search_terms_data()));
41 TemplateURLRef::SearchTermsArgs
search_terms_args(terms
);
42 search_terms_args
.append_extra_query_params
= true;
43 return GURL(search_url
.ReplaceSearchTerms(search_terms_args
,
44 service
->search_terms_data()));
47 void RemoveDuplicatePrepopulateIDs(
48 KeywordWebDataService
* service
,
49 const ScopedVector
<TemplateURLData
>& prepopulated_urls
,
50 TemplateURL
* default_search_provider
,
51 TemplateURLService::TemplateURLVector
* template_urls
,
52 const SearchTermsData
& search_terms_data
,
53 std::set
<std::string
>* removed_keyword_guids
) {
54 DCHECK(template_urls
);
56 // For convenience construct an ID->TemplateURL* map from |prepopulated_urls|.
57 typedef std::map
<int, TemplateURLData
*> PrepopulatedURLMap
;
58 PrepopulatedURLMap prepopulated_url_map
;
59 for (std::vector
<TemplateURLData
*>::const_iterator
i(
60 prepopulated_urls
.begin());
61 i
!= prepopulated_urls
.end();
63 prepopulated_url_map
[(*i
)->prepopulate_id
] = *i
;
65 // Separate |template_urls| into prepopulated and non-prepopulated groups.
66 typedef std::multimap
<int, TemplateURL
*> UncheckedURLMap
;
67 UncheckedURLMap unchecked_urls
;
68 TemplateURLService::TemplateURLVector checked_urls
;
69 for (TemplateURLService::TemplateURLVector::iterator
i(
70 template_urls
->begin()); i
!= template_urls
->end(); ++i
) {
71 TemplateURL
* turl
= *i
;
72 int prepopulate_id
= turl
->prepopulate_id();
74 unchecked_urls
.insert(std::make_pair(prepopulate_id
, turl
));
76 checked_urls
.push_back(turl
);
79 // For each group of prepopulated URLs with one ID, find the best URL to use
80 // and add it to the (initially all non-prepopulated) URLs we've already OKed.
81 // Delete the others from the service and from memory.
82 while (!unchecked_urls
.empty()) {
84 int prepopulate_id
= unchecked_urls
.begin()->first
;
85 PrepopulatedURLMap::const_iterator prepopulated_url
=
86 prepopulated_url_map
.find(prepopulate_id
);
87 UncheckedURLMap::iterator end
= unchecked_urls
.upper_bound(prepopulate_id
);
88 UncheckedURLMap::iterator best
= unchecked_urls
.begin();
89 bool matched_keyword
= false;
90 for (UncheckedURLMap::iterator i
= unchecked_urls
.begin(); i
!= end
; ++i
) {
91 // If the user-selected DSE is a prepopulated engine its properties will
92 // either come from the prepopulation origin or from the user preferences
93 // file (see DefaultSearchManager). Those properties will end up
94 // overwriting whatever we load now anyway. If we are eliminating
95 // duplicates, then, we err on the side of keeping the thing that looks
96 // more like the value we will end up with in the end.
97 if (default_search_provider
&&
98 (default_search_provider
->prepopulate_id() ==
99 i
->second
->prepopulate_id()) &&
100 default_search_provider
->HasSameKeywordAs(i
->second
->data(),
101 search_terms_data
)) {
106 // Otherwise, a URL is best if it matches the prepopulated data's keyword;
107 // if none match, just fall back to using the one with the lowest ID.
110 if ((prepopulated_url
!= prepopulated_url_map
.end()) &&
111 i
->second
->HasSameKeywordAs(*prepopulated_url
->second
,
112 search_terms_data
)) {
114 matched_keyword
= true;
115 } else if (i
->second
->id() < best
->second
->id()) {
120 // Add the best URL to the checked group and delete the rest.
121 checked_urls
.push_back(best
->second
);
122 for (UncheckedURLMap::iterator i
= unchecked_urls
.begin(); i
!= end
; ++i
) {
126 service
->RemoveKeyword(i
->second
->id());
127 if (removed_keyword_guids
)
128 removed_keyword_guids
->insert(i
->second
->sync_guid());
133 // Done with this group.
134 unchecked_urls
.erase(unchecked_urls
.begin(), end
);
137 // Return the checked URLs.
138 template_urls
->swap(checked_urls
);
141 // Returns the TemplateURL with id specified from the list of TemplateURLs.
142 // If not found, returns NULL.
143 TemplateURL
* GetTemplateURLByID(
144 const TemplateURLService::TemplateURLVector
& template_urls
,
146 for (TemplateURLService::TemplateURLVector::const_iterator
i(
147 template_urls
.begin()); i
!= template_urls
.end(); ++i
) {
148 if ((*i
)->id() == id
) {
155 TemplateURL
* FindURLByPrepopulateID(
156 const TemplateURLService::TemplateURLVector
& template_urls
,
157 int prepopulate_id
) {
158 for (std::vector
<TemplateURL
*>::const_iterator i
= template_urls
.begin();
159 i
< template_urls
.end(); ++i
) {
160 if ((*i
)->prepopulate_id() == prepopulate_id
)
166 void MergeIntoPrepopulatedEngineData(const TemplateURL
* original_turl
,
167 TemplateURLData
* prepopulated_url
) {
168 DCHECK_EQ(original_turl
->prepopulate_id(), prepopulated_url
->prepopulate_id
);
169 if (!original_turl
->safe_for_autoreplace()) {
170 prepopulated_url
->safe_for_autoreplace
= false;
171 prepopulated_url
->SetKeyword(original_turl
->keyword());
172 prepopulated_url
->short_name
= original_turl
->short_name();
174 prepopulated_url
->id
= original_turl
->id();
175 prepopulated_url
->sync_guid
= original_turl
->sync_guid();
176 prepopulated_url
->date_created
= original_turl
->date_created();
177 prepopulated_url
->last_modified
= original_turl
->last_modified();
180 ActionsFromPrepopulateData::ActionsFromPrepopulateData() {}
182 ActionsFromPrepopulateData::~ActionsFromPrepopulateData() {}
184 // This is invoked when the version of the prepopulate data changes.
185 // If |removed_keyword_guids| is not NULL, the Sync GUID of each item removed
186 // from the DB will be added to it. Note that this function will take
187 // ownership of |prepopulated_urls| and will clear the vector.
188 void MergeEnginesFromPrepopulateData(
189 KeywordWebDataService
* service
,
190 ScopedVector
<TemplateURLData
>* prepopulated_urls
,
191 size_t default_search_index
,
192 TemplateURLService::TemplateURLVector
* template_urls
,
193 TemplateURL
* default_search_provider
,
194 std::set
<std::string
>* removed_keyword_guids
) {
195 DCHECK(prepopulated_urls
);
196 DCHECK(template_urls
);
198 ActionsFromPrepopulateData
actions(CreateActionsFromCurrentPrepopulateData(
199 prepopulated_urls
, *template_urls
, default_search_provider
));
202 for (std::vector
<TemplateURL
*>::iterator i
= actions
.removed_engines
.begin();
203 i
< actions
.removed_engines
.end(); ++i
) {
204 scoped_ptr
<TemplateURL
> template_url(*i
);
205 TemplateURLService::TemplateURLVector::iterator j
=
206 std::find(template_urls
->begin(), template_urls
->end(), template_url
);
207 DCHECK(j
!= template_urls
->end());
208 DCHECK(!default_search_provider
||
209 (*j
)->prepopulate_id() != default_search_provider
->prepopulate_id());
210 template_urls
->erase(j
);
212 service
->RemoveKeyword(template_url
->id());
213 if (removed_keyword_guids
)
214 removed_keyword_guids
->insert(template_url
->sync_guid());
219 for (EditedEngines::iterator
i(actions
.edited_engines
.begin());
220 i
< actions
.edited_engines
.end(); ++i
) {
221 TemplateURLData
& data
= i
->second
;
222 scoped_ptr
<TemplateURL
> existing_url(i
->first
);
224 service
->UpdateKeyword(data
);
226 // Replace the entry in |template_urls| with the updated one.
227 TemplateURLService::TemplateURLVector::iterator j
= std::find(
228 template_urls
->begin(), template_urls
->end(), existing_url
.get());
229 *j
= new TemplateURL(data
);
233 for (std::vector
<TemplateURLData
>::const_iterator it
=
234 actions
.added_engines
.begin();
235 it
!= actions
.added_engines
.end();
237 template_urls
->push_back(new TemplateURL(*it
));
241 ActionsFromPrepopulateData
CreateActionsFromCurrentPrepopulateData(
242 ScopedVector
<TemplateURLData
>* prepopulated_urls
,
243 const TemplateURLService::TemplateURLVector
& existing_urls
,
244 const TemplateURL
* default_search_provider
) {
245 // Create a map to hold all provided |template_urls| that originally came from
246 // prepopulate data (i.e. have a non-zero prepopulate_id()).
247 typedef std::map
<int, TemplateURL
*> IDMap
;
249 for (TemplateURLService::TemplateURLVector::const_iterator
i(
250 existing_urls
.begin()); i
!= existing_urls
.end(); ++i
) {
251 int prepopulate_id
= (*i
)->prepopulate_id();
252 if (prepopulate_id
> 0)
253 id_to_turl
[prepopulate_id
] = *i
;
256 // For each current prepopulated URL, check whether |template_urls| contained
257 // a matching prepopulated URL. If so, update the passed-in URL to match the
258 // current data. (If the passed-in URL was user-edited, we persist the user's
259 // name and keyword.) If not, add the prepopulated URL.
260 ActionsFromPrepopulateData actions
;
261 for (size_t i
= 0; i
< prepopulated_urls
->size(); ++i
) {
262 // We take ownership of |prepopulated_urls[i]|.
263 scoped_ptr
<TemplateURLData
> prepopulated_url((*prepopulated_urls
)[i
]);
264 const int prepopulated_id
= prepopulated_url
->prepopulate_id
;
265 DCHECK_NE(0, prepopulated_id
);
267 IDMap::iterator
existing_url_iter(id_to_turl
.find(prepopulated_id
));
268 if (existing_url_iter
!= id_to_turl
.end()) {
269 // Update the data store with the new prepopulated data. Preserve user
270 // edits to the name and keyword.
271 TemplateURL
* existing_url(existing_url_iter
->second
);
272 id_to_turl
.erase(existing_url_iter
);
273 MergeIntoPrepopulatedEngineData(existing_url
, prepopulated_url
.get());
274 // Update last_modified to ensure that if this entry is later merged with
275 // entries from Sync, the conflict resolution logic knows that this was
276 // updated and propagates the new values to the server.
277 prepopulated_url
->last_modified
= base::Time::Now();
278 actions
.edited_engines
.push_back(
279 std::make_pair(existing_url
, *prepopulated_url
));
281 actions
.added_engines
.push_back(*prepopulated_url
);
284 // The above loop takes ownership of all the contents of prepopulated_urls.
285 // Clear the pointers.
286 prepopulated_urls
->weak_erase(prepopulated_urls
->begin(),
287 prepopulated_urls
->end());
289 // The block above removed all the URLs from the |id_to_turl| map that were
290 // found in the prepopulate data. Any remaining URLs that haven't been
291 // user-edited or made default can be removed from the data store.
292 // We assume that this entry is equivalent to the DSE if its prepopulate ID
293 // and keyword both match. If the prepopulate ID _does_ match all properties
294 // will be replaced with those from |default_search_provider| anyway.
295 for (IDMap::iterator
i(id_to_turl
.begin()); i
!= id_to_turl
.end(); ++i
) {
296 TemplateURL
* template_url
= i
->second
;
297 if ((template_url
->safe_for_autoreplace()) &&
298 (!default_search_provider
||
299 (template_url
->prepopulate_id() !=
300 default_search_provider
->prepopulate_id()) ||
301 (template_url
->keyword() != default_search_provider
->keyword())))
302 actions
.removed_engines
.push_back(template_url
);
308 void GetSearchProvidersUsingKeywordResult(
309 const WDTypedResult
& result
,
310 KeywordWebDataService
* service
,
312 TemplateURLService::TemplateURLVector
* template_urls
,
313 TemplateURL
* default_search_provider
,
314 const SearchTermsData
& search_terms_data
,
315 int* new_resource_keyword_version
,
316 std::set
<std::string
>* removed_keyword_guids
) {
317 DCHECK(template_urls
);
318 DCHECK(template_urls
->empty());
319 DCHECK_EQ(KEYWORDS_RESULT
, result
.GetType());
320 DCHECK(new_resource_keyword_version
);
322 WDKeywordsResult keyword_result
= reinterpret_cast<
323 const WDResult
<WDKeywordsResult
>*>(&result
)->GetValue();
325 for (KeywordTable::Keywords::iterator
i(keyword_result
.keywords
.begin());
326 i
!= keyword_result
.keywords
.end(); ++i
) {
327 // Fix any duplicate encodings in the local database. Note that we don't
328 // adjust the last_modified time of this keyword; this way, we won't later
329 // overwrite any changes on the sync server that happened to this keyword
330 // since the last time we synced. Instead, we also run a de-duping pass on
331 // the server-provided data in
332 // TemplateURLService::CreateTemplateURLFromTemplateURLAndSyncData() and
333 // update the server with the merged, de-duped results at that time. We
334 // still fix here, though, to correct problems in clients that have disabled
335 // search engine sync, since in that case that code will never be reached.
336 if (DeDupeEncodings(&i
->input_encodings
) && service
)
337 service
->UpdateKeyword(*i
);
338 template_urls
->push_back(new TemplateURL(*i
));
341 *new_resource_keyword_version
= keyword_result
.builtin_keyword_version
;
342 GetSearchProvidersUsingLoadedEngines(service
, prefs
, template_urls
,
343 default_search_provider
,
345 new_resource_keyword_version
,
346 removed_keyword_guids
);
349 void GetSearchProvidersUsingLoadedEngines(
350 KeywordWebDataService
* service
,
352 TemplateURLService::TemplateURLVector
* template_urls
,
353 TemplateURL
* default_search_provider
,
354 const SearchTermsData
& search_terms_data
,
355 int* resource_keyword_version
,
356 std::set
<std::string
>* removed_keyword_guids
) {
357 DCHECK(template_urls
);
358 DCHECK(resource_keyword_version
);
359 size_t default_search_index
;
360 ScopedVector
<TemplateURLData
> prepopulated_urls
=
361 TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs
,
362 &default_search_index
);
363 RemoveDuplicatePrepopulateIDs(service
, prepopulated_urls
,
364 default_search_provider
, template_urls
,
365 search_terms_data
, removed_keyword_guids
);
367 const int prepopulate_resource_keyword_version
=
368 TemplateURLPrepopulateData::GetDataVersion(prefs
);
369 if (*resource_keyword_version
< prepopulate_resource_keyword_version
) {
370 MergeEnginesFromPrepopulateData(
371 service
, &prepopulated_urls
, default_search_index
, template_urls
,
372 default_search_provider
, removed_keyword_guids
);
373 *resource_keyword_version
= prepopulate_resource_keyword_version
;
375 *resource_keyword_version
= 0;
379 bool DeDupeEncodings(std::vector
<std::string
>* encodings
) {
380 std::vector
<std::string
> deduped_encodings
;
381 std::set
<std::string
> encoding_set
;
382 for (std::vector
<std::string
>::const_iterator
i(encodings
->begin());
383 i
!= encodings
->end(); ++i
) {
384 if (encoding_set
.insert(*i
).second
)
385 deduped_encodings
.push_back(*i
);
387 encodings
->swap(deduped_encodings
);
388 return encodings
->size() != deduped_encodings
.size();