1 // Copyright (c) 2011 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/search_engines/util.h"
6
7 #include <set>
8 #include <vector>
9
10 #include "base/logging.h"
11 #include "chrome/browser/search_engines/template_url.h"
12 #include "chrome/browser/search_engines/template_url_model.h"
13 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
14 #include "chrome/browser/prefs/pref_service.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "content/browser/browser_thread.h"
17
GetDefaultSearchEngineName(Profile * profile)18 string16 GetDefaultSearchEngineName(Profile* profile) {
19 if (!profile) {
20 NOTREACHED();
21 return string16();
22 }
23 const TemplateURL* const default_provider =
24 profile->GetTemplateURLModel()->GetDefaultSearchProvider();
25 if (!default_provider) {
26 // TODO(cpu): bug 1187517. It is possible to have no default provider.
27 // returning an empty string is a stopgap measure for the crash
28 // http://code.google.com/p/chromium/issues/detail?id=2573
29 return string16();
30 }
31 return default_provider->short_name();
32 }
33
34 // Removes (and deletes) TemplateURLs from |urls| that have duplicate
35 // prepopulate ids. Duplicate prepopulate ids are not allowed, but due to a
36 // bug it was possible get dups. This step is only called when the version
37 // number changes. Only pass in a non-NULL value for |service| if the removed
38 // items should be removed from the DB.
RemoveDuplicatePrepopulateIDs(std::vector<TemplateURL * > * template_urls,WebDataService * service)39 static void RemoveDuplicatePrepopulateIDs(
40 std::vector<TemplateURL*>* template_urls,
41 WebDataService* service) {
42 DCHECK(template_urls);
43 DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI));
44
45 std::set<int> ids;
46 for (std::vector<TemplateURL*>::iterator i = template_urls->begin();
47 i != template_urls->end(); ) {
48 int prepopulate_id = (*i)->prepopulate_id();
49 if (prepopulate_id) {
50 if (ids.find(prepopulate_id) != ids.end()) {
51 if (service)
52 service->RemoveKeyword(**i);
53 delete *i;
54 i = template_urls->erase(i);
55 } else {
56 ids.insert(prepopulate_id);
57 ++i;
58 }
59 } else {
60 ++i;
61 }
62 }
63 }
64
65 // Loads engines from prepopulate data and merges them in with the existing
66 // engines. This is invoked when the version of the prepopulate data changes.
MergeEnginesFromPrepopulateData(PrefService * prefs,WebDataService * service,std::vector<TemplateURL * > * template_urls,const TemplateURL ** default_search_provider)67 void MergeEnginesFromPrepopulateData(
68 PrefService* prefs,
69 WebDataService* service,
70 std::vector<TemplateURL*>* template_urls,
71 const TemplateURL** default_search_provider) {
72 DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI));
73 DCHECK(template_urls);
74 DCHECK(default_search_provider);
75
76 // Build a map from prepopulate id to TemplateURL of existing urls.
77 typedef std::map<int, TemplateURL*> IDMap;
78 IDMap id_to_turl;
79 for (std::vector<TemplateURL*>::iterator i(template_urls->begin());
80 i != template_urls->end(); ++i) {
81 int prepopulate_id = (*i)->prepopulate_id();
82 if (prepopulate_id > 0)
83 id_to_turl[prepopulate_id] = *i;
84 }
85
86 std::vector<TemplateURL*> prepopulated_urls;
87 size_t default_search_index;
88 TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs,
89 &prepopulated_urls,
90 &default_search_index);
91
92 std::set<int> updated_ids;
93 for (size_t i = 0; i < prepopulated_urls.size(); ++i) {
94 // We take ownership of |prepopulated_urls[i]|.
95 scoped_ptr<TemplateURL> prepopulated_url(prepopulated_urls[i]);
96 const int prepopulated_id = prepopulated_url->prepopulate_id();
97 if (!prepopulated_id || updated_ids.count(prepopulated_id)) {
98 // Prepopulate engines need a unique id.
99 NOTREACHED();
100 continue;
101 }
102
103 TemplateURL* existing_url = NULL;
104 IDMap::iterator existing_url_iter(id_to_turl.find(prepopulated_id));
105 if (existing_url_iter != id_to_turl.end()) {
106 existing_url = existing_url_iter->second;
107 if (!existing_url->safe_for_autoreplace()) {
108 // User edited the entry, preserve the keyword and description.
109 prepopulated_url->set_safe_for_autoreplace(false);
110 prepopulated_url->set_keyword(existing_url->keyword());
111 prepopulated_url->set_autogenerate_keyword(
112 existing_url->autogenerate_keyword());
113 prepopulated_url->set_short_name(existing_url->short_name());
114 }
115 prepopulated_url->set_id(existing_url->id());
116
117 *existing_url = *prepopulated_url;
118 if (service) {
119 service->UpdateKeyword(*existing_url);
120 }
121 id_to_turl.erase(existing_url_iter);
122 } else {
123 existing_url = prepopulated_url.get();
124 template_urls->push_back(prepopulated_url.release());
125 }
126 DCHECK(existing_url);
127 if (i == default_search_index && !*default_search_provider)
128 *default_search_provider = existing_url;
129
130 updated_ids.insert(prepopulated_id);
131 }
132
133 // Remove any prepopulated engines which are no longer in the master list, as
134 // long as the user hasn't modified them or made them the default engine.
135 for (IDMap::iterator i(id_to_turl.begin()); i != id_to_turl.end(); ++i) {
136 const TemplateURL* template_url = i->second;
137 if ((template_url->safe_for_autoreplace()) &&
138 (template_url != *default_search_provider)) {
139 std::vector<TemplateURL*>::iterator i = find(template_urls->begin(),
140 template_urls->end(),
141 template_url);
142 DCHECK(i != template_urls->end());
143 template_urls->erase(i);
144 if (service)
145 service->RemoveKeyword(*template_url);
146 delete template_url;
147 }
148 }
149 }
150
GetSearchProvidersUsingKeywordResult(const WDTypedResult & result,WebDataService * service,PrefService * prefs,std::vector<TemplateURL * > * template_urls,const TemplateURL ** default_search_provider,int * new_resource_keyword_version)151 void GetSearchProvidersUsingKeywordResult(
152 const WDTypedResult& result,
153 WebDataService* service,
154 PrefService* prefs,
155 std::vector<TemplateURL*>* template_urls,
156 const TemplateURL** default_search_provider,
157 int* new_resource_keyword_version) {
158 DCHECK(service == NULL || BrowserThread::CurrentlyOn(BrowserThread::UI));
159 DCHECK(template_urls);
160 DCHECK(template_urls->empty());
161 DCHECK(default_search_provider);
162 DCHECK(*default_search_provider == NULL);
163 DCHECK(result.GetType() == KEYWORDS_RESULT);
164 DCHECK(new_resource_keyword_version);
165
166 *new_resource_keyword_version = 0;
167 WDKeywordsResult keyword_result = reinterpret_cast<
168 const WDResult<WDKeywordsResult>*>(&result)->GetValue();
169
170 template_urls->swap(keyword_result.keywords);
171
172 const int resource_keyword_version =
173 TemplateURLPrepopulateData::GetDataVersion(prefs);
174 if (keyword_result.builtin_keyword_version != resource_keyword_version) {
175 // There should never be duplicate TemplateURLs. We had a bug such that
176 // duplicate TemplateURLs existed for one locale. As such we invoke
177 // RemoveDuplicatePrepopulateIDs to nuke the duplicates.
178 RemoveDuplicatePrepopulateIDs(template_urls, service);
179 }
180
181 if (keyword_result.default_search_provider_id) {
182 // See if we can find the default search provider.
183 for (std::vector<TemplateURL*>::iterator i = template_urls->begin();
184 i != template_urls->end(); ++i) {
185 if ((*i)->id() == keyword_result.default_search_provider_id) {
186 *default_search_provider = *i;
187 break;
188 }
189 }
190 }
191
192 if (keyword_result.builtin_keyword_version != resource_keyword_version) {
193 MergeEnginesFromPrepopulateData(prefs, service, template_urls,
194 default_search_provider);
195 *new_resource_keyword_version = resource_keyword_version;
196 }
197 }
198
199