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.
4
5 #include "components/search_engines/template_url_service.h"
6
7 #include <algorithm>
8
9 #include "base/auto_reset.h"
10 #include "base/base64.h"
11 #include "base/base64url.h"
12 #include "base/bind.h"
13 #include "base/callback.h"
14 #include "base/debug/crash_logging.h"
15 #include "base/format_macros.h"
16 #include "base/metrics/histogram_macros.h"
17 #include "base/rand_util.h"
18 #include "base/stl_util.h"
19 #include "base/strings/string_split.h"
20 #include "base/strings/string_util.h"
21 #include "base/strings/utf_string_conversions.h"
22 #include "components/pref_registry/pref_registry_syncable.h"
23 #include "components/prefs/pref_service.h"
24 #include "components/search_engines/search_engines_pref_names.h"
25 #include "components/search_engines/search_terms_data.h"
26 #include "components/search_engines/template_url_prepopulate_data.h"
27 #include "components/search_engines/template_url_service_client.h"
28 #include "components/search_engines/template_url_service_observer.h"
29 #include "components/search_engines/util.h"
30 #include "components/sync/model/sync_change.h"
31 #include "components/sync/model/sync_change_processor.h"
32 #include "components/sync/model/sync_error_factory.h"
33 #include "components/sync/protocol/search_engine_specifics.pb.h"
34 #include "components/sync/protocol/sync.pb.h"
35 #include "components/url_formatter/url_fixer.h"
36 #include "components/variations/variations_associated_data.h"
37 #include "net/base/registry_controlled_domains/registry_controlled_domain.h"
38 #include "url/gurl.h"
39
40 #if defined(OS_ANDROID)
41 #include "components/search_engines/android/template_url_service_android.h"
42 #endif
43
44 typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet;
45 typedef TemplateURLService::SyncDataMap SyncDataMap;
46
47 namespace {
48
49 const char kDeleteSyncedEngineHistogramName[] =
50 "Search.DeleteSyncedSearchEngine";
51
52 // Values for an enumerated histogram used to track whenever an ACTION_DELETE is
53 // sent to the server for search engines. These are persisted. Do not re-number.
54 enum DeleteSyncedSearchEngineEvent {
55 DELETE_ENGINE_USER_ACTION = 0,
56 DELETE_ENGINE_PRE_SYNC = 1,
57 DELETE_ENGINE_EMPTY_FIELD = 2,
58 DELETE_ENGINE_MAX,
59 };
60
61 const char kSearchTemplateURLEventsHistogramName[] =
62 "Search.TemplateURL.Events";
63
64 // Values for an enumerated histogram used to track TemplateURL edge cases.
65 // These are persisted. Do not re-number.
66 enum SearchTemplateURLEvent {
67 SYNC_DELETE_SUCCESS = 0,
68 SYNC_DELETE_FAIL_NONEXISTENT_ENGINE = 1,
69 SYNC_DELETE_FAIL_DEFAULT_SEARCH_PROVIDER = 2,
70 SEARCH_TEMPLATE_URL_EVENT_MAX,
71 };
72
73 // Returns true iff the change in |change_list| at index |i| should not be sent
74 // up to the server based on its GUIDs presence in |sync_data| or when compared
75 // to changes after it in |change_list|.
76 // The criteria is:
77 // 1) It is an ACTION_UPDATE or ACTION_DELETE and the sync_guid associated
78 // with it is NOT found in |sync_data|. We can only update and remove
79 // entries that were originally from the Sync server.
80 // 2) It is an ACTION_ADD and the sync_guid associated with it is found in
81 // |sync_data|. We cannot re-add entries that Sync already knew about.
82 // 3) There is an update after an update for the same GUID. We prune earlier
83 // ones just to save bandwidth (Sync would normally coalesce them).
ShouldRemoveSyncChange(size_t index,syncer::SyncChangeList * change_list,const SyncDataMap * sync_data)84 bool ShouldRemoveSyncChange(size_t index,
85 syncer::SyncChangeList* change_list,
86 const SyncDataMap* sync_data) {
87 DCHECK(index < change_list->size());
88 const syncer::SyncChange& change_i = (*change_list)[index];
89 const std::string guid = change_i.sync_data().GetSpecifics()
90 .search_engine().sync_guid();
91 syncer::SyncChange::SyncChangeType type = change_i.change_type();
92 if ((type == syncer::SyncChange::ACTION_UPDATE ||
93 type == syncer::SyncChange::ACTION_DELETE) &&
94 sync_data->find(guid) == sync_data->end())
95 return true;
96 if (type == syncer::SyncChange::ACTION_ADD &&
97 sync_data->find(guid) != sync_data->end())
98 return true;
99 if (type == syncer::SyncChange::ACTION_UPDATE) {
100 for (size_t j = index + 1; j < change_list->size(); j++) {
101 const syncer::SyncChange& change_j = (*change_list)[j];
102 if ((syncer::SyncChange::ACTION_UPDATE == change_j.change_type()) &&
103 (change_j.sync_data().GetSpecifics().search_engine().sync_guid() ==
104 guid))
105 return true;
106 }
107 }
108 return false;
109 }
110
111 // Remove SyncChanges that should not be sent to the server from |change_list|.
112 // This is done to eliminate incorrect SyncChanges added by the merge and
113 // conflict resolution logic when it is unsure of whether or not an entry is new
114 // from Sync or originally from the local model. This also removes changes that
115 // would be otherwise be coalesced by Sync in order to save bandwidth.
PruneSyncChanges(const SyncDataMap * sync_data,syncer::SyncChangeList * change_list)116 void PruneSyncChanges(const SyncDataMap* sync_data,
117 syncer::SyncChangeList* change_list) {
118 for (size_t i = 0; i < change_list->size(); ) {
119 if (ShouldRemoveSyncChange(i, change_list, sync_data))
120 change_list->erase(change_list->begin() + i);
121 else
122 ++i;
123 }
124 }
125
126 // Returns true if |turl|'s GUID is not found inside |sync_data|. This is to be
127 // used in MergeDataAndStartSyncing to differentiate between TemplateURLs from
128 // Sync and TemplateURLs that were initially local, assuming |sync_data| is the
129 // |initial_sync_data| parameter.
IsFromSync(const TemplateURL * turl,const SyncDataMap & sync_data)130 bool IsFromSync(const TemplateURL* turl, const SyncDataMap& sync_data) {
131 return base::Contains(sync_data, turl->sync_guid());
132 }
133
134 // Log the number of instances of a keyword that exist, with zero or more
135 // underscores, which could occur as the result of conflict resolution.
LogDuplicatesHistogram(const TemplateURLService::TemplateURLVector & template_urls)136 void LogDuplicatesHistogram(
137 const TemplateURLService::TemplateURLVector& template_urls) {
138 std::map<base::string16, int> duplicates;
139 for (auto it = template_urls.begin(); it != template_urls.end(); ++it) {
140 base::string16 keyword = (*it)->keyword();
141 base::TrimString(keyword, base::ASCIIToUTF16("_"), &keyword);
142 duplicates[keyword]++;
143 }
144
145 // Count the keywords with duplicates.
146 int num_dupes = 0;
147 for (std::map<base::string16, int>::const_iterator it = duplicates.begin();
148 it != duplicates.end(); ++it) {
149 if (it->second > 1)
150 num_dupes++;
151 }
152
153 UMA_HISTOGRAM_COUNTS_100("Search.SearchEngineDuplicateCounts", num_dupes);
154 }
155
156 // Returns the length of the registry portion of a hostname. For example,
157 // www.google.co.uk will return 5 (the length of co.uk).
GetRegistryLength(const base::string16 & host)158 size_t GetRegistryLength(const base::string16& host) {
159 return net::registry_controlled_domains::PermissiveGetHostRegistryLength(
160 host, net::registry_controlled_domains::EXCLUDE_UNKNOWN_REGISTRIES,
161 net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES);
162 }
163
164 // For keywords that look like hostnames, returns whether KeywordProvider
165 // should require users to type a prefix of the hostname to match against
166 // them, rather than just the domain name portion. In other words, returns
167 // whether the prefix before the domain name should be considered important
168 // for matching purposes. Returns true if the experiment isn't active.
OmniboxFieldTrialKeywordRequiresRegistry()169 bool OmniboxFieldTrialKeywordRequiresRegistry() {
170 // This would normally be
171 // bool OmniboxFieldTrial::KeywordRequiresRegistry()
172 // but that would create a dependency cycle since omnibox depends on
173 // search_engines (and search -> search_engines)
174 constexpr char kBundledExperimentFieldTrialName[] =
175 "OmniboxBundledExperimentV1";
176 constexpr char kKeywordRequiresRegistryRule[] = "KeywordRequiresRegistry";
177 const std::string value = variations::GetVariationParamValue(
178 kBundledExperimentFieldTrialName, kKeywordRequiresRegistryRule);
179 return value.empty() || (value == "true");
180 }
181
182 // Returns the length of the important part of the |keyword|, assumed to be
183 // associated with the TemplateURL. For instance, for the keyword
184 // google.co.uk, this can return 6 (the length of "google").
GetMeaningfulKeywordLength(const base::string16 & keyword,const TemplateURL * turl)185 size_t GetMeaningfulKeywordLength(const base::string16& keyword,
186 const TemplateURL* turl) {
187 // Using Omnibox from here is a layer violation and should be fixed.
188 if (OmniboxFieldTrialKeywordRequiresRegistry())
189 return keyword.length();
190 const size_t registry_length = GetRegistryLength(keyword);
191 if (registry_length == std::string::npos)
192 return keyword.length();
193 DCHECK_LT(registry_length, keyword.length());
194 // The meaningful keyword length is the length of any portion before the
195 // registry ("co.uk") and its preceding dot.
196 return keyword.length() - (registry_length ? (registry_length + 1) : 0);
197 }
198
Contains(TemplateURLService::OwnedTemplateURLVector * template_urls,const TemplateURL * turl)199 bool Contains(TemplateURLService::OwnedTemplateURLVector* template_urls,
200 const TemplateURL* turl) {
201 return FindTemplateURL(template_urls, turl) != template_urls->end();
202 }
203
IsCreatedByExtension(const TemplateURL * template_url)204 bool IsCreatedByExtension(const TemplateURL* template_url) {
205 return template_url->type() ==
206 TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION ||
207 template_url->type() == TemplateURL::OMNIBOX_API_EXTENSION;
208 }
209
210 } // namespace
211
212 // TemplateURLService::LessWithPrefix -----------------------------------------
213
214 class TemplateURLService::LessWithPrefix {
215 public:
216 // We want to find the set of keywords that begin with a prefix. The STL
217 // algorithms will return the set of elements that are "equal to" the
218 // prefix, where "equal(x, y)" means "!(cmp(x, y) || cmp(y, x))". When
219 // cmp() is the typical std::less<>, this results in lexicographic equality;
220 // we need to extend this to mark a prefix as "not less than" a keyword it
221 // begins, which will cause the desired elements to be considered "equal to"
222 // the prefix. Note: this is still a strict weak ordering, as required by
223 // equal_range() (though I will not prove that here).
224 //
225 // Unfortunately the calling convention is not "prefix and element" but
226 // rather "two elements", so we pass the prefix as a fake "element" which has
227 // a NULL KeywordDataElement pointer.
operator ()(const KeywordToTURLAndMeaningfulLength::value_type & elem1,const KeywordToTURLAndMeaningfulLength::value_type & elem2) const228 bool operator()(
229 const KeywordToTURLAndMeaningfulLength::value_type& elem1,
230 const KeywordToTURLAndMeaningfulLength::value_type& elem2) const {
231 return (elem1.second.first == nullptr)
232 ? (elem2.first.compare(0, elem1.first.length(), elem1.first) > 0)
233 : (elem1.first < elem2.first);
234 }
235 };
236
237 // TemplateURLService::Scoper -------------------------------------------------
238
239 class TemplateURLService::Scoper {
240 public:
241 // Keep one of these handles in scope to coalesce all the notifications into a
242 // single notification. Likewise, BatchModeScoper defers web data service
243 // operations into a batch operation.
244 //
245 // Notifications are sent when the last outstanding handle is destroyed and
246 // |model_mutated_notification_pending_| is true.
247 //
248 // The web data service batch operation is performed when the batch mode level
249 // is 0 and more than one operation is pending. This check happens when
250 // BatchModeScoper is destroyed.
Scoper(TemplateURLService * service)251 explicit Scoper(TemplateURLService* service)
252 : batch_mode_scoper_(
253 std::make_unique<KeywordWebDataService::BatchModeScoper>(
254 service->web_data_service_.get())),
255 service_(service) {
256 ++service_->outstanding_scoper_handles_;
257 }
258
259 // When a Scoper is destroyed, the handle count is updated. If the handle
260 // count is at zero, notify the observers that the model has changed if
261 // service is loaded and model was mutated.
~Scoper()262 ~Scoper() {
263 DCHECK_GT(service_->outstanding_scoper_handles_, 0);
264
265 --service_->outstanding_scoper_handles_;
266 if (service_->outstanding_scoper_handles_ == 0 &&
267 service_->model_mutated_notification_pending_) {
268 service_->model_mutated_notification_pending_ = false;
269
270 if (!service_->loaded_)
271 return;
272
273 for (auto& observer : service_->model_observers_)
274 observer.OnTemplateURLServiceChanged();
275 }
276 }
277
278 private:
279 std::unique_ptr<KeywordWebDataService::BatchModeScoper> batch_mode_scoper_;
280 TemplateURLService* service_;
281
282 DISALLOW_COPY_AND_ASSIGN(Scoper);
283 };
284
285 // TemplateURLService ---------------------------------------------------------
286
TemplateURLService(PrefService * prefs,std::unique_ptr<SearchTermsData> search_terms_data,const scoped_refptr<KeywordWebDataService> & web_data_service,std::unique_ptr<TemplateURLServiceClient> client,const base::Closure & dsp_change_callback)287 TemplateURLService::TemplateURLService(
288 PrefService* prefs,
289 std::unique_ptr<SearchTermsData> search_terms_data,
290 const scoped_refptr<KeywordWebDataService>& web_data_service,
291 std::unique_ptr<TemplateURLServiceClient> client,
292 const base::Closure& dsp_change_callback)
293 : prefs_(prefs),
294 search_terms_data_(std::move(search_terms_data)),
295 web_data_service_(web_data_service),
296 client_(std::move(client)),
297 dsp_change_callback_(dsp_change_callback),
298 default_search_manager_(
299 prefs_,
300 base::BindRepeating(&TemplateURLService::ApplyDefaultSearchChange,
301 base::Unretained(this))) {
302 DCHECK(search_terms_data_);
303 Init(nullptr, 0);
304 }
305
TemplateURLService(const Initializer * initializers,const int count)306 TemplateURLService::TemplateURLService(const Initializer* initializers,
307 const int count)
308 : default_search_manager_(
309 prefs_,
310 base::BindRepeating(&TemplateURLService::ApplyDefaultSearchChange,
311 base::Unretained(this))) {
312 Init(initializers, count);
313 }
314
~TemplateURLService()315 TemplateURLService::~TemplateURLService() {
316 // |web_data_service_| should be deleted during Shutdown().
317 DCHECK(!web_data_service_);
318 }
319
320 // static
RegisterProfilePrefs(user_prefs::PrefRegistrySyncable * registry)321 void TemplateURLService::RegisterProfilePrefs(
322 user_prefs::PrefRegistrySyncable* registry) {
323 #if defined(OS_IOS) || defined(OS_ANDROID)
324 uint32_t flags = PrefRegistry::NO_REGISTRATION_FLAGS;
325 #else
326 uint32_t flags = user_prefs::PrefRegistrySyncable::SYNCABLE_PREF;
327 #endif
328 registry->RegisterStringPref(prefs::kSyncedDefaultSearchProviderGUID,
329 std::string(),
330 flags);
331 registry->RegisterBooleanPref(prefs::kDefaultSearchProviderEnabled, true);
332 registry->RegisterBooleanPref(
333 prefs::kDefaultSearchProviderContextMenuAccessAllowed, true);
334 }
335
336 #if defined(OS_ANDROID)
GetJavaObject()337 base::android::ScopedJavaLocalRef<jobject> TemplateURLService::GetJavaObject() {
338 if (!template_url_service_android_) {
339 template_url_service_android_ =
340 std::make_unique<TemplateUrlServiceAndroid>(this);
341 }
342 return template_url_service_android_->GetJavaObject();
343 }
344 #endif
345
CanAddAutogeneratedKeyword(const base::string16 & keyword,const GURL & url,const TemplateURL ** template_url_to_replace)346 bool TemplateURLService::CanAddAutogeneratedKeyword(
347 const base::string16& keyword,
348 const GURL& url,
349 const TemplateURL** template_url_to_replace) {
350 DCHECK(!keyword.empty()); // This should only be called for non-empty
351 // keywords. If we need to support empty kewords
352 // the code needs to change slightly.
353 const TemplateURL* existing_url = GetTemplateURLForKeyword(keyword);
354 if (template_url_to_replace)
355 *template_url_to_replace = existing_url;
356 if (existing_url) {
357 // We already have a TemplateURL for this keyword. Only allow it to be
358 // replaced if the TemplateURL can be replaced.
359 return CanReplace(existing_url);
360 }
361
362 // We don't have a TemplateURL with keyword. We still may not allow this
363 // keyword if there's evidence we may have created this keyword before and
364 // the user renamed it (because, for instance, the keyword is a common word
365 // that may interfere with search queries). An easy heuristic for this is
366 // whether the user has a TemplateURL that has been manually modified (e.g.,
367 // renamed) connected to the same host.
368 return !url.is_valid() || url.host().empty() ||
369 CanAddAutogeneratedKeywordForHost(url.host());
370 }
371
IsPrepopulatedOrCreatedByPolicy(const TemplateURL * t_url) const372 bool TemplateURLService::IsPrepopulatedOrCreatedByPolicy(
373 const TemplateURL* t_url) const {
374 return (t_url->prepopulate_id() > 0 || t_url->created_by_policy() ||
375 t_url->created_from_play_api()) &&
376 t_url->SupportsReplacement(search_terms_data());
377 }
378
ShowInDefaultList(const TemplateURL * t_url) const379 bool TemplateURLService::ShowInDefaultList(const TemplateURL* t_url) const {
380 return t_url == default_search_provider_ ||
381 IsPrepopulatedOrCreatedByPolicy(t_url);
382 }
383
AddMatchingKeywords(const base::string16 & prefix,bool supports_replacement_only,TURLsAndMeaningfulLengths * matches)384 void TemplateURLService::AddMatchingKeywords(
385 const base::string16& prefix,
386 bool supports_replacement_only,
387 TURLsAndMeaningfulLengths* matches) {
388 AddMatchingKeywordsHelper(
389 keyword_to_turl_and_length_, prefix, supports_replacement_only, matches);
390 }
391
GetTemplateURLForKeyword(const base::string16 & keyword)392 TemplateURL* TemplateURLService::GetTemplateURLForKeyword(
393 const base::string16& keyword) {
394 return const_cast<TemplateURL*>(
395 static_cast<const TemplateURLService*>(this)->
396 GetTemplateURLForKeyword(keyword));
397 }
398
GetTemplateURLForKeyword(const base::string16 & keyword) const399 const TemplateURL* TemplateURLService::GetTemplateURLForKeyword(
400 const base::string16& keyword) const {
401 // Finds and returns the best match for |keyword|.
402 const auto match_range = keyword_to_turl_and_length_.equal_range(keyword);
403 if (match_range.first != match_range.second) {
404 // Among the matches for |keyword| in the multimap, return the best one.
405 return std::min_element(
406 match_range.first, match_range.second,
407 [](const auto& a, const auto& b) {
408 return a.second.first
409 ->IsBetterThanEngineWithConflictingKeyword(b.second.first);
410 })
411 ->second.first;
412 }
413
414 return (!loaded_ && initial_default_search_provider_ &&
415 (initial_default_search_provider_->keyword() == keyword))
416 ? initial_default_search_provider_.get()
417 : nullptr;
418 }
419
GetTemplateURLForGUID(const std::string & sync_guid)420 TemplateURL* TemplateURLService::GetTemplateURLForGUID(
421 const std::string& sync_guid) {
422 return const_cast<TemplateURL*>(
423 static_cast<const TemplateURLService*>(this)->
424 GetTemplateURLForGUID(sync_guid));
425 }
426
GetTemplateURLForGUID(const std::string & sync_guid) const427 const TemplateURL* TemplateURLService::GetTemplateURLForGUID(
428 const std::string& sync_guid) const {
429 auto elem(guid_to_turl_.find(sync_guid));
430 if (elem != guid_to_turl_.end())
431 return elem->second;
432 return (!loaded_ && initial_default_search_provider_ &&
433 (initial_default_search_provider_->sync_guid() == sync_guid))
434 ? initial_default_search_provider_.get()
435 : nullptr;
436 }
437
GetTemplateURLForHost(const std::string & host)438 TemplateURL* TemplateURLService::GetTemplateURLForHost(
439 const std::string& host) {
440 return const_cast<TemplateURL*>(
441 static_cast<const TemplateURLService*>(this)->
442 GetTemplateURLForHost(host));
443 }
444
GetTemplateURLForHost(const std::string & host) const445 const TemplateURL* TemplateURLService::GetTemplateURLForHost(
446 const std::string& host) const {
447 if (loaded_)
448 return provider_map_->GetTemplateURLForHost(host);
449 TemplateURL* initial_dsp = initial_default_search_provider_.get();
450 return (initial_dsp &&
451 (initial_dsp->GenerateSearchURL(search_terms_data()).host_piece() ==
452 host))
453 ? initial_dsp
454 : nullptr;
455 }
456
Add(std::unique_ptr<TemplateURL> template_url)457 TemplateURL* TemplateURLService::Add(
458 std::unique_ptr<TemplateURL> template_url) {
459 DCHECK(template_url);
460 DCHECK(
461 !IsCreatedByExtension(template_url.get()) ||
462 (!FindTemplateURLForExtension(template_url->extension_info_->extension_id,
463 template_url->type()) &&
464 template_url->id() == kInvalidTemplateURLID));
465
466 return Add(std::move(template_url), true);
467 }
468
AddWithOverrides(std::unique_ptr<TemplateURL> template_url,const base::string16 & short_name,const base::string16 & keyword,const std::string & url)469 TemplateURL* TemplateURLService::AddWithOverrides(
470 std::unique_ptr<TemplateURL> template_url,
471 const base::string16& short_name,
472 const base::string16& keyword,
473 const std::string& url) {
474 DCHECK(!short_name.empty());
475 DCHECK(!keyword.empty());
476 DCHECK(!url.empty());
477 template_url->data_.SetShortName(short_name);
478 template_url->data_.SetKeyword(keyword);
479 template_url->SetURL(url);
480 return Add(std::move(template_url));
481 }
482
Remove(const TemplateURL * template_url)483 void TemplateURLService::Remove(const TemplateURL* template_url) {
484 DCHECK_NE(template_url, default_search_provider_);
485
486 auto i = FindTemplateURL(&template_urls_, template_url);
487 if (i == template_urls_.end())
488 return;
489
490 Scoper scoper(this);
491 model_mutated_notification_pending_ = true;
492
493 RemoveFromMaps(template_url);
494
495 // Remove it from the vector containing all TemplateURLs.
496 std::unique_ptr<TemplateURL> scoped_turl = std::move(*i);
497 template_urls_.erase(i);
498
499 if (template_url->type() == TemplateURL::NORMAL) {
500 if (web_data_service_)
501 web_data_service_->RemoveKeyword(template_url->id());
502
503 // Inform sync of the deletion.
504 ProcessTemplateURLChange(FROM_HERE, template_url,
505 syncer::SyncChange::ACTION_DELETE);
506
507 // The default search engine can't be deleted. But the user defined DSE can
508 // be hidden by an extension or policy and then deleted. Clean up the user
509 // prefs then.
510 if (prefs_ &&
511 (template_url->sync_guid() ==
512 prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID))) {
513 prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID, std::string());
514 }
515
516 UMA_HISTOGRAM_ENUMERATION(kDeleteSyncedEngineHistogramName,
517 DELETE_ENGINE_USER_ACTION, DELETE_ENGINE_MAX);
518 }
519
520 if (loaded_ && client_)
521 client_->DeleteAllSearchTermsForKeyword(template_url->id());
522 }
523
RemoveExtensionControlledTURL(const std::string & extension_id,TemplateURL::Type type)524 void TemplateURLService::RemoveExtensionControlledTURL(
525 const std::string& extension_id,
526 TemplateURL::Type type) {
527 TemplateURL* url = FindTemplateURLForExtension(extension_id, type);
528 if (!url)
529 return;
530 // NULL this out so that we can call Remove.
531 if (default_search_provider_ == url)
532 default_search_provider_ = nullptr;
533 Remove(url);
534 }
535
RemoveAutoGeneratedSince(base::Time created_after)536 void TemplateURLService::RemoveAutoGeneratedSince(base::Time created_after) {
537 RemoveAutoGeneratedBetween(created_after, base::Time());
538 }
539
RemoveAutoGeneratedBetween(base::Time created_after,base::Time created_before)540 void TemplateURLService::RemoveAutoGeneratedBetween(base::Time created_after,
541 base::Time created_before) {
542 RemoveAutoGeneratedForUrlsBetween(base::Callback<bool(const GURL&)>(),
543 created_after, created_before);
544 }
545
RemoveAutoGeneratedForUrlsBetween(const base::Callback<bool (const GURL &)> & url_filter,base::Time created_after,base::Time created_before)546 void TemplateURLService::RemoveAutoGeneratedForUrlsBetween(
547 const base::Callback<bool(const GURL&)>& url_filter,
548 base::Time created_after,
549 base::Time created_before) {
550 Scoper scoper(this);
551
552 for (size_t i = 0; i < template_urls_.size();) {
553 if (template_urls_[i]->date_created() >= created_after &&
554 (created_before.is_null() ||
555 template_urls_[i]->date_created() < created_before) &&
556 CanReplace(template_urls_[i].get()) &&
557 (url_filter.is_null() ||
558 url_filter.Run(
559 template_urls_[i]->GenerateSearchURL(search_terms_data())))) {
560 Remove(template_urls_[i].get());
561 } else {
562 ++i;
563 }
564 }
565 }
566
RegisterOmniboxKeyword(const std::string & extension_id,const std::string & extension_name,const std::string & keyword,const std::string & template_url_string,const base::Time & extension_install_time)567 void TemplateURLService::RegisterOmniboxKeyword(
568 const std::string& extension_id,
569 const std::string& extension_name,
570 const std::string& keyword,
571 const std::string& template_url_string,
572 const base::Time& extension_install_time) {
573 DCHECK(loaded_);
574
575 if (FindTemplateURLForExtension(extension_id,
576 TemplateURL::OMNIBOX_API_EXTENSION))
577 return;
578
579 TemplateURLData data;
580 data.SetShortName(base::UTF8ToUTF16(extension_name));
581 data.SetKeyword(base::UTF8ToUTF16(keyword));
582 data.SetURL(template_url_string);
583 Add(std::make_unique<TemplateURL>(data, TemplateURL::OMNIBOX_API_EXTENSION,
584 extension_id, extension_install_time,
585 false));
586 }
587
GetTemplateURLs()588 TemplateURLService::TemplateURLVector TemplateURLService::GetTemplateURLs() {
589 TemplateURLVector result;
590 for (const auto& turl : template_urls_)
591 result.push_back(turl.get());
592 return result;
593 }
594
IncrementUsageCount(TemplateURL * url)595 void TemplateURLService::IncrementUsageCount(TemplateURL* url) {
596 DCHECK(url);
597 // Extension-controlled search engines are not persisted.
598 if (url->type() != TemplateURL::NORMAL)
599 return;
600 if (!Contains(&template_urls_, url))
601 return;
602 ++url->data_.usage_count;
603
604 if (web_data_service_)
605 web_data_service_->UpdateKeyword(url->data());
606 }
607
ResetTemplateURL(TemplateURL * url,const base::string16 & title,const base::string16 & keyword,const std::string & search_url)608 void TemplateURLService::ResetTemplateURL(TemplateURL* url,
609 const base::string16& title,
610 const base::string16& keyword,
611 const std::string& search_url) {
612 DCHECK(!IsCreatedByExtension(url));
613 DCHECK(!keyword.empty());
614 DCHECK(!search_url.empty());
615 TemplateURLData data(url->data());
616 data.SetShortName(title);
617 data.SetKeyword(keyword);
618 if (search_url != data.url()) {
619 data.SetURL(search_url);
620 // The urls have changed, reset the favicon url.
621 data.favicon_url = GURL();
622 }
623 data.safe_for_autoreplace = false;
624 data.last_modified = clock_->Now();
625 Update(url, TemplateURL(data));
626 }
627
CreateOrUpdateTemplateURLFromPlayAPIData(const base::string16 & title,const base::string16 & keyword,const std::string & search_url,const std::string & suggestions_url,const std::string & favicon_url)628 TemplateURL* TemplateURLService::CreateOrUpdateTemplateURLFromPlayAPIData(
629 const base::string16& title,
630 const base::string16& keyword,
631 const std::string& search_url,
632 const std::string& suggestions_url,
633 const std::string& favicon_url) {
634 TemplateURL* existing_turl = FindNonExtensionTemplateURLForKeyword(keyword);
635 TemplateURLData data;
636 if (existing_turl)
637 data = existing_turl->data();
638 data.SetShortName(title);
639 data.SetKeyword(keyword);
640 data.SetURL(search_url);
641 data.suggestions_url = suggestions_url;
642 data.favicon_url = GURL(favicon_url);
643 data.safe_for_autoreplace = true;
644 data.created_from_play_api = true;
645 if (existing_turl) {
646 Update(existing_turl, TemplateURL(data));
647 } else {
648 existing_turl = Add(std::make_unique<TemplateURL>(data));
649 DCHECK(existing_turl);
650 }
651 return existing_turl;
652 }
653
UpdateProviderFavicons(const GURL & potential_search_url,const GURL & favicon_url)654 void TemplateURLService::UpdateProviderFavicons(
655 const GURL& potential_search_url,
656 const GURL& favicon_url) {
657 DCHECK(loaded_);
658 DCHECK(potential_search_url.is_valid());
659
660 const TemplateURLSet* urls_for_host =
661 provider_map_->GetURLsForHost(potential_search_url.host());
662 if (!urls_for_host)
663 return;
664
665 // Make a copy of the container of the matching TemplateURLs, as the original
666 // container is invalidated as we update the contained TemplateURLs.
667 TemplateURLSet urls_for_host_copy(*urls_for_host);
668
669 Scoper scoper(this);
670 for (TemplateURL* turl : urls_for_host_copy) {
671 if (!IsCreatedByExtension(turl) &&
672 turl->IsSearchURL(potential_search_url, search_terms_data()) &&
673 turl->favicon_url() != favicon_url) {
674 TemplateURLData data(turl->data());
675 data.favicon_url = favicon_url;
676 Update(turl, TemplateURL(data));
677 }
678 }
679 }
680
CanMakeDefault(const TemplateURL * url) const681 bool TemplateURLService::CanMakeDefault(const TemplateURL* url) const {
682 return
683 ((default_search_provider_source_ == DefaultSearchManager::FROM_USER) ||
684 (default_search_provider_source_ ==
685 DefaultSearchManager::FROM_FALLBACK)) &&
686 (url != GetDefaultSearchProvider()) &&
687 url->url_ref().SupportsReplacement(search_terms_data()) &&
688 (url->type() == TemplateURL::NORMAL);
689 }
690
SetUserSelectedDefaultSearchProvider(TemplateURL * url)691 void TemplateURLService::SetUserSelectedDefaultSearchProvider(
692 TemplateURL* url) {
693 // Omnibox keywords cannot be made default. Extension-controlled search
694 // engines can be made default only by the extension itself because they
695 // aren't persisted.
696 DCHECK(!url || !IsCreatedByExtension(url));
697 if (load_failed_) {
698 // Skip the DefaultSearchManager, which will persist to user preferences.
699 if ((default_search_provider_source_ == DefaultSearchManager::FROM_USER) ||
700 (default_search_provider_source_ ==
701 DefaultSearchManager::FROM_FALLBACK)) {
702 ApplyDefaultSearchChange(url ? &url->data() : nullptr,
703 DefaultSearchManager::FROM_USER);
704 }
705 } else {
706 // We rely on the DefaultSearchManager to call ApplyDefaultSearchChange if,
707 // in fact, the effective DSE changes.
708 if (url)
709 default_search_manager_.SetUserSelectedDefaultSearchEngine(url->data());
710 else
711 default_search_manager_.ClearUserSelectedDefaultSearchEngine();
712 }
713 }
714
GetDefaultSearchProvider() const715 const TemplateURL* TemplateURLService::GetDefaultSearchProvider() const {
716 return loaded_ ? default_search_provider_
717 : initial_default_search_provider_.get();
718 }
719
720 const TemplateURL*
GetDefaultSearchProviderIgnoringExtensions() const721 TemplateURLService::GetDefaultSearchProviderIgnoringExtensions() const {
722 std::unique_ptr<TemplateURLData> next_search =
723 default_search_manager_.GetDefaultSearchEngineIgnoringExtensions();
724 if (!next_search)
725 return nullptr;
726
727 // Find the TemplateURL matching the data retrieved.
728 auto iter = std::find_if(template_urls_.begin(), template_urls_.end(),
729 [this, &next_search](const auto& turl_to_check) {
730 return TemplateURL::MatchesData(
731 turl_to_check.get(), next_search.get(),
732 search_terms_data());
733 });
734 return iter == template_urls_.end() ? nullptr : iter->get();
735 }
736
IsSearchResultsPageFromDefaultSearchProvider(const GURL & url) const737 bool TemplateURLService::IsSearchResultsPageFromDefaultSearchProvider(
738 const GURL& url) const {
739 const TemplateURL* default_provider = GetDefaultSearchProvider();
740 return default_provider &&
741 default_provider->IsSearchURL(url, search_terms_data());
742 }
743
IsExtensionControlledDefaultSearch() const744 bool TemplateURLService::IsExtensionControlledDefaultSearch() const {
745 return default_search_provider_source_ ==
746 DefaultSearchManager::FROM_EXTENSION;
747 }
748
RepairPrepopulatedSearchEngines()749 void TemplateURLService::RepairPrepopulatedSearchEngines() {
750 // Can't clean DB if it hasn't been loaded.
751 DCHECK(loaded());
752
753 Scoper scoper(this);
754
755 if ((default_search_provider_source_ == DefaultSearchManager::FROM_USER) ||
756 (default_search_provider_source_ ==
757 DefaultSearchManager::FROM_FALLBACK)) {
758 // Clear |default_search_provider_| in case we want to remove the engine it
759 // points to. This will get reset at the end of the function anyway.
760 default_search_provider_ = nullptr;
761 }
762
763 std::vector<std::unique_ptr<TemplateURLData>> prepopulated_urls =
764 TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs_, nullptr);
765 DCHECK(!prepopulated_urls.empty());
766 ActionsFromPrepopulateData actions(CreateActionsFromCurrentPrepopulateData(
767 &prepopulated_urls, template_urls_, default_search_provider_));
768
769 // Remove items.
770 for (auto i = actions.removed_engines.begin();
771 i < actions.removed_engines.end(); ++i)
772 Remove(*i);
773
774 // Edit items.
775 for (auto i(actions.edited_engines.begin()); i < actions.edited_engines.end();
776 ++i) {
777 TemplateURL new_values(i->second);
778 Update(i->first, new_values);
779 }
780
781 // Add items.
782 for (std::vector<TemplateURLData>::const_iterator i =
783 actions.added_engines.begin();
784 i < actions.added_engines.end();
785 ++i) {
786 Add(std::make_unique<TemplateURL>(*i));
787 }
788
789 base::AutoReset<DefaultSearchChangeOrigin> change_origin(
790 &dsp_change_origin_, DSP_CHANGE_PROFILE_RESET);
791
792 default_search_manager_.ClearUserSelectedDefaultSearchEngine();
793
794 if (default_search_provider_) {
795 // Set fallback engine as user selected, because repair is considered a user
796 // action and we are expected to sync new fallback engine to other devices.
797 const TemplateURLData* fallback_engine_data =
798 default_search_manager_.GetFallbackSearchEngine();
799 if (fallback_engine_data) {
800 TemplateURL* fallback_engine =
801 FindPrepopulatedTemplateURL(fallback_engine_data->prepopulate_id);
802 // The fallback engine is created from built-in/override data that should
803 // always have a prepopulate ID, so this engine should always exist after
804 // a repair.
805 DCHECK(fallback_engine);
806 // Write the fallback engine's GUID to prefs, which will cause
807 // OnSyncedDefaultSearchProviderGUIDChanged() to set it as the new
808 // user-selected engine.
809 prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID,
810 fallback_engine->sync_guid());
811 }
812 } else {
813 // If the default search provider came from a user pref we would have been
814 // notified of the new (fallback-provided) value in
815 // ClearUserSelectedDefaultSearchEngine() above. Since we are here, the
816 // value was presumably originally a fallback value (which may have been
817 // repaired).
818 DefaultSearchManager::Source source;
819 const TemplateURLData* new_dse =
820 default_search_manager_.GetDefaultSearchEngine(&source);
821 ApplyDefaultSearchChange(new_dse, source);
822 }
823 }
824
AddObserver(TemplateURLServiceObserver * observer)825 void TemplateURLService::AddObserver(TemplateURLServiceObserver* observer) {
826 model_observers_.AddObserver(observer);
827 }
828
RemoveObserver(TemplateURLServiceObserver * observer)829 void TemplateURLService::RemoveObserver(TemplateURLServiceObserver* observer) {
830 model_observers_.RemoveObserver(observer);
831 }
832
Load()833 void TemplateURLService::Load() {
834 if (loaded_ || load_handle_ || disable_load_)
835 return;
836
837 if (web_data_service_)
838 load_handle_ = web_data_service_->GetKeywords(this);
839 else
840 ChangeToLoadedState();
841 }
842
843 std::unique_ptr<TemplateURLService::Subscription>
RegisterOnLoadedCallback(const base::RepeatingClosure & callback)844 TemplateURLService::RegisterOnLoadedCallback(
845 const base::RepeatingClosure& callback) {
846 return loaded_ ? std::unique_ptr<TemplateURLService::Subscription>()
847 : on_loaded_callbacks_.Add(callback);
848 }
849
OnWebDataServiceRequestDone(KeywordWebDataService::Handle h,std::unique_ptr<WDTypedResult> result)850 void TemplateURLService::OnWebDataServiceRequestDone(
851 KeywordWebDataService::Handle h,
852 std::unique_ptr<WDTypedResult> result) {
853 // Reset the load_handle so that we don't try and cancel the load in
854 // the destructor.
855 load_handle_ = 0;
856
857 if (!result) {
858 // Results are null if the database went away or (most likely) wasn't
859 // loaded.
860 load_failed_ = true;
861 web_data_service_ = nullptr;
862 ChangeToLoadedState();
863 return;
864 }
865
866 std::unique_ptr<OwnedTemplateURLVector> template_urls =
867 std::make_unique<OwnedTemplateURLVector>();
868 int new_resource_keyword_version = 0;
869 {
870 GetSearchProvidersUsingKeywordResult(
871 *result, web_data_service_.get(), prefs_, template_urls.get(),
872 (default_search_provider_source_ == DefaultSearchManager::FROM_USER)
873 ? initial_default_search_provider_.get()
874 : nullptr,
875 search_terms_data(), &new_resource_keyword_version, &pre_sync_deletes_);
876 }
877
878 Scoper scoper(this);
879
880 {
881 PatchMissingSyncGUIDs(template_urls.get());
882 SetTemplateURLs(std::move(template_urls));
883
884 // This initializes provider_map_ which should be done before
885 // calling UpdateKeywordSearchTermsForURL.
886 ChangeToLoadedState();
887
888 // Index any visits that occurred before we finished loading.
889 for (size_t i = 0; i < visits_to_add_.size(); ++i)
890 UpdateKeywordSearchTermsForURL(visits_to_add_[i]);
891 visits_to_add_.clear();
892
893 if (new_resource_keyword_version)
894 web_data_service_->SetBuiltinKeywordVersion(new_resource_keyword_version);
895 }
896
897 if (default_search_provider_) {
898 UMA_HISTOGRAM_ENUMERATION(
899 "Search.DefaultSearchProviderType",
900 default_search_provider_->GetEngineType(search_terms_data()),
901 SEARCH_ENGINE_MAX);
902 }
903 }
904
GetKeywordShortName(const base::string16 & keyword,bool * is_omnibox_api_extension_keyword) const905 base::string16 TemplateURLService::GetKeywordShortName(
906 const base::string16& keyword,
907 bool* is_omnibox_api_extension_keyword) const {
908 const TemplateURL* template_url = GetTemplateURLForKeyword(keyword);
909
910 // TODO(sky): Once LocationBarView adds a listener to the TemplateURLService
911 // to track changes to the model, this should become a DCHECK.
912 if (template_url) {
913 *is_omnibox_api_extension_keyword =
914 template_url->type() == TemplateURL::OMNIBOX_API_EXTENSION;
915 return template_url->AdjustedShortNameForLocaleDirection();
916 }
917 *is_omnibox_api_extension_keyword = false;
918 return base::string16();
919 }
920
OnHistoryURLVisited(const URLVisitedDetails & details)921 void TemplateURLService::OnHistoryURLVisited(const URLVisitedDetails& details) {
922 if (!loaded_)
923 visits_to_add_.push_back(details);
924 else
925 UpdateKeywordSearchTermsForURL(details);
926 }
927
Shutdown()928 void TemplateURLService::Shutdown() {
929 for (auto& observer : model_observers_)
930 observer.OnTemplateURLServiceShuttingDown();
931
932 if (client_)
933 client_->Shutdown();
934 // This check has to be done at Shutdown() instead of in the dtor to ensure
935 // that no clients of KeywordWebDataService are holding ptrs to it after the
936 // first phase of the KeyedService Shutdown() process.
937 if (load_handle_) {
938 DCHECK(web_data_service_);
939 web_data_service_->CancelRequest(load_handle_);
940 }
941 web_data_service_ = nullptr;
942 }
943
WaitUntilReadyToSync(base::OnceClosure done)944 void TemplateURLService::WaitUntilReadyToSync(base::OnceClosure done) {
945 DCHECK(!on_loaded_callback_for_sync_);
946
947 // We force a load here to allow remote updates to be processed, without
948 // waiting for the lazy load.
949 Load();
950
951 if (loaded_)
952 std::move(done).Run();
953 else
954 on_loaded_callback_for_sync_ = std::move(done);
955 }
956
GetAllSyncData(syncer::ModelType type) const957 syncer::SyncDataList TemplateURLService::GetAllSyncData(
958 syncer::ModelType type) const {
959 DCHECK_EQ(syncer::SEARCH_ENGINES, type);
960
961 syncer::SyncDataList current_data;
962 for (const auto& turl : template_urls_) {
963 // We don't sync keywords managed by policy.
964 if (turl->created_by_policy())
965 continue;
966 // We don't sync extension-controlled search engines.
967 if (turl->type() != TemplateURL::NORMAL)
968 continue;
969 current_data.push_back(CreateSyncDataFromTemplateURL(*turl));
970 }
971
972 return current_data;
973 }
974
ProcessSyncChanges(const base::Location & from_here,const syncer::SyncChangeList & change_list)975 base::Optional<syncer::ModelError> TemplateURLService::ProcessSyncChanges(
976 const base::Location& from_here,
977 const syncer::SyncChangeList& change_list) {
978 if (!models_associated_) {
979 return syncer::ModelError(FROM_HERE, "Models not yet associated.");
980 }
981 DCHECK(loaded_);
982
983 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true);
984
985 Scoper scoper(this);
986
987 // We've started syncing, so set our origin member to the base Sync value.
988 // As we move through Sync Code, we may set this to increasingly specific
989 // origins so we can tell what exactly caused a DSP change.
990 base::AutoReset<DefaultSearchChangeOrigin> change_origin(&dsp_change_origin_,
991 DSP_CHANGE_SYNC_UNINTENTIONAL);
992
993 syncer::SyncChangeList new_changes;
994 syncer::SyncError error;
995 for (auto iter = change_list.begin(); iter != change_list.end(); ++iter) {
996 DCHECK_EQ(syncer::SEARCH_ENGINES, iter->sync_data().GetDataType());
997
998 TemplateURL* existing_turl = GetTemplateURLForGUID(
999 iter->sync_data().GetSpecifics().search_engine().sync_guid());
1000 std::unique_ptr<TemplateURL> turl =
1001 CreateTemplateURLFromTemplateURLAndSyncData(
1002 client_.get(), prefs_, search_terms_data(), existing_turl,
1003 iter->sync_data(), &new_changes);
1004 if (!turl)
1005 continue;
1006
1007 const std::string error_msg =
1008 "ProcessSyncChanges failed on ChangeType " +
1009 syncer::SyncChange::ChangeTypeToString(iter->change_type());
1010 if (iter->change_type() == syncer::SyncChange::ACTION_INVALID) {
1011 error = sync_error_factory_->CreateAndUploadError(FROM_HERE, error_msg);
1012 continue;
1013 }
1014
1015 if (iter->change_type() == syncer::SyncChange::ACTION_DELETE) {
1016 if (!existing_turl) {
1017 // Can't DELETE a non-existent engine, although we log it.
1018 UMA_HISTOGRAM_ENUMERATION(kSearchTemplateURLEventsHistogramName,
1019 SYNC_DELETE_FAIL_NONEXISTENT_ENGINE,
1020 SEARCH_TEMPLATE_URL_EVENT_MAX);
1021 error = sync_error_factory_->CreateAndUploadError(FROM_HERE, error_msg);
1022 continue;
1023 }
1024
1025 // We can get an ACTION_DELETE for the default search provider if the user
1026 // has changed the default search provider on a different machine, and we
1027 // get the search engine update before the preference update.
1028 //
1029 // In this case, ignore the delete, because we never want to reset the
1030 // default search provider as a result of ACTION_DELETE. If the preference
1031 // update arrives later, we may be stuck with an extra search engine entry
1032 // in this edge case, but it's better than most alternatives.
1033 //
1034 // In the past, we tried re-creating the deleted TemplateURL, but it was
1035 // likely a source of duplicate search engine entries. crbug.com/1022775
1036 if (existing_turl != GetDefaultSearchProvider()) {
1037 Remove(existing_turl);
1038 UMA_HISTOGRAM_ENUMERATION(kSearchTemplateURLEventsHistogramName,
1039 SYNC_DELETE_SUCCESS,
1040 SEARCH_TEMPLATE_URL_EVENT_MAX);
1041 } else {
1042 UMA_HISTOGRAM_ENUMERATION(kSearchTemplateURLEventsHistogramName,
1043 SYNC_DELETE_FAIL_DEFAULT_SEARCH_PROVIDER,
1044 SEARCH_TEMPLATE_URL_EVENT_MAX);
1045 }
1046 continue;
1047 }
1048
1049 if ((iter->change_type() == syncer::SyncChange::ACTION_ADD &&
1050 existing_turl) ||
1051 (iter->change_type() == syncer::SyncChange::ACTION_UPDATE &&
1052 !existing_turl)) {
1053 // Can't ADD an already-existing engine, and can't UPDATE a non-existent
1054 // engine. Early exit here to avoid ResolvingSyncKeywordConflict().
1055 error = sync_error_factory_->CreateAndUploadError(FROM_HERE, error_msg);
1056 continue;
1057 }
1058
1059 // Explicitly don't check for conflicts against extension keywords; in this
1060 // case the functions which modify the keyword map know how to handle the
1061 // conflicts.
1062 // TODO(mpcomplete): If we allow editing extension keywords, then those will
1063 // need to undergo conflict resolution.
1064 TemplateURL* existing_keyword_turl =
1065 FindNonExtensionTemplateURLForKeyword(turl->keyword());
1066 const bool has_conflict =
1067 existing_keyword_turl && (existing_keyword_turl != existing_turl);
1068 if (has_conflict) {
1069 // Resolve any conflicts with other entries so we can safely update the
1070 // keyword.
1071 ResolveSyncKeywordConflict(turl.get(), existing_keyword_turl,
1072 &new_changes);
1073 }
1074
1075 if (iter->change_type() == syncer::SyncChange::ACTION_ADD) {
1076 base::AutoReset<DefaultSearchChangeOrigin> change_origin(
1077 &dsp_change_origin_, DSP_CHANGE_SYNC_ADD);
1078 // Force the local ID to kInvalidTemplateURLID so we can add it.
1079 TemplateURLData data(turl->data());
1080 data.id = kInvalidTemplateURLID;
1081 auto added_ptr = std::make_unique<TemplateURL>(data);
1082 TemplateURL* added = added_ptr.get();
1083 if (Add(std::move(added_ptr)))
1084 MaybeUpdateDSEViaPrefs(added);
1085 continue;
1086 }
1087
1088 DCHECK_EQ(syncer::SyncChange::ACTION_UPDATE, iter->change_type());
1089 if (Update(existing_turl, *turl))
1090 MaybeUpdateDSEViaPrefs(existing_turl);
1091 }
1092
1093 // If something went wrong, we want to prematurely exit to avoid pushing
1094 // inconsistent data to Sync. We return the last error we received.
1095 if (error.IsSet())
1096 return ConvertToModelError(error);
1097
1098 return sync_processor_->ProcessSyncChanges(from_here, new_changes);
1099 }
1100
MergeDataAndStartSyncing(syncer::ModelType type,const syncer::SyncDataList & initial_sync_data,std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory)1101 base::Optional<syncer::ModelError> TemplateURLService::MergeDataAndStartSyncing(
1102 syncer::ModelType type,
1103 const syncer::SyncDataList& initial_sync_data,
1104 std::unique_ptr<syncer::SyncChangeProcessor> sync_processor,
1105 std::unique_ptr<syncer::SyncErrorFactory> sync_error_factory) {
1106 DCHECK(loaded_);
1107 DCHECK_EQ(type, syncer::SEARCH_ENGINES);
1108 DCHECK(!sync_processor_);
1109 DCHECK(sync_processor);
1110 DCHECK(sync_error_factory);
1111
1112 // Disable sync if we failed to load.
1113 if (load_failed_) {
1114 return syncer::ModelError(FROM_HERE, "Local database load failed.");
1115 }
1116
1117 sync_processor_ = std::move(sync_processor);
1118 sync_error_factory_ = std::move(sync_error_factory);
1119
1120 // We do a lot of calls to Add/Remove/ResetTemplateURL here, so ensure we
1121 // don't step on our own toes.
1122 base::AutoReset<bool> processing_changes(&processing_syncer_changes_, true);
1123
1124 Scoper scoper(this);
1125
1126 // We've started syncing, so set our origin member to the base Sync value.
1127 // As we move through Sync Code, we may set this to increasingly specific
1128 // origins so we can tell what exactly caused a DSP change.
1129 base::AutoReset<DefaultSearchChangeOrigin> change_origin(&dsp_change_origin_,
1130 DSP_CHANGE_SYNC_UNINTENTIONAL);
1131
1132 syncer::SyncChangeList new_changes;
1133
1134 // Build maps of our sync GUIDs to syncer::SyncData.
1135 SyncDataMap local_data_map = CreateGUIDToSyncDataMap(
1136 GetAllSyncData(syncer::SEARCH_ENGINES));
1137 SyncDataMap sync_data_map = CreateGUIDToSyncDataMap(initial_sync_data);
1138
1139 for (SyncDataMap::const_iterator iter = sync_data_map.begin();
1140 iter != sync_data_map.end(); ++iter) {
1141 TemplateURL* local_turl = GetTemplateURLForGUID(iter->first);
1142 std::unique_ptr<TemplateURL> sync_turl(
1143 CreateTemplateURLFromTemplateURLAndSyncData(
1144 client_.get(), prefs_, search_terms_data(), local_turl,
1145 iter->second, &new_changes));
1146 if (!sync_turl)
1147 continue;
1148
1149 if (pre_sync_deletes_.find(sync_turl->sync_guid()) !=
1150 pre_sync_deletes_.end()) {
1151 // This entry was deleted before the initial sync began (possibly through
1152 // preprocessing in TemplateURLService's loading code). Ignore it and send
1153 // an ACTION_DELETE up to the server.
1154 new_changes.push_back(
1155 syncer::SyncChange(FROM_HERE,
1156 syncer::SyncChange::ACTION_DELETE,
1157 iter->second));
1158 UMA_HISTOGRAM_ENUMERATION(kDeleteSyncedEngineHistogramName,
1159 DELETE_ENGINE_PRE_SYNC, DELETE_ENGINE_MAX);
1160 continue;
1161 }
1162
1163 if (local_turl) {
1164 DCHECK(IsFromSync(local_turl, sync_data_map));
1165 // This local search engine is already synced. If the timestamp differs
1166 // from Sync, we need to update locally or to the cloud. Note that if the
1167 // timestamps are equal, we touch neither.
1168 if (sync_turl->last_modified() > local_turl->last_modified()) {
1169 // We've received an update from Sync. We should replace all synced
1170 // fields in the local TemplateURL. Note that this includes the
1171 // TemplateURLID and the TemplateURL may have to be reparsed. This
1172 // also makes the local data's last_modified timestamp equal to Sync's,
1173 // avoiding an Update on the next MergeData call.
1174 Update(local_turl, *sync_turl);
1175 } else if (sync_turl->last_modified() < local_turl->last_modified()) {
1176 // Otherwise, we know we have newer data, so update Sync with our
1177 // data fields.
1178 new_changes.push_back(
1179 syncer::SyncChange(FROM_HERE,
1180 syncer::SyncChange::ACTION_UPDATE,
1181 local_data_map[local_turl->sync_guid()]));
1182 }
1183 local_data_map.erase(iter->first);
1184 } else {
1185 // The search engine from the cloud has not been synced locally. Merge it
1186 // into our local model. This will handle any conflicts with local (and
1187 // already-synced) TemplateURLs. It will prefer to keep entries from Sync
1188 // over not-yet-synced TemplateURLs.
1189 MergeInSyncTemplateURL(sync_turl.get(), sync_data_map, &new_changes,
1190 &local_data_map);
1191 }
1192 }
1193
1194
1195 // The remaining SyncData in local_data_map should be everything that needs to
1196 // be pushed as ADDs to sync.
1197 for (SyncDataMap::const_iterator iter = local_data_map.begin();
1198 iter != local_data_map.end(); ++iter) {
1199 new_changes.push_back(
1200 syncer::SyncChange(FROM_HERE,
1201 syncer::SyncChange::ACTION_ADD,
1202 iter->second));
1203 }
1204
1205 // Do some post-processing on the change list to ensure that we are sending
1206 // valid changes to sync_processor_.
1207 PruneSyncChanges(&sync_data_map, &new_changes);
1208
1209 LogDuplicatesHistogram(GetTemplateURLs());
1210 base::Optional<syncer::ModelError> error =
1211 sync_processor_->ProcessSyncChanges(FROM_HERE, new_changes);
1212 if (!error.has_value()) {
1213 // The ACTION_DELETEs from this set are processed. Empty it so we don't try
1214 // to reuse them on the next call to MergeDataAndStartSyncing.
1215 pre_sync_deletes_.clear();
1216
1217 models_associated_ = true;
1218 }
1219
1220 return error;
1221 }
1222
StopSyncing(syncer::ModelType type)1223 void TemplateURLService::StopSyncing(syncer::ModelType type) {
1224 DCHECK_EQ(type, syncer::SEARCH_ENGINES);
1225 models_associated_ = false;
1226 sync_processor_.reset();
1227 sync_error_factory_.reset();
1228 }
1229
ProcessTemplateURLChange(const base::Location & from_here,const TemplateURL * turl,syncer::SyncChange::SyncChangeType type)1230 void TemplateURLService::ProcessTemplateURLChange(
1231 const base::Location& from_here,
1232 const TemplateURL* turl,
1233 syncer::SyncChange::SyncChangeType type) {
1234 DCHECK_NE(type, syncer::SyncChange::ACTION_INVALID);
1235 DCHECK(turl);
1236
1237 if (!models_associated_)
1238 return; // Not syncing.
1239
1240 if (processing_syncer_changes_)
1241 return; // These are changes originating from us. Ignore.
1242
1243 // Avoid syncing keywords managed by policy.
1244 if (turl->created_by_policy())
1245 return;
1246
1247 // Avoid syncing extension-controlled search engines.
1248 if (turl->type() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION)
1249 return;
1250
1251 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*turl);
1252 syncer::SyncChangeList changes = {
1253 syncer::SyncChange(from_here, type, sync_data)};
1254 sync_processor_->ProcessSyncChanges(FROM_HERE, changes);
1255 }
1256
GetSessionToken()1257 std::string TemplateURLService::GetSessionToken() {
1258 base::TimeTicks current_time(base::TimeTicks::Now());
1259 // Renew token if it expired.
1260 if (current_time > token_expiration_time_) {
1261 const size_t kTokenBytes = 12;
1262 std::string raw_data;
1263 base::RandBytes(base::WriteInto(&raw_data, kTokenBytes + 1), kTokenBytes);
1264 base::Base64UrlEncode(raw_data,
1265 base::Base64UrlEncodePolicy::INCLUDE_PADDING,
1266 ¤t_token_);
1267 }
1268
1269 // Extend expiration time another 60 seconds.
1270 token_expiration_time_ = current_time + base::TimeDelta::FromSeconds(60);
1271 return current_token_;
1272 }
1273
ClearSessionToken()1274 void TemplateURLService::ClearSessionToken() {
1275 token_expiration_time_ = base::TimeTicks();
1276 }
1277
1278 // static
CreateSyncDataFromTemplateURL(const TemplateURL & turl)1279 syncer::SyncData TemplateURLService::CreateSyncDataFromTemplateURL(
1280 const TemplateURL& turl) {
1281 sync_pb::EntitySpecifics specifics;
1282 sync_pb::SearchEngineSpecifics* se_specifics =
1283 specifics.mutable_search_engine();
1284 se_specifics->set_short_name(base::UTF16ToUTF8(turl.short_name()));
1285 se_specifics->set_keyword(base::UTF16ToUTF8(turl.keyword()));
1286 se_specifics->set_favicon_url(turl.favicon_url().spec());
1287 se_specifics->set_url(turl.url());
1288 se_specifics->set_safe_for_autoreplace(turl.safe_for_autoreplace());
1289 se_specifics->set_originating_url(turl.originating_url().spec());
1290 se_specifics->set_date_created(turl.date_created().ToInternalValue());
1291 se_specifics->set_input_encodings(
1292 base::JoinString(turl.input_encodings(), ";"));
1293 se_specifics->set_suggestions_url(turl.suggestions_url());
1294 se_specifics->set_prepopulate_id(turl.prepopulate_id());
1295 if (!turl.image_url().empty())
1296 se_specifics->set_image_url(turl.image_url());
1297 se_specifics->set_new_tab_url(turl.new_tab_url());
1298 if (!turl.search_url_post_params().empty())
1299 se_specifics->set_search_url_post_params(turl.search_url_post_params());
1300 if (!turl.suggestions_url_post_params().empty()) {
1301 se_specifics->set_suggestions_url_post_params(
1302 turl.suggestions_url_post_params());
1303 }
1304 if (!turl.image_url_post_params().empty())
1305 se_specifics->set_image_url_post_params(turl.image_url_post_params());
1306 se_specifics->set_last_modified(turl.last_modified().ToInternalValue());
1307 se_specifics->set_sync_guid(turl.sync_guid());
1308 for (size_t i = 0; i < turl.alternate_urls().size(); ++i)
1309 se_specifics->add_alternate_urls(turl.alternate_urls()[i]);
1310
1311 return syncer::SyncData::CreateLocalData(se_specifics->sync_guid(),
1312 se_specifics->keyword(),
1313 specifics);
1314 }
1315
1316 // static
1317 std::unique_ptr<TemplateURL>
CreateTemplateURLFromTemplateURLAndSyncData(TemplateURLServiceClient * client,PrefService * prefs,const SearchTermsData & search_terms_data,const TemplateURL * existing_turl,const syncer::SyncData & sync_data,syncer::SyncChangeList * change_list)1318 TemplateURLService::CreateTemplateURLFromTemplateURLAndSyncData(
1319 TemplateURLServiceClient* client,
1320 PrefService* prefs,
1321 const SearchTermsData& search_terms_data,
1322 const TemplateURL* existing_turl,
1323 const syncer::SyncData& sync_data,
1324 syncer::SyncChangeList* change_list) {
1325 DCHECK(change_list);
1326
1327 sync_pb::SearchEngineSpecifics specifics =
1328 sync_data.GetSpecifics().search_engine();
1329
1330 // Past bugs might have caused either of these fields to be empty. Just
1331 // delete this data off the server.
1332 if (specifics.url().empty() || specifics.sync_guid().empty()) {
1333 change_list->push_back(
1334 syncer::SyncChange(FROM_HERE,
1335 syncer::SyncChange::ACTION_DELETE,
1336 sync_data));
1337 UMA_HISTOGRAM_ENUMERATION(kDeleteSyncedEngineHistogramName,
1338 DELETE_ENGINE_EMPTY_FIELD, DELETE_ENGINE_MAX);
1339 return nullptr;
1340 }
1341
1342 TemplateURLData data(existing_turl ?
1343 existing_turl->data() : TemplateURLData());
1344 data.SetShortName(base::UTF8ToUTF16(specifics.short_name()));
1345 data.originating_url = GURL(specifics.originating_url());
1346 base::string16 keyword(base::UTF8ToUTF16(specifics.keyword()));
1347 // NOTE: Once this code has shipped in a couple of stable releases, we can
1348 // probably remove the migration portion, comment out the
1349 // "autogenerate_keyword" field entirely in the .proto file, and fold the
1350 // empty keyword case into the "delete data" block above.
1351 bool reset_keyword =
1352 specifics.autogenerate_keyword() || specifics.keyword().empty();
1353 if (reset_keyword)
1354 keyword = base::ASCIIToUTF16("dummy"); // Will be replaced below.
1355 DCHECK(!keyword.empty());
1356 data.SetKeyword(keyword);
1357 data.SetURL(specifics.url());
1358 data.suggestions_url = specifics.suggestions_url();
1359 data.image_url = specifics.image_url();
1360 data.new_tab_url = specifics.new_tab_url();
1361 data.search_url_post_params = specifics.search_url_post_params();
1362 data.suggestions_url_post_params = specifics.suggestions_url_post_params();
1363 data.image_url_post_params = specifics.image_url_post_params();
1364 data.favicon_url = GURL(specifics.favicon_url());
1365 data.safe_for_autoreplace = specifics.safe_for_autoreplace();
1366 data.input_encodings = base::SplitString(
1367 specifics.input_encodings(), ";",
1368 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
1369 // If the server data has duplicate encodings, we'll want to push an update
1370 // below to correct it. Note that we also fix this in
1371 // GetSearchProvidersUsingKeywordResult(), since otherwise we'd never correct
1372 // local problems for clients which have disabled search engine sync.
1373 bool deduped = DeDupeEncodings(&data.input_encodings);
1374 data.date_created = base::Time::FromInternalValue(specifics.date_created());
1375 data.last_modified = base::Time::FromInternalValue(specifics.last_modified());
1376 data.prepopulate_id = specifics.prepopulate_id();
1377 data.sync_guid = specifics.sync_guid();
1378 data.alternate_urls.clear();
1379 for (int i = 0; i < specifics.alternate_urls_size(); ++i)
1380 data.alternate_urls.push_back(specifics.alternate_urls(i));
1381
1382 std::unique_ptr<TemplateURL> turl(new TemplateURL(data));
1383 // If this TemplateURL matches a built-in prepopulated template URL, it's
1384 // possible that sync is trying to modify fields that should not be touched.
1385 // Revert these fields to the built-in values.
1386 UpdateTemplateURLIfPrepopulated(turl.get(), prefs);
1387
1388 DCHECK_EQ(TemplateURL::NORMAL, turl->type());
1389 if (reset_keyword || deduped) {
1390 if (reset_keyword)
1391 turl->ResetKeywordIfNecessary(search_terms_data, true);
1392 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*turl);
1393 change_list->push_back(syncer::SyncChange(FROM_HERE,
1394 syncer::SyncChange::ACTION_UPDATE,
1395 sync_data));
1396 } else if (turl->IsGoogleSearchURLWithReplaceableKeyword(search_terms_data)) {
1397 if (!existing_turl) {
1398 // We're adding a new TemplateURL that uses the Google base URL, so set
1399 // its keyword appropriately for the local environment.
1400 turl->ResetKeywordIfNecessary(search_terms_data, false);
1401 } else if (existing_turl->IsGoogleSearchURLWithReplaceableKeyword(
1402 search_terms_data)) {
1403 // Ignore keyword changes triggered by the Google base URL changing on
1404 // another client. If the base URL changes in this client as well, we'll
1405 // pick that up separately at the appropriate time. Otherwise, changing
1406 // the keyword here could result in having the wrong keyword for the local
1407 // environment.
1408 turl->data_.SetKeyword(existing_turl->keyword());
1409 }
1410 }
1411
1412 return turl;
1413 }
1414
1415 // static
CreateGUIDToSyncDataMap(const syncer::SyncDataList & sync_data)1416 SyncDataMap TemplateURLService::CreateGUIDToSyncDataMap(
1417 const syncer::SyncDataList& sync_data) {
1418 SyncDataMap data_map;
1419 for (auto i(sync_data.begin()); i != sync_data.end(); ++i)
1420 data_map[i->GetSpecifics().search_engine().sync_guid()] = *i;
1421 return data_map;
1422 }
1423
Init(const Initializer * initializers,int num_initializers)1424 void TemplateURLService::Init(const Initializer* initializers,
1425 int num_initializers) {
1426 if (client_)
1427 client_->SetOwner(this);
1428
1429 if (prefs_) {
1430 pref_change_registrar_.Init(prefs_);
1431 pref_change_registrar_.Add(
1432 prefs::kSyncedDefaultSearchProviderGUID,
1433 base::Bind(
1434 &TemplateURLService::OnSyncedDefaultSearchProviderGUIDChanged,
1435 base::Unretained(this)));
1436 }
1437
1438 DefaultSearchManager::Source source = DefaultSearchManager::FROM_USER;
1439 const TemplateURLData* dse =
1440 default_search_manager_.GetDefaultSearchEngine(&source);
1441
1442 Scoper scoper(this);
1443
1444 ApplyDefaultSearchChange(dse, source);
1445
1446 if (num_initializers > 0) {
1447 // This path is only hit by test code and is used to simulate a loaded
1448 // TemplateURLService.
1449 ChangeToLoadedState();
1450
1451 // Add specific initializers, if any.
1452 for (int i(0); i < num_initializers; ++i) {
1453 DCHECK(initializers[i].keyword);
1454 DCHECK(initializers[i].url);
1455 DCHECK(initializers[i].content);
1456
1457 // TemplateURLService ends up owning the TemplateURL, don't try and free
1458 // it.
1459 TemplateURLData data;
1460 data.SetShortName(base::UTF8ToUTF16(initializers[i].content));
1461 data.SetKeyword(base::UTF8ToUTF16(initializers[i].keyword));
1462 data.SetURL(initializers[i].url);
1463 Add(std::make_unique<TemplateURL>(data));
1464
1465 // Set the first provided identifier to be the default.
1466 if (i == 0)
1467 default_search_manager_.SetUserSelectedDefaultSearchEngine(data);
1468 }
1469 }
1470 }
1471
RemoveFromMaps(const TemplateURL * template_url)1472 void TemplateURLService::RemoveFromMaps(const TemplateURL* template_url) {
1473 const base::string16& keyword = template_url->keyword();
1474
1475 // Remove from |keyword_to_turl_and_length_|. No need to find the best
1476 // fallback. We choose the best one as-needed from the multimap.
1477 const auto match_range = keyword_to_turl_and_length_.equal_range(keyword);
1478 for (auto it = match_range.first; it != match_range.second;) {
1479 if (it->second.first == template_url) {
1480 it = keyword_to_turl_and_length_.erase(it);
1481 } else {
1482 ++it;
1483 }
1484 }
1485
1486 if (template_url->type() == TemplateURL::OMNIBOX_API_EXTENSION)
1487 return;
1488
1489 if (!template_url->sync_guid().empty())
1490 guid_to_turl_.erase(template_url->sync_guid());
1491 // |provider_map_| is only initialized after loading has completed.
1492 if (loaded_) {
1493 provider_map_->Remove(template_url);
1494 }
1495 }
1496
AddToMaps(TemplateURL * template_url)1497 void TemplateURLService::AddToMaps(TemplateURL* template_url) {
1498 const base::string16& keyword = template_url->keyword();
1499 keyword_to_turl_and_length_.insert(std::make_pair(
1500 keyword,
1501 TURLAndMeaningfulLength(
1502 template_url, GetMeaningfulKeywordLength(keyword, template_url))));
1503
1504 if (template_url->type() == TemplateURL::OMNIBOX_API_EXTENSION)
1505 return;
1506
1507 if (!template_url->sync_guid().empty())
1508 guid_to_turl_[template_url->sync_guid()] = template_url;
1509 // |provider_map_| is only initialized after loading has completed.
1510 if (loaded_)
1511 provider_map_->Add(template_url, search_terms_data());
1512 }
1513
SetTemplateURLs(std::unique_ptr<OwnedTemplateURLVector> urls)1514 void TemplateURLService::SetTemplateURLs(
1515 std::unique_ptr<OwnedTemplateURLVector> urls) {
1516 Scoper scoper(this);
1517
1518 // Partition the URLs first, instead of implementing the loops below by simply
1519 // scanning the input twice. While it's not supposed to happen normally, it's
1520 // possible for corrupt databases to return multiple entries with the same
1521 // keyword. In this case, the first loop may delete the first entry when
1522 // adding the second. If this happens, the second loop must not attempt to
1523 // access the deleted entry. Partitioning ensures this constraint.
1524 auto first_invalid = std::partition(
1525 urls->begin(), urls->end(), [](const std::unique_ptr<TemplateURL>& turl) {
1526 return turl->id() != kInvalidTemplateURLID;
1527 });
1528
1529 // First, add the items that already have id's, so that the next_id_ gets
1530 // properly set.
1531 for (auto i = urls->begin(); i != first_invalid; ++i) {
1532 next_id_ = std::max(next_id_, (*i)->id());
1533 Add(std::move(*i), false);
1534 }
1535
1536 // Next add the new items that don't have id's.
1537 for (auto i = first_invalid; i != urls->end(); ++i)
1538 Add(std::move(*i));
1539 }
1540
ChangeToLoadedState()1541 void TemplateURLService::ChangeToLoadedState() {
1542 DCHECK(!loaded_);
1543
1544 provider_map_->Init(template_urls_, search_terms_data());
1545 loaded_ = true;
1546
1547 ApplyDefaultSearchChangeNoMetrics(
1548 initial_default_search_provider_
1549 ? &initial_default_search_provider_->data()
1550 : nullptr,
1551 default_search_provider_source_);
1552 initial_default_search_provider_.reset();
1553
1554 if (on_loaded_callback_for_sync_)
1555 std::move(on_loaded_callback_for_sync_).Run();
1556
1557 on_loaded_callbacks_.Notify();
1558 }
1559
CanAddAutogeneratedKeywordForHost(const std::string & host) const1560 bool TemplateURLService::CanAddAutogeneratedKeywordForHost(
1561 const std::string& host) const {
1562 const TemplateURLSet* urls = provider_map_->GetURLsForHost(host);
1563 if (!urls)
1564 return true;
1565
1566 return std::all_of(urls->begin(), urls->end(), [](const TemplateURL* turl) {
1567 return turl->safe_for_autoreplace();
1568 });
1569 }
1570
CanReplace(const TemplateURL * t_url) const1571 bool TemplateURLService::CanReplace(const TemplateURL* t_url) const {
1572 return !ShowInDefaultList(t_url) && t_url->safe_for_autoreplace();
1573 }
1574
FindNonExtensionTemplateURLForKeyword(const base::string16 & keyword)1575 TemplateURL* TemplateURLService::FindNonExtensionTemplateURLForKeyword(
1576 const base::string16& keyword) {
1577 TemplateURL* keyword_turl = GetTemplateURLForKeyword(keyword);
1578 if (!keyword_turl || (keyword_turl->type() == TemplateURL::NORMAL))
1579 return keyword_turl;
1580 // The extension keyword in the model may be hiding a replaceable
1581 // non-extension keyword. Look for it.
1582 for (const auto& turl : template_urls_) {
1583 if ((turl->type() == TemplateURL::NORMAL) &&
1584 (turl->keyword() == keyword))
1585 return turl.get();
1586 }
1587 return nullptr;
1588 }
1589
Update(TemplateURL * existing_turl,const TemplateURL & new_values)1590 bool TemplateURLService::Update(TemplateURL* existing_turl,
1591 const TemplateURL& new_values) {
1592 DCHECK(existing_turl);
1593 DCHECK(!IsCreatedByExtension(existing_turl));
1594 if (!Contains(&template_urls_, existing_turl))
1595 return false;
1596
1597 Scoper scoper(this);
1598 model_mutated_notification_pending_ = true;
1599
1600 TemplateURLID previous_id = existing_turl->id();
1601 RemoveFromMaps(existing_turl);
1602
1603 // Check for new keyword conflicts with another normal engine.
1604 // This is possible when autogeneration of the keyword for a Google default
1605 // search provider at load time causes it to conflict with an existing
1606 // keyword. If the conflicting engines are replaceable, we delete them.
1607 // If they're not replaceable, we leave them alone, and trust AddToMaps() to
1608 // choose the best engine to assign the keyword.
1609 std::vector<TemplateURL*> turls_to_remove;
1610 for (const auto& turl : template_urls_) {
1611 // TODO(tommycli): Investigate also replacing TemplateURL::LOCAL engines.
1612 if (turl.get() != existing_turl && (turl->type() == TemplateURL::NORMAL) &&
1613 (turl->keyword() == new_values.keyword()) && CanReplace(turl.get())) {
1614 // Remove() invalidates iterators.
1615 turls_to_remove.push_back(turl.get());
1616 }
1617 }
1618 for (TemplateURL* turl : turls_to_remove) {
1619 Remove(turl);
1620 }
1621
1622 // Update existing turl with new values. This must happen after calling
1623 // Remove(conflicting_keyword_turl) above, since otherwise during that
1624 // function RemoveFromMaps() may find |existing_turl| as an alternate engine
1625 // for the same keyword. Duplicate keyword handling is only meant for the
1626 // case of extensions, and if done here would leave internal state
1627 // inconsistent (e.g. |existing_turl| would already be re-added to maps before
1628 // calling AddToMaps() below).
1629 existing_turl->CopyFrom(new_values);
1630 existing_turl->data_.id = previous_id;
1631
1632 AddToMaps(existing_turl);
1633
1634 if (existing_turl->type() == TemplateURL::NORMAL) {
1635 if (web_data_service_)
1636 web_data_service_->UpdateKeyword(existing_turl->data());
1637
1638 // Inform sync of the update.
1639 ProcessTemplateURLChange(FROM_HERE, existing_turl,
1640 syncer::SyncChange::ACTION_UPDATE);
1641 }
1642
1643 // Even if the DSE is controlled by an extension or policy, update the user
1644 // preferences as they may take over later.
1645 if (default_search_provider_source_ != DefaultSearchManager::FROM_FALLBACK)
1646 MaybeUpdateDSEViaPrefs(existing_turl);
1647
1648 return true;
1649 }
1650
1651 // static
UpdateTemplateURLIfPrepopulated(TemplateURL * template_url,PrefService * prefs)1652 void TemplateURLService::UpdateTemplateURLIfPrepopulated(
1653 TemplateURL* template_url,
1654 PrefService* prefs) {
1655 int prepopulate_id = template_url->prepopulate_id();
1656 if (template_url->prepopulate_id() == 0)
1657 return;
1658
1659 std::vector<std::unique_ptr<TemplateURLData>> prepopulated_urls =
1660 TemplateURLPrepopulateData::GetPrepopulatedEngines(prefs, nullptr);
1661
1662 for (const auto& url : prepopulated_urls) {
1663 if (url->prepopulate_id == prepopulate_id) {
1664 MergeIntoPrepopulatedEngineData(template_url, url.get());
1665 template_url->CopyFrom(TemplateURL(*url));
1666 }
1667 }
1668 }
1669
MaybeUpdateDSEViaPrefs(TemplateURL * synced_turl)1670 void TemplateURLService::MaybeUpdateDSEViaPrefs(TemplateURL* synced_turl) {
1671 if (prefs_ &&
1672 (synced_turl->sync_guid() ==
1673 prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID))) {
1674 default_search_manager_.SetUserSelectedDefaultSearchEngine(
1675 synced_turl->data());
1676 }
1677 }
1678
UpdateKeywordSearchTermsForURL(const URLVisitedDetails & details)1679 void TemplateURLService::UpdateKeywordSearchTermsForURL(
1680 const URLVisitedDetails& details) {
1681 if (!details.url.is_valid())
1682 return;
1683
1684 const TemplateURLSet* urls_for_host =
1685 provider_map_->GetURLsForHost(details.url.host());
1686 if (!urls_for_host)
1687 return;
1688
1689 TemplateURL* visited_url = nullptr;
1690 for (auto i = urls_for_host->begin(); i != urls_for_host->end(); ++i) {
1691 base::string16 search_terms;
1692 if ((*i)->ExtractSearchTermsFromURL(details.url, search_terms_data(),
1693 &search_terms) &&
1694 !search_terms.empty()) {
1695 if (details.is_keyword_transition) {
1696 // The visit is the result of the user entering a keyword, generate a
1697 // KEYWORD_GENERATED visit for the KEYWORD so that the keyword typed
1698 // count is boosted.
1699 AddTabToSearchVisit(**i);
1700 }
1701 if (client_) {
1702 client_->SetKeywordSearchTermsForURL(
1703 details.url, (*i)->id(), search_terms);
1704 }
1705 // Caches the matched TemplateURL so its last_visited could be updated
1706 // later after iteration.
1707 // Note: Update() will replace the entry from the container of this
1708 // iterator, so update here directly will cause an error about it.
1709 if (!IsCreatedByExtension(*i))
1710 visited_url = *i;
1711 }
1712 }
1713 if (visited_url)
1714 UpdateTemplateURLVisitTime(visited_url);
1715 }
1716
UpdateTemplateURLVisitTime(TemplateURL * url)1717 void TemplateURLService::UpdateTemplateURLVisitTime(TemplateURL* url) {
1718 TemplateURLData data(url->data());
1719 data.last_visited = clock_->Now();
1720 Update(url, TemplateURL(data));
1721 }
1722
AddTabToSearchVisit(const TemplateURL & t_url)1723 void TemplateURLService::AddTabToSearchVisit(const TemplateURL& t_url) {
1724 // Only add visits for entries the user hasn't modified. If the user modified
1725 // the entry the keyword may no longer correspond to the host name. It may be
1726 // possible to do something more sophisticated here, but it's so rare as to
1727 // not be worth it.
1728 if (!t_url.safe_for_autoreplace())
1729 return;
1730
1731 if (!client_)
1732 return;
1733
1734 GURL url(url_formatter::FixupURL(base::UTF16ToUTF8(t_url.keyword()),
1735 std::string()));
1736 if (!url.is_valid())
1737 return;
1738
1739 // Synthesize a visit for the keyword. This ensures the url for the keyword is
1740 // autocompleted even if the user doesn't type the url in directly.
1741 client_->AddKeywordGeneratedVisit(url);
1742 }
1743
ApplyDefaultSearchChange(const TemplateURLData * data,DefaultSearchManager::Source source)1744 void TemplateURLService::ApplyDefaultSearchChange(
1745 const TemplateURLData* data,
1746 DefaultSearchManager::Source source) {
1747 if (!ApplyDefaultSearchChangeNoMetrics(data, source))
1748 return;
1749
1750 UMA_HISTOGRAM_ENUMERATION(
1751 "Search.DefaultSearchChangeOrigin", dsp_change_origin_, DSP_CHANGE_MAX);
1752
1753 if (GetDefaultSearchProvider() &&
1754 GetDefaultSearchProvider()->HasGoogleBaseURLs(search_terms_data()) &&
1755 !dsp_change_callback_.is_null())
1756 dsp_change_callback_.Run();
1757 }
1758
ApplyDefaultSearchChangeNoMetrics(const TemplateURLData * data,DefaultSearchManager::Source source)1759 bool TemplateURLService::ApplyDefaultSearchChangeNoMetrics(
1760 const TemplateURLData* data,
1761 DefaultSearchManager::Source source) {
1762 // We do not want any sort of reentrancy while changing the default search
1763 // engine. This can occur when resolving conflicting entries. In those cases,
1764 // it's best to early exit and let the original process finish.
1765 if (applying_default_search_engine_change_)
1766 return false;
1767 base::AutoReset<bool> applying_change(&applying_default_search_engine_change_,
1768 true);
1769
1770 if (!loaded_) {
1771 // Set |initial_default_search_provider_| from the preferences. This is
1772 // mainly so we can hold ownership until we get to the point where the list
1773 // of keywords from Web Data is the owner of everything including the
1774 // default.
1775 bool changed = !TemplateURL::MatchesData(
1776 initial_default_search_provider_.get(), data, search_terms_data());
1777 TemplateURL::Type initial_engine_type =
1778 (source == DefaultSearchManager::FROM_EXTENSION)
1779 ? TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION
1780 : TemplateURL::NORMAL;
1781 initial_default_search_provider_ =
1782 data ? std::make_unique<TemplateURL>(*data, initial_engine_type)
1783 : nullptr;
1784 default_search_provider_source_ = source;
1785 return changed;
1786 }
1787
1788 // This may be deleted later. Use exclusively for pointer comparison to detect
1789 // a change.
1790 TemplateURL* previous_default_search_engine = default_search_provider_;
1791
1792 Scoper scoper(this);
1793
1794 if (default_search_provider_source_ == DefaultSearchManager::FROM_POLICY ||
1795 source == DefaultSearchManager::FROM_POLICY) {
1796 // We do this both to remove any no-longer-applicable policy-defined DSE as
1797 // well as to add the new one, if appropriate.
1798 UpdateProvidersCreatedByPolicy(
1799 &template_urls_,
1800 source == DefaultSearchManager::FROM_POLICY ? data : nullptr);
1801 }
1802
1803 // |default_search_provider_source_| must be set before calling Update(),
1804 // since that function needs to know the source of the update in question.
1805 default_search_provider_source_ = source;
1806
1807 if (!data) {
1808 default_search_provider_ = nullptr;
1809 } else if (source == DefaultSearchManager::FROM_EXTENSION) {
1810 default_search_provider_ = FindMatchingDefaultExtensionTemplateURL(*data);
1811 DCHECK(default_search_provider_);
1812 } else if (source == DefaultSearchManager::FROM_FALLBACK) {
1813 default_search_provider_ =
1814 FindPrepopulatedTemplateURL(data->prepopulate_id);
1815 if (default_search_provider_) {
1816 TemplateURLData update_data(*data);
1817 update_data.sync_guid = default_search_provider_->sync_guid();
1818
1819 // Now that we are auto-updating the favicon_url as the user browses,
1820 // respect the favicon_url entry in the database, instead of falling back
1821 // to the one in the prepopulated list.
1822 update_data.favicon_url = default_search_provider_->favicon_url();
1823
1824 if (!default_search_provider_->safe_for_autoreplace()) {
1825 update_data.safe_for_autoreplace = false;
1826 update_data.SetKeyword(default_search_provider_->keyword());
1827 update_data.SetShortName(default_search_provider_->short_name());
1828 }
1829 Update(default_search_provider_, TemplateURL(update_data));
1830 } else {
1831 // Normally the prepopulated fallback should be present in
1832 // |template_urls_|, but in a few cases it might not be:
1833 // (1) Tests that initialize the TemplateURLService in peculiar ways.
1834 // (2) If the user deleted the pre-populated default and we subsequently
1835 // lost their user-selected value.
1836 default_search_provider_ = Add(std::make_unique<TemplateURL>(*data));
1837 }
1838 } else if (source == DefaultSearchManager::FROM_USER) {
1839 default_search_provider_ = GetTemplateURLForGUID(data->sync_guid);
1840 if (!default_search_provider_ && data->prepopulate_id) {
1841 default_search_provider_ =
1842 FindPrepopulatedTemplateURL(data->prepopulate_id);
1843 }
1844 TemplateURLData new_data(*data);
1845 if (default_search_provider_) {
1846 Update(default_search_provider_, TemplateURL(new_data));
1847 } else {
1848 new_data.id = kInvalidTemplateURLID;
1849 default_search_provider_ = Add(std::make_unique<TemplateURL>(new_data));
1850 }
1851 if (default_search_provider_ && prefs_) {
1852 prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID,
1853 default_search_provider_->sync_guid());
1854 }
1855 }
1856
1857 bool changed = default_search_provider_ != previous_default_search_engine;
1858 if (changed) {
1859 model_mutated_notification_pending_ = true;
1860 }
1861
1862 return changed;
1863 }
1864
Add(std::unique_ptr<TemplateURL> template_url,bool newly_adding)1865 TemplateURL* TemplateURLService::Add(std::unique_ptr<TemplateURL> template_url,
1866 bool newly_adding) {
1867 DCHECK(template_url);
1868
1869 Scoper scoper(this);
1870
1871 if (newly_adding) {
1872 DCHECK_EQ(kInvalidTemplateURLID, template_url->id());
1873 DCHECK(!Contains(&template_urls_, template_url.get()));
1874 template_url->data_.id = ++next_id_;
1875 }
1876
1877 template_url->ResetKeywordIfNecessary(search_terms_data(), false);
1878
1879 // If |template_url| is not created by an extension, its keyword must not
1880 // conflict with any already in the model.
1881 if (!IsCreatedByExtension(template_url.get())) {
1882 TemplateURL* existing_turl =
1883 FindNonExtensionTemplateURLForKeyword(template_url->keyword());
1884
1885 // Note that we can reach here during the loading phase while processing the
1886 // template URLs from the web data service. In this case,
1887 // GetTemplateURLForKeyword() will look not only at what's already in the
1888 // model, but at the |initial_default_search_provider_|. Since this engine
1889 // will presumably also be present in the web data, we need to double-check
1890 // that any "pre-existing" entries we find are actually coming from
1891 // |template_urls_|, lest we detect a "conflict" between the
1892 // |initial_default_search_provider_| and the web data version of itself.
1893 if (existing_turl && Contains(&template_urls_, existing_turl)) {
1894 DCHECK_NE(existing_turl, template_url.get());
1895 if (CanReplace(existing_turl)) {
1896 Remove(existing_turl);
1897 } else if (CanReplace(template_url.get())) {
1898 return nullptr;
1899 } else {
1900 // Neither engine can be replaced. Uniquify the existing keyword.
1901 base::string16 new_keyword = UniquifyKeyword(*existing_turl, false);
1902 ResetTemplateURL(existing_turl, existing_turl->short_name(),
1903 new_keyword, existing_turl->url());
1904 DCHECK_EQ(new_keyword, existing_turl->keyword());
1905 }
1906 }
1907 }
1908 TemplateURL* template_url_ptr = template_url.get();
1909 template_urls_.push_back(std::move(template_url));
1910 AddToMaps(template_url_ptr);
1911
1912 if (newly_adding && (template_url_ptr->type() == TemplateURL::NORMAL)) {
1913 if (web_data_service_)
1914 web_data_service_->AddKeyword(template_url_ptr->data());
1915
1916 // Inform sync of the addition. Note that this will assign a GUID to
1917 // template_url and add it to the guid_to_turl_.
1918 ProcessTemplateURLChange(FROM_HERE, template_url_ptr,
1919 syncer::SyncChange::ACTION_ADD);
1920 }
1921
1922 if (template_url_ptr)
1923 model_mutated_notification_pending_ = true;
1924
1925 return template_url_ptr;
1926 }
1927
1928 // |template_urls| are the TemplateURLs loaded from the database.
1929 // |default_from_prefs| is the default search provider from the preferences, or
1930 // NULL if the DSE is not policy-defined.
1931 //
1932 // This function removes from the vector and the database all the TemplateURLs
1933 // that were set by policy, unless it is the current default search provider, in
1934 // which case it is updated with the data from prefs.
UpdateProvidersCreatedByPolicy(OwnedTemplateURLVector * template_urls,const TemplateURLData * default_from_prefs)1935 void TemplateURLService::UpdateProvidersCreatedByPolicy(
1936 OwnedTemplateURLVector* template_urls,
1937 const TemplateURLData* default_from_prefs) {
1938 DCHECK(template_urls);
1939
1940 Scoper scoper(this);
1941
1942 for (auto i = template_urls->begin(); i != template_urls->end();) {
1943 TemplateURL* template_url = i->get();
1944 if (template_url->created_by_policy()) {
1945 if (default_from_prefs &&
1946 TemplateURL::MatchesData(template_url, default_from_prefs,
1947 search_terms_data())) {
1948 // If the database specified a default search provider that was set
1949 // by policy, and the default search provider from the preferences
1950 // is also set by policy and they are the same, keep the entry in the
1951 // database and the |default_search_provider|.
1952 default_search_provider_ = template_url;
1953 // Prevent us from saving any other entries, or creating a new one.
1954 default_from_prefs = nullptr;
1955 ++i;
1956 continue;
1957 }
1958
1959 TemplateURLID id = template_url->id();
1960 RemoveFromMaps(template_url);
1961 i = template_urls->erase(i);
1962 if (web_data_service_)
1963 web_data_service_->RemoveKeyword(id);
1964 } else {
1965 ++i;
1966 }
1967 }
1968
1969 if (default_from_prefs) {
1970 default_search_provider_ = nullptr;
1971 default_search_provider_source_ = DefaultSearchManager::FROM_POLICY;
1972 TemplateURLData new_data(*default_from_prefs);
1973 if (new_data.sync_guid.empty())
1974 new_data.GenerateSyncGUID();
1975 new_data.created_by_policy = true;
1976 std::unique_ptr<TemplateURL> new_dse_ptr =
1977 std::make_unique<TemplateURL>(new_data);
1978 TemplateURL* new_dse = new_dse_ptr.get();
1979 if (Add(std::move(new_dse_ptr)))
1980 default_search_provider_ = new_dse;
1981 }
1982 }
1983
ResetTemplateURLGUID(TemplateURL * url,const std::string & guid)1984 void TemplateURLService::ResetTemplateURLGUID(TemplateURL* url,
1985 const std::string& guid) {
1986 DCHECK(loaded_);
1987 DCHECK(!guid.empty());
1988
1989 TemplateURLData data(url->data());
1990 data.sync_guid = guid;
1991 Update(url, TemplateURL(data));
1992 }
1993
UniquifyKeyword(const TemplateURL & turl,bool force)1994 base::string16 TemplateURLService::UniquifyKeyword(const TemplateURL& turl,
1995 bool force) {
1996 DCHECK(!IsCreatedByExtension(&turl));
1997 if (!force) {
1998 // Already unique.
1999 if (!GetTemplateURLForKeyword(turl.keyword()))
2000 return turl.keyword();
2001
2002 // First, try to return the generated keyword for the TemplateURL.
2003 GURL gurl(turl.url());
2004 if (gurl.is_valid()) {
2005 base::string16 keyword_candidate = TemplateURL::GenerateKeyword(gurl);
2006 if (!GetTemplateURLForKeyword(keyword_candidate))
2007 return keyword_candidate;
2008 }
2009 }
2010
2011 // We try to uniquify the keyword by appending a special character to the end.
2012 // This is a best-effort approach where we try to preserve the original
2013 // keyword and let the user do what they will after our attempt.
2014 base::string16 keyword_candidate(turl.keyword());
2015 do {
2016 keyword_candidate.append(base::ASCIIToUTF16("_"));
2017 } while (GetTemplateURLForKeyword(keyword_candidate));
2018
2019 return keyword_candidate;
2020 }
2021
IsLocalTemplateURLBetter(const TemplateURL * local_turl,const TemplateURL * sync_turl,bool prefer_local_default) const2022 bool TemplateURLService::IsLocalTemplateURLBetter(
2023 const TemplateURL* local_turl,
2024 const TemplateURL* sync_turl,
2025 bool prefer_local_default) const {
2026 DCHECK(GetTemplateURLForGUID(local_turl->sync_guid()));
2027 return local_turl->last_modified() > sync_turl->last_modified() ||
2028 local_turl->created_by_policy() ||
2029 (prefer_local_default && local_turl == GetDefaultSearchProvider());
2030 }
2031
ResolveSyncKeywordConflict(TemplateURL * unapplied_sync_turl,TemplateURL * applied_sync_turl,syncer::SyncChangeList * change_list)2032 void TemplateURLService::ResolveSyncKeywordConflict(
2033 TemplateURL* unapplied_sync_turl,
2034 TemplateURL* applied_sync_turl,
2035 syncer::SyncChangeList* change_list) {
2036 DCHECK(loaded_);
2037 DCHECK(unapplied_sync_turl);
2038 DCHECK(applied_sync_turl);
2039 DCHECK(change_list);
2040 DCHECK_EQ(applied_sync_turl->keyword(), unapplied_sync_turl->keyword());
2041 DCHECK_EQ(TemplateURL::NORMAL, applied_sync_turl->type());
2042
2043 Scoper scoper(this);
2044
2045 // Both |unapplied_sync_turl| and |applied_sync_turl| are known to Sync, so
2046 // don't delete either of them. Instead, determine which is "better" and
2047 // uniquify the other one, sending an update to the server for the updated
2048 // entry.
2049 const bool applied_turl_is_better =
2050 IsLocalTemplateURLBetter(applied_sync_turl, unapplied_sync_turl);
2051 TemplateURL* loser = applied_turl_is_better ?
2052 unapplied_sync_turl : applied_sync_turl;
2053 base::string16 new_keyword = UniquifyKeyword(*loser, false);
2054 DCHECK(!GetTemplateURLForKeyword(new_keyword));
2055 if (applied_turl_is_better) {
2056 // Just set the keyword of |unapplied_sync_turl|. The caller is responsible
2057 // for adding or updating unapplied_sync_turl in the local model.
2058 unapplied_sync_turl->data_.SetKeyword(new_keyword);
2059 } else {
2060 // Update |applied_sync_turl| in the local model with the new keyword.
2061 TemplateURLData data(applied_sync_turl->data());
2062 data.SetKeyword(new_keyword);
2063 Update(applied_sync_turl, TemplateURL(data));
2064 }
2065 // The losing TemplateURL should have their keyword updated. Send a change to
2066 // the server to reflect this change.
2067 syncer::SyncData sync_data = CreateSyncDataFromTemplateURL(*loser);
2068 change_list->push_back(syncer::SyncChange(FROM_HERE,
2069 syncer::SyncChange::ACTION_UPDATE,
2070 sync_data));
2071 }
2072
MergeInSyncTemplateURL(TemplateURL * sync_turl,const SyncDataMap & sync_data,syncer::SyncChangeList * change_list,SyncDataMap * local_data)2073 void TemplateURLService::MergeInSyncTemplateURL(
2074 TemplateURL* sync_turl,
2075 const SyncDataMap& sync_data,
2076 syncer::SyncChangeList* change_list,
2077 SyncDataMap* local_data) {
2078 DCHECK(sync_turl);
2079 DCHECK(!GetTemplateURLForGUID(sync_turl->sync_guid()));
2080 DCHECK(IsFromSync(sync_turl, sync_data));
2081
2082 TemplateURL* conflicting_turl =
2083 FindNonExtensionTemplateURLForKeyword(sync_turl->keyword());
2084 bool should_add_sync_turl = true;
2085
2086 Scoper scoper(this);
2087
2088 // Resolve conflicts with local TemplateURLs.
2089 if (conflicting_turl) {
2090 // Modify |conflicting_turl| to make room for |sync_turl|.
2091 if (IsFromSync(conflicting_turl, sync_data)) {
2092 // |conflicting_turl| is already known to Sync, so we're not allowed to
2093 // remove it. In this case, we want to uniquify the worse one and send an
2094 // update for the changed keyword to sync. We can reuse the logic from
2095 // ResolveSyncKeywordConflict for this.
2096 ResolveSyncKeywordConflict(sync_turl, conflicting_turl, change_list);
2097 } else {
2098 // |conflicting_turl| is not yet known to Sync. If it is better, then we
2099 // want to transfer its values up to sync. Otherwise, we remove it and
2100 // allow the entry from Sync to overtake it in the model.
2101 const std::string guid = conflicting_turl->sync_guid();
2102 if (IsLocalTemplateURLBetter(conflicting_turl, sync_turl)) {
2103 ResetTemplateURLGUID(conflicting_turl, sync_turl->sync_guid());
2104 syncer::SyncData sync_data =
2105 CreateSyncDataFromTemplateURL(*conflicting_turl);
2106 change_list->push_back(syncer::SyncChange(
2107 FROM_HERE, syncer::SyncChange::ACTION_UPDATE, sync_data));
2108 // Note that in this case we do not add the Sync TemplateURL to the
2109 // local model, since we've effectively "merged" it in by updating the
2110 // local conflicting entry with its sync_guid.
2111 should_add_sync_turl = false;
2112 } else {
2113 // We guarantee that this isn't the local search provider. Otherwise,
2114 // local would have won.
2115 DCHECK(conflicting_turl != GetDefaultSearchProvider());
2116 Remove(conflicting_turl);
2117 }
2118 // This TemplateURL was either removed or overwritten in the local model.
2119 // Remove the entry from the local data so it isn't pushed up to Sync.
2120 local_data->erase(guid);
2121 }
2122 // prepopulate_id 0 effectively means unspecified; i.e. that the turl isn't
2123 // a pre-populated one, so we want to ignore that case.
2124 } else if (sync_turl->prepopulate_id() != 0) {
2125 // Check for a turl with a conflicting prepopulate_id. This detects the case
2126 // where the user changes a prepopulated engine's keyword on one client,
2127 // then begins syncing on another client. We want to reflect this keyword
2128 // change to that prepopulated URL on other clients instead of assuming that
2129 // the modified TemplateURL is a new entity.
2130 TemplateURL* conflicting_prepopulated_turl =
2131 FindPrepopulatedTemplateURL(sync_turl->prepopulate_id());
2132
2133 // If we found a conflict, and the sync entity is better, apply the remote
2134 // changes locally. We consider |sync_turl| better if it's been modified
2135 // more recently and the local TemplateURL isn't yet known to sync. We will
2136 // consider the sync entity better even if the local TemplateURL is the
2137 // current default, since in this case being default does not necessarily
2138 // mean the user explicitly set this TemplateURL as default. If we didn't do
2139 // this, changes to the keywords of prepopulated default engines would never
2140 // be applied to other clients.
2141 // If we can't safely replace the local entry with the synced one, or merge
2142 // the relevant changes in, we give up and leave both intact.
2143 if (conflicting_prepopulated_turl &&
2144 !IsFromSync(conflicting_prepopulated_turl, sync_data) &&
2145 !IsLocalTemplateURLBetter(conflicting_prepopulated_turl, sync_turl,
2146 false)) {
2147 std::string guid = conflicting_prepopulated_turl->sync_guid();
2148 if (conflicting_prepopulated_turl == default_search_provider_) {
2149 bool pref_matched =
2150 prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID) ==
2151 default_search_provider_->sync_guid();
2152 // Update the existing engine in-place.
2153 Update(default_search_provider_, TemplateURL(sync_turl->data()));
2154 // If prefs::kSyncedDefaultSearchProviderGUID matched
2155 // |default_search_provider_|'s GUID before, then update it to match its
2156 // new GUID. If the pref didn't match before, then it probably refers to
2157 // a new search engine from Sync which just hasn't been added locally
2158 // yet, so leave it alone in that case.
2159 if (pref_matched) {
2160 prefs_->SetString(prefs::kSyncedDefaultSearchProviderGUID,
2161 default_search_provider_->sync_guid());
2162 }
2163
2164 should_add_sync_turl = false;
2165 } else {
2166 Remove(conflicting_prepopulated_turl);
2167 }
2168 // Remove the local data so it isn't written to sync.
2169 local_data->erase(guid);
2170 }
2171 }
2172
2173 if (should_add_sync_turl) {
2174 // Force the local ID to kInvalidTemplateURLID so we can add it.
2175 TemplateURLData data(sync_turl->data());
2176 data.id = kInvalidTemplateURLID;
2177 std::unique_ptr<TemplateURL> added_ptr =
2178 std::make_unique<TemplateURL>(data);
2179 TemplateURL* added = added_ptr.get();
2180 base::AutoReset<DefaultSearchChangeOrigin> change_origin(
2181 &dsp_change_origin_, DSP_CHANGE_SYNC_ADD);
2182 if (Add(std::move(added_ptr)))
2183 MaybeUpdateDSEViaPrefs(added);
2184 }
2185 }
2186
PatchMissingSyncGUIDs(OwnedTemplateURLVector * template_urls)2187 void TemplateURLService::PatchMissingSyncGUIDs(
2188 OwnedTemplateURLVector* template_urls) {
2189 DCHECK(template_urls);
2190 for (auto& template_url : *template_urls) {
2191 DCHECK(template_url);
2192 if (template_url->sync_guid().empty() &&
2193 (template_url->type() == TemplateURL::NORMAL)) {
2194 template_url->data_.GenerateSyncGUID();
2195 if (web_data_service_)
2196 web_data_service_->UpdateKeyword(template_url->data());
2197 }
2198 }
2199 }
2200
OnSyncedDefaultSearchProviderGUIDChanged()2201 void TemplateURLService::OnSyncedDefaultSearchProviderGUIDChanged() {
2202 base::AutoReset<DefaultSearchChangeOrigin> change_origin(
2203 &dsp_change_origin_, DSP_CHANGE_SYNC_PREF);
2204
2205 std::string new_guid =
2206 prefs_->GetString(prefs::kSyncedDefaultSearchProviderGUID);
2207 if (new_guid.empty()) {
2208 default_search_manager_.ClearUserSelectedDefaultSearchEngine();
2209 return;
2210 }
2211
2212 const TemplateURL* turl = GetTemplateURLForGUID(new_guid);
2213 if (turl)
2214 default_search_manager_.SetUserSelectedDefaultSearchEngine(turl->data());
2215 }
2216
2217 template <typename Container>
AddMatchingKeywordsHelper(const Container & keyword_to_turl_and_length,const base::string16 & prefix,bool supports_replacement_only,TURLsAndMeaningfulLengths * matches)2218 void TemplateURLService::AddMatchingKeywordsHelper(
2219 const Container& keyword_to_turl_and_length,
2220 const base::string16& prefix,
2221 bool supports_replacement_only,
2222 TURLsAndMeaningfulLengths* matches) {
2223 // Sanity check args.
2224 if (prefix.empty())
2225 return;
2226 DCHECK(matches);
2227
2228 // Find matching keyword range. Searches the element map for keywords
2229 // beginning with |prefix| and stores the endpoints of the resulting set in
2230 // |match_range|.
2231 const auto match_range(std::equal_range(
2232 keyword_to_turl_and_length.begin(), keyword_to_turl_and_length.end(),
2233 typename Container::value_type(prefix,
2234 TURLAndMeaningfulLength(nullptr, 0)),
2235 LessWithPrefix()));
2236
2237 // Add to vector of matching keywords.
2238 for (typename Container::const_iterator i(match_range.first);
2239 i != match_range.second; ++i) {
2240 if (!supports_replacement_only ||
2241 i->second.first->url_ref().SupportsReplacement(search_terms_data()))
2242 matches->push_back(i->second);
2243 }
2244 }
2245
FindPrepopulatedTemplateURL(int prepopulated_id)2246 TemplateURL* TemplateURLService::FindPrepopulatedTemplateURL(
2247 int prepopulated_id) {
2248 DCHECK(prepopulated_id);
2249 for (const auto& turl : template_urls_) {
2250 if (turl->prepopulate_id() == prepopulated_id)
2251 return turl.get();
2252 }
2253 return nullptr;
2254 }
2255
FindTemplateURLForExtension(const std::string & extension_id,TemplateURL::Type type)2256 TemplateURL* TemplateURLService::FindTemplateURLForExtension(
2257 const std::string& extension_id,
2258 TemplateURL::Type type) {
2259 DCHECK_NE(TemplateURL::NORMAL, type);
2260 for (const auto& turl : template_urls_) {
2261 if (turl->type() == type && turl->GetExtensionId() == extension_id)
2262 return turl.get();
2263 }
2264 return nullptr;
2265 }
2266
FindMatchingDefaultExtensionTemplateURL(const TemplateURLData & data)2267 TemplateURL* TemplateURLService::FindMatchingDefaultExtensionTemplateURL(
2268 const TemplateURLData& data) {
2269 for (const auto& turl : template_urls_) {
2270 if (turl->type() == TemplateURL::NORMAL_CONTROLLED_BY_EXTENSION &&
2271 turl->extension_info_->wants_to_be_default_engine &&
2272 TemplateURL::MatchesData(turl.get(), &data, search_terms_data()))
2273 return turl.get();
2274 }
2275 return nullptr;
2276 }
2277