Print Preview: Changing displayed error message when PDF Viewer is missing.
[chromium-blink-merge.git] / chrome / browser / search_engines / template_url_service.cc
blob6ee102fde01437800689932477b98737fb2973c9
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "chrome/browser/search_engines/template_url_service.h"
7 #include "base/command_line.h"
8 #include "base/environment.h"
9 #include "base/i18n/case_conversion.h"
10 #include "base/stl_util-inl.h"
11 #include "base/string_number_conversions.h"
12 #include "base/string_split.h"
13 #include "base/string_util.h"
14 #include "base/threading/thread_restrictions.h"
15 #include "base/utf_string_conversions.h"
16 #include "chrome/browser/extensions/extension_service.h"
17 #include "chrome/browser/google/google_url_tracker.h"
18 #include "chrome/browser/history/history.h"
19 #include "chrome/browser/history/history_notifications.h"
20 #include "chrome/browser/net/url_fixer_upper.h"
21 #include "chrome/browser/prefs/pref_service.h"
22 #include "chrome/browser/prefs/pref_set_observer.h"
23 #include "chrome/browser/profiles/profile.h"
24 #include "chrome/browser/rlz/rlz.h"
25 #include "chrome/browser/search_engines/search_host_to_urls_map.h"
26 #include "chrome/browser/search_engines/search_terms_data.h"
27 #include "chrome/browser/search_engines/template_url.h"
28 #include "chrome/browser/search_engines/template_url_service_observer.h"
29 #include "chrome/browser/search_engines/template_url_prepopulate_data.h"
30 #include "chrome/browser/search_engines/util.h"
31 #include "chrome/common/chrome_switches.h"
32 #include "chrome/common/env_vars.h"
33 #include "chrome/common/extensions/extension.h"
34 #include "chrome/common/pref_names.h"
35 #include "chrome/common/url_constants.h"
36 #include "content/common/notification_service.h"
37 #include "net/base/net_util.h"
38 #include "ui/base/l10n/l10n_util.h"
40 using base::Time;
41 typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet;
43 namespace {
45 // String in the URL that is replaced by the search term.
46 const char kSearchTermParameter[] = "{searchTerms}";
48 // String in Initializer that is replaced with kSearchTermParameter.
49 const char kTemplateParameter[] = "%s";
51 // Term used when generating a search url. Use something obscure so that on
52 // the rare case the term replaces the URL it's unlikely another keyword would
53 // have the same url.
54 const char kReplacementTerm[] = "blah.blah.blah.blah.blah";
56 bool TemplateURLsHaveSamePrefs(const TemplateURL* url1,
57 const TemplateURL* url2) {
58 if (url1 == url2)
59 return true;
60 return NULL != url1 &&
61 NULL != url2 &&
62 url1->short_name() == url2->short_name() &&
63 url1->keyword() == url2->keyword() &&
64 TemplateURLRef::SameUrlRefs(url1->url(), url2->url()) &&
65 TemplateURLRef::SameUrlRefs(url1->suggestions_url(),
66 url2->suggestions_url()) &&
67 url1->GetFaviconURL() == url2->GetFaviconURL() &&
68 url1->safe_for_autoreplace() == url2->safe_for_autoreplace() &&
69 url1->show_in_default_list() == url2->show_in_default_list() &&
70 url1->input_encodings() == url2->input_encodings();
73 } // namespace
76 class TemplateURLService::LessWithPrefix {
77 public:
78 // We want to find the set of keywords that begin with a prefix. The STL
79 // algorithms will return the set of elements that are "equal to" the
80 // prefix, where "equal(x, y)" means "!(cmp(x, y) || cmp(y, x))". When
81 // cmp() is the typical std::less<>, this results in lexicographic equality;
82 // we need to extend this to mark a prefix as "not less than" a keyword it
83 // begins, which will cause the desired elements to be considered "equal to"
84 // the prefix. Note: this is still a strict weak ordering, as required by
85 // equal_range() (though I will not prove that here).
87 // Unfortunately the calling convention is not "prefix and element" but
88 // rather "two elements", so we pass the prefix as a fake "element" which has
89 // a NULL KeywordDataElement pointer.
90 bool operator()(const KeywordToTemplateMap::value_type& elem1,
91 const KeywordToTemplateMap::value_type& elem2) const {
92 return (elem1.second == NULL) ?
93 (elem2.first.compare(0, elem1.first.length(), elem1.first) > 0) :
94 (elem1.first < elem2.first);
98 TemplateURLService::TemplateURLService(Profile* profile)
99 : profile_(profile),
100 loaded_(false),
101 load_failed_(false),
102 load_handle_(0),
103 default_search_provider_(NULL),
104 is_default_search_managed_(false),
105 next_id_(1) {
106 DCHECK(profile_);
107 Init(NULL, 0);
110 TemplateURLService::TemplateURLService(const Initializer* initializers,
111 const int count)
112 : profile_(NULL),
113 loaded_(false),
114 load_failed_(false),
115 load_handle_(0),
116 service_(NULL),
117 default_search_provider_(NULL),
118 is_default_search_managed_(false),
119 next_id_(1) {
120 Init(initializers, count);
123 TemplateURLService::~TemplateURLService() {
124 if (load_handle_) {
125 DCHECK(service_.get());
126 service_->CancelRequest(load_handle_);
129 STLDeleteElements(&template_urls_);
132 // static
133 string16 TemplateURLService::GenerateKeyword(const GURL& url,
134 bool autodetected) {
135 // Don't autogenerate keywords for referrers that are the result of a form
136 // submission (TODO: right now we approximate this by checking for the URL
137 // having a query, but we should replace this with a call to WebCore to see if
138 // the originating page was actually a form submission), anything other than
139 // http, or referrers with a path.
141 // If we relax the path constraint, we need to be sure to sanitize the path
142 // elements and update AutocompletePopup to look for keywords using the path.
143 // See http://b/issue?id=863583.
144 if (!url.is_valid() ||
145 (autodetected && (url.has_query() || !url.SchemeIs(chrome::kHttpScheme) ||
146 ((url.path() != "") && (url.path() != "/")))))
147 return string16();
149 // Strip "www." off the front of the keyword; otherwise the keyword won't work
150 // properly. See http://code.google.com/p/chromium/issues/detail?id=6984 .
151 return net::StripWWW(UTF8ToUTF16(url.host()));
154 // static
155 string16 TemplateURLService::CleanUserInputKeyword(const string16& keyword) {
156 // Remove the scheme.
157 string16 result(base::i18n::ToLower(keyword));
158 TrimWhitespace(result, TRIM_ALL, &result);
159 url_parse::Component scheme_component;
160 if (url_parse::ExtractScheme(UTF16ToUTF8(keyword).c_str(),
161 static_cast<int>(keyword.length()),
162 &scheme_component)) {
163 // If the scheme isn't "http" or "https", bail. The user isn't trying to
164 // type a web address, but rather an FTP, file:, or other scheme URL, or a
165 // search query with some sort of initial operator (e.g. "site:").
166 if (result.compare(0, scheme_component.end(),
167 ASCIIToUTF16(chrome::kHttpScheme)) &&
168 result.compare(0, scheme_component.end(),
169 ASCIIToUTF16(chrome::kHttpsScheme)))
170 return string16();
172 // Include trailing ':'.
173 result.erase(0, scheme_component.end() + 1);
174 // Many schemes usually have "//" after them, so strip it too.
175 const string16 after_scheme(ASCIIToUTF16("//"));
176 if (result.compare(0, after_scheme.length(), after_scheme) == 0)
177 result.erase(0, after_scheme.length());
180 // Remove leading "www.".
181 result = net::StripWWW(result);
183 // Remove trailing "/".
184 return (result.length() > 0 && result[result.length() - 1] == '/') ?
185 result.substr(0, result.length() - 1) : result;
188 // static
189 GURL TemplateURLService::GenerateSearchURL(const TemplateURL* t_url) {
190 DCHECK(t_url);
191 UIThreadSearchTermsData search_terms_data;
192 return GenerateSearchURLUsingTermsData(t_url, search_terms_data);
195 // static
196 GURL TemplateURLService::GenerateSearchURLUsingTermsData(
197 const TemplateURL* t_url,
198 const SearchTermsData& search_terms_data) {
199 DCHECK(t_url);
200 const TemplateURLRef* search_ref = t_url->url();
201 // Extension keywords don't have host-based search URLs.
202 if (!search_ref || !search_ref->IsValidUsingTermsData(search_terms_data) ||
203 t_url->IsExtensionKeyword())
204 return GURL();
206 if (!search_ref->SupportsReplacementUsingTermsData(search_terms_data))
207 return GURL(search_ref->url());
209 return GURL(search_ref->ReplaceSearchTermsUsingTermsData(
210 *t_url, ASCIIToUTF16(kReplacementTerm),
211 TemplateURLRef::NO_SUGGESTIONS_AVAILABLE,
212 string16(), search_terms_data));
215 bool TemplateURLService::CanReplaceKeyword(
216 const string16& keyword,
217 const GURL& url,
218 const TemplateURL** template_url_to_replace) {
219 DCHECK(!keyword.empty()); // This should only be called for non-empty
220 // keywords. If we need to support empty kewords
221 // the code needs to change slightly.
222 const TemplateURL* existing_url = GetTemplateURLForKeyword(keyword);
223 if (existing_url) {
224 // We already have a TemplateURL for this keyword. Only allow it to be
225 // replaced if the TemplateURL can be replaced.
226 if (template_url_to_replace)
227 *template_url_to_replace = existing_url;
228 return CanReplace(existing_url);
231 // We don't have a TemplateURL with keyword. Only allow a new one if there
232 // isn't a TemplateURL for the specified host, or there is one but it can
233 // be replaced. We do this to ensure that if the user assigns a different
234 // keyword to a generated TemplateURL, we won't regenerate another keyword for
235 // the same host.
236 if (url.is_valid() && !url.host().empty())
237 return CanReplaceKeywordForHost(url.host(), template_url_to_replace);
238 return true;
241 void TemplateURLService::FindMatchingKeywords(
242 const string16& prefix,
243 bool support_replacement_only,
244 std::vector<string16>* matches) const {
245 // Sanity check args.
246 if (prefix.empty())
247 return;
248 DCHECK(matches != NULL);
249 DCHECK(matches->empty()); // The code for exact matches assumes this.
251 // Visual Studio 2010 has problems converting NULL to the null pointer for
252 // std::pair. See http://connect.microsoft.com/VisualStudio/feedback/details/520043/error-converting-from-null-to-a-pointer-type-in-std-pair
253 // It will work if we pass nullptr.
254 #if defined(_MSC_VER) && _MSC_VER >= 1600
255 const TemplateURL* null_url = nullptr;
256 #else
257 const TemplateURL* null_url = NULL;
258 #endif
260 // Find matching keyword range. Searches the element map for keywords
261 // beginning with |prefix| and stores the endpoints of the resulting set in
262 // |match_range|.
263 const std::pair<KeywordToTemplateMap::const_iterator,
264 KeywordToTemplateMap::const_iterator> match_range(
265 std::equal_range(
266 keyword_to_template_map_.begin(), keyword_to_template_map_.end(),
267 KeywordToTemplateMap::value_type(prefix, null_url),
268 LessWithPrefix()));
270 // Return vector of matching keywords.
271 for (KeywordToTemplateMap::const_iterator i(match_range.first);
272 i != match_range.second; ++i) {
273 DCHECK(i->second->url());
274 if (!support_replacement_only || i->second->url()->SupportsReplacement())
275 matches->push_back(i->first);
279 const TemplateURL* TemplateURLService::GetTemplateURLForKeyword(
280 const string16& keyword) const {
281 KeywordToTemplateMap::const_iterator elem(
282 keyword_to_template_map_.find(keyword));
283 return (elem == keyword_to_template_map_.end()) ? NULL : elem->second;
286 const TemplateURL* TemplateURLService::GetTemplateURLForHost(
287 const std::string& host) const {
288 return provider_map_.GetTemplateURLForHost(host);
291 void TemplateURLService::Add(TemplateURL* template_url) {
292 AddNoNotify(template_url);
293 NotifyObservers();
296 void TemplateURLService::Remove(const TemplateURL* template_url) {
297 RemoveNoNotify(template_url);
298 NotifyObservers();
301 void TemplateURLService::RemoveAutoGeneratedBetween(Time created_after,
302 Time created_before) {
303 bool should_notify = false;
304 for (size_t i = 0; i < template_urls_.size();) {
305 if (template_urls_[i]->date_created() >= created_after &&
306 (created_before.is_null() ||
307 template_urls_[i]->date_created() < created_before) &&
308 CanReplace(template_urls_[i])) {
309 RemoveNoNotify(template_urls_[i]);
310 should_notify = true;
311 } else {
312 ++i;
315 if (should_notify)
316 NotifyObservers();
319 void TemplateURLService::RemoveAutoGeneratedSince(Time created_after) {
320 RemoveAutoGeneratedBetween(created_after, Time());
323 void TemplateURLService::RegisterExtensionKeyword(const Extension* extension) {
324 // TODO(mpcomplete): disable the keyword when the extension is disabled.
325 if (extension->omnibox_keyword().empty())
326 return;
328 Load();
329 if (!loaded_) {
330 pending_extension_ids_.push_back(extension->id());
331 return;
334 const TemplateURL* existing_url = GetTemplateURLForExtension(extension);
335 string16 keyword = UTF8ToUTF16(extension->omnibox_keyword());
337 scoped_ptr<TemplateURL> template_url(new TemplateURL);
338 template_url->set_short_name(UTF8ToUTF16(extension->name()));
339 template_url->set_keyword(keyword);
340 // This URL is not actually used for navigation. It holds the extension's
341 // ID, as well as forcing the TemplateURL to be treated as a search keyword.
342 template_url->SetURL(
343 std::string(chrome::kExtensionScheme) + "://" +
344 extension->id() + "/?q={searchTerms}", 0, 0);
345 template_url->set_safe_for_autoreplace(false);
347 if (existing_url) {
348 // TODO(mpcomplete): only replace if the user hasn't changed the keyword.
349 // (We don't have UI for that yet).
350 UpdateNoNotify(existing_url, *template_url);
351 } else {
352 AddNoNotify(template_url.release());
354 NotifyObservers();
357 void TemplateURLService::UnregisterExtensionKeyword(
358 const Extension* extension) {
359 const TemplateURL* url = GetTemplateURLForExtension(extension);
360 if (url)
361 Remove(url);
364 const TemplateURL* TemplateURLService::GetTemplateURLForExtension(
365 const Extension* extension) const {
366 for (TemplateURLVector::const_iterator i = template_urls_.begin();
367 i != template_urls_.end(); ++i) {
368 if ((*i)->IsExtensionKeyword() && (*i)->url()->GetHost() == extension->id())
369 return *i;
372 return NULL;
375 std::vector<const TemplateURL*> TemplateURLService::GetTemplateURLs() const {
376 return template_urls_;
379 void TemplateURLService::IncrementUsageCount(const TemplateURL* url) {
380 DCHECK(url && find(template_urls_.begin(), template_urls_.end(), url) !=
381 template_urls_.end());
382 const_cast<TemplateURL*>(url)->set_usage_count(url->usage_count() + 1);
383 if (service_.get())
384 service_.get()->UpdateKeyword(*url);
387 void TemplateURLService::ResetTemplateURL(const TemplateURL* url,
388 const string16& title,
389 const string16& keyword,
390 const std::string& search_url) {
391 TemplateURL new_url(*url);
392 new_url.set_short_name(title);
393 new_url.set_keyword(keyword);
394 if ((new_url.url() && search_url.empty()) ||
395 (!new_url.url() && !search_url.empty()) ||
396 (new_url.url() && new_url.url()->url() != search_url)) {
397 // The urls have changed, reset the favicon url.
398 new_url.SetFaviconURL(GURL());
399 new_url.SetURL(search_url, 0, 0);
401 new_url.set_safe_for_autoreplace(false);
402 UpdateNoNotify(url, new_url);
403 NotifyObservers();
406 bool TemplateURLService::CanMakeDefault(const TemplateURL* url) {
407 return url != GetDefaultSearchProvider() &&
408 url->url() &&
409 url->url()->SupportsReplacement() &&
410 !is_default_search_managed();
413 void TemplateURLService::SetDefaultSearchProvider(const TemplateURL* url) {
414 if (is_default_search_managed_) {
415 NOTREACHED();
416 return;
418 if (default_search_provider_ == url)
419 return;
420 SetDefaultSearchProviderNoNotify(url);
421 NotifyObservers();
424 const TemplateURL* TemplateURLService::GetDefaultSearchProvider() {
425 if (loaded_ && !load_failed_)
426 return default_search_provider_;
428 // We're not loaded, rely on the default search provider stored in prefs.
429 return initial_default_search_provider_.get();
432 void TemplateURLService::AddObserver(TemplateURLServiceObserver* observer) {
433 model_observers_.AddObserver(observer);
436 void TemplateURLService::RemoveObserver(TemplateURLServiceObserver* observer) {
437 model_observers_.RemoveObserver(observer);
440 void TemplateURLService::Load() {
441 if (loaded_ || load_handle_)
442 return;
444 if (!service_.get())
445 service_ = profile_->GetWebDataService(Profile::EXPLICIT_ACCESS);
447 if (service_.get()) {
448 load_handle_ = service_->GetKeywords(this);
449 } else {
450 ChangeToLoadedState();
451 NotifyLoaded();
455 void TemplateURLService::OnWebDataServiceRequestDone(
456 WebDataService::Handle h,
457 const WDTypedResult* result) {
458 // Reset the load_handle so that we don't try and cancel the load in
459 // the destructor.
460 load_handle_ = 0;
462 if (!result) {
463 // Results are null if the database went away or (most likely) wasn't
464 // loaded.
465 load_failed_ = true;
466 ChangeToLoadedState();
467 NotifyLoaded();
468 return;
471 // initial_default_search_provider_ is only needed before we've finished
472 // loading. Now that we've loaded we can nuke it.
473 initial_default_search_provider_.reset();
474 is_default_search_managed_ = false;
476 std::vector<TemplateURL*> template_urls;
477 const TemplateURL* default_search_provider = NULL;
478 int new_resource_keyword_version = 0;
479 GetSearchProvidersUsingKeywordResult(*result,
480 service_.get(),
481 GetPrefs(),
482 &template_urls,
483 &default_search_provider,
484 &new_resource_keyword_version);
486 bool database_specified_a_default = NULL != default_search_provider;
488 // Check if default search provider is now managed.
489 scoped_ptr<TemplateURL> default_from_prefs;
490 LoadDefaultSearchProviderFromPrefs(&default_from_prefs,
491 &is_default_search_managed_);
493 // Remove entries that were created because of policy as they may have
494 // changed since the database was saved.
495 RemoveProvidersCreatedByPolicy(&template_urls,
496 &default_search_provider,
497 default_from_prefs.get());
499 if (is_default_search_managed_) {
500 SetTemplateURLs(template_urls);
502 if (TemplateURLsHaveSamePrefs(default_search_provider,
503 default_from_prefs.get())) {
504 // The value from the preferences was previously stored in the database.
505 // Reuse it.
506 } else {
507 // The value from the preferences takes over.
509 // AddNoNotify will take ownership of default_from_prefs so it is safe to
510 // release. If it's null, there's no ownership to worry about :-)
511 TemplateURL* managed_default = default_from_prefs.release();
512 if (managed_default) {
513 managed_default->set_created_by_policy(true);
514 managed_default->set_id(0);
515 AddNoNotify(managed_default);
516 default_search_provider = managed_default;
519 // Note that this saves the default search provider to prefs.
520 SetDefaultSearchProviderNoNotify(default_search_provider);
521 } else {
522 // If we had a managed default, replace it with the first provider of
523 // the list.
524 if (database_specified_a_default &&
525 NULL == default_search_provider &&
526 !template_urls.empty())
527 default_search_provider = template_urls[0];
529 // If the default search provider existed previously, then just
530 // set the member variable. Otherwise, we'll set it using the method
531 // to ensure that it is saved properly after its id is set.
532 if (default_search_provider && default_search_provider->id() != 0) {
533 default_search_provider_ = default_search_provider;
534 default_search_provider = NULL;
536 SetTemplateURLs(template_urls);
538 if (default_search_provider) {
539 // Note that this saves the default search provider to prefs.
540 SetDefaultSearchProvider(default_search_provider);
541 } else {
542 // Always save the default search provider to prefs. That way we don't
543 // have to worry about it being out of sync.
544 if (default_search_provider_)
545 SaveDefaultSearchProviderToPrefs(default_search_provider_);
549 // This initializes provider_map_ which should be done before
550 // calling UpdateKeywordSearchTermsForURL.
551 ChangeToLoadedState();
553 // Index any visits that occurred before we finished loading.
554 for (size_t i = 0; i < visits_to_add_.size(); ++i)
555 UpdateKeywordSearchTermsForURL(visits_to_add_[i]);
556 visits_to_add_.clear();
558 if (new_resource_keyword_version && service_.get())
559 service_->SetBuiltinKeywordVersion(new_resource_keyword_version);
561 NotifyObservers();
562 NotifyLoaded();
565 string16 TemplateURLService::GetKeywordShortName(const string16& keyword,
566 bool* is_extension_keyword) {
567 const TemplateURL* template_url = GetTemplateURLForKeyword(keyword);
569 // TODO(sky): Once LocationBarView adds a listener to the TemplateURLService
570 // to track changes to the model, this should become a DCHECK.
571 if (template_url) {
572 *is_extension_keyword = template_url->IsExtensionKeyword();
573 return template_url->AdjustedShortNameForLocaleDirection();
575 *is_extension_keyword = false;
576 return string16();
579 void TemplateURLService::Observe(NotificationType type,
580 const NotificationSource& source,
581 const NotificationDetails& details) {
582 if (type == NotificationType::HISTORY_URL_VISITED) {
583 Details<history::URLVisitedDetails> visit_details(details);
584 if (!loaded())
585 visits_to_add_.push_back(*visit_details.ptr());
586 else
587 UpdateKeywordSearchTermsForURL(*visit_details.ptr());
588 } else if (type == NotificationType::GOOGLE_URL_UPDATED) {
589 if (loaded_)
590 GoogleBaseURLChanged();
591 } else if (type == NotificationType::PREF_CHANGED) {
592 const std::string* pref_name = Details<std::string>(details).ptr();
593 if (!pref_name || default_search_prefs_->IsObserved(*pref_name)) {
594 // A preference related to default search engine has changed.
595 // Update the model if needed.
596 UpdateDefaultSearch();
598 } else {
599 NOTREACHED();
603 // static
604 void TemplateURLService::RegisterUserPrefs(PrefService* prefs) {
605 prefs->RegisterBooleanPref(prefs::kDefaultSearchProviderEnabled,
606 true,
607 PrefService::UNSYNCABLE_PREF);
608 prefs->RegisterStringPref(prefs::kDefaultSearchProviderName,
609 std::string(),
610 PrefService::UNSYNCABLE_PREF);
611 prefs->RegisterStringPref(prefs::kDefaultSearchProviderID,
612 std::string(),
613 PrefService::UNSYNCABLE_PREF);
614 prefs->RegisterStringPref(prefs::kDefaultSearchProviderPrepopulateID,
615 std::string(),
616 PrefService::UNSYNCABLE_PREF);
617 prefs->RegisterStringPref(prefs::kDefaultSearchProviderSuggestURL,
618 std::string(),
619 PrefService::UNSYNCABLE_PREF);
620 prefs->RegisterStringPref(prefs::kDefaultSearchProviderSearchURL,
621 std::string(),
622 PrefService::UNSYNCABLE_PREF);
623 prefs->RegisterStringPref(prefs::kDefaultSearchProviderInstantURL,
624 std::string(),
625 PrefService::UNSYNCABLE_PREF);
626 prefs->RegisterStringPref(prefs::kDefaultSearchProviderKeyword,
627 std::string(),
628 PrefService::UNSYNCABLE_PREF);
629 prefs->RegisterStringPref(prefs::kDefaultSearchProviderIconURL,
630 std::string(),
631 PrefService::UNSYNCABLE_PREF);
632 prefs->RegisterStringPref(prefs::kDefaultSearchProviderEncodings,
633 std::string(),
634 PrefService::UNSYNCABLE_PREF);
637 void TemplateURLService::SetKeywordSearchTermsForURL(const TemplateURL* t_url,
638 const GURL& url,
639 const string16& term) {
640 HistoryService* history = profile_ ?
641 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS) : NULL;
642 if (!history)
643 return;
644 history->SetKeywordSearchTermsForURL(url, t_url->id(), term);
647 void TemplateURLService::Init(const Initializer* initializers,
648 int num_initializers) {
649 // Register for notifications.
650 if (profile_) {
651 // TODO(sky): bug 1166191. The keywords should be moved into the history
652 // db, which will mean we no longer need this notification and the history
653 // backend can handle automatically adding the search terms as the user
654 // navigates.
655 registrar_.Add(this, NotificationType::HISTORY_URL_VISITED,
656 Source<Profile>(profile_->GetOriginalProfile()));
657 PrefService* prefs = GetPrefs();
658 default_search_prefs_.reset(
659 PrefSetObserver::CreateDefaultSearchPrefSetObserver(prefs, this));
661 registrar_.Add(this, NotificationType::GOOGLE_URL_UPDATED,
662 NotificationService::AllSources());
664 if (num_initializers > 0) {
665 // This path is only hit by test code and is used to simulate a loaded
666 // TemplateURLService.
667 ChangeToLoadedState();
669 // Add specific initializers, if any.
670 for (int i(0); i < num_initializers; ++i) {
671 DCHECK(initializers[i].keyword);
672 DCHECK(initializers[i].url);
673 DCHECK(initializers[i].content);
675 size_t template_position =
676 std::string(initializers[i].url).find(kTemplateParameter);
677 DCHECK(template_position != std::string::npos);
678 std::string osd_url(initializers[i].url);
679 osd_url.replace(template_position, arraysize(kTemplateParameter) - 1,
680 kSearchTermParameter);
682 // TemplateURLService ends up owning the TemplateURL, don't try and free
683 // it.
684 TemplateURL* template_url = new TemplateURL();
685 template_url->set_keyword(UTF8ToUTF16(initializers[i].keyword));
686 template_url->set_short_name(UTF8ToUTF16(initializers[i].content));
687 template_url->SetURL(osd_url, 0, 0);
688 AddNoNotify(template_url);
692 // Initialize default search.
693 UpdateDefaultSearch();
695 // Request a server check for the correct Google URL if Google is the
696 // default search engine, not in headless mode and not in Chrome Frame.
697 if (initial_default_search_provider_.get()) {
698 const TemplateURLRef* default_provider_ref =
699 initial_default_search_provider_->url();
700 if (default_provider_ref && default_provider_ref->HasGoogleBaseURLs()) {
701 scoped_ptr<base::Environment> env(base::Environment::Create());
702 if (!env->HasVar(env_vars::kHeadless) &&
703 !CommandLine::ForCurrentProcess()->HasSwitch(switches::kChromeFrame))
704 GoogleURLTracker::RequestServerCheck();
709 void TemplateURLService::RemoveFromMaps(const TemplateURL* template_url) {
710 if (!template_url->keyword().empty())
711 keyword_to_template_map_.erase(template_url->keyword());
712 if (loaded_)
713 provider_map_.Remove(template_url);
716 void TemplateURLService::RemoveFromKeywordMapByPointer(
717 const TemplateURL* template_url) {
718 DCHECK(template_url);
719 for (KeywordToTemplateMap::iterator i = keyword_to_template_map_.begin();
720 i != keyword_to_template_map_.end(); ++i) {
721 if (i->second == template_url) {
722 keyword_to_template_map_.erase(i);
723 // A given TemplateURL only occurs once in the map. As soon as we find the
724 // entry, stop.
725 break;
730 void TemplateURLService::AddToMaps(const TemplateURL* template_url) {
731 if (!template_url->keyword().empty())
732 keyword_to_template_map_[template_url->keyword()] = template_url;
733 if (loaded_) {
734 UIThreadSearchTermsData search_terms_data;
735 provider_map_.Add(template_url, search_terms_data);
739 void TemplateURLService::SetTemplateURLs(
740 const std::vector<TemplateURL*>& urls) {
741 // Add mappings for the new items.
743 // First, add the items that already have id's, so that the next_id_
744 // gets properly set.
745 for (std::vector<TemplateURL*>::const_iterator i = urls.begin();
746 i != urls.end();
747 ++i) {
748 if ((*i)->id() == 0)
749 continue;
750 next_id_ = std::max(next_id_, (*i)->id());
751 AddToMaps(*i);
752 template_urls_.push_back(*i);
755 // Next add the new items that don't have id's.
756 for (std::vector<TemplateURL*>::const_iterator i = urls.begin();
757 i != urls.end();
758 ++i) {
759 if ((*i)->id() != 0)
760 continue;
761 AddNoNotify(*i);
765 void TemplateURLService::ChangeToLoadedState() {
766 DCHECK(!loaded_);
768 UIThreadSearchTermsData search_terms_data;
769 provider_map_.Init(template_urls_, search_terms_data);
770 loaded_ = true;
773 void TemplateURLService::NotifyLoaded() {
774 NotificationService::current()->Notify(
775 NotificationType::TEMPLATE_URL_SERVICE_LOADED,
776 Source<TemplateURLService>(this),
777 NotificationService::NoDetails());
779 for (size_t i = 0; i < pending_extension_ids_.size(); ++i) {
780 const Extension* extension = profile_->GetExtensionService()->
781 GetExtensionById(pending_extension_ids_[i], true);
782 if (extension)
783 RegisterExtensionKeyword(extension);
785 pending_extension_ids_.clear();
788 void TemplateURLService::SaveDefaultSearchProviderToPrefs(
789 const TemplateURL* t_url) {
790 PrefService* prefs = GetPrefs();
791 if (!prefs)
792 return;
794 bool enabled = false;
795 std::string search_url;
796 std::string suggest_url;
797 std::string instant_url;
798 std::string icon_url;
799 std::string encodings;
800 std::string short_name;
801 std::string keyword;
802 std::string id_string;
803 std::string prepopulate_id;
804 if (t_url) {
805 enabled = true;
806 if (t_url->url())
807 search_url = t_url->url()->url();
808 if (t_url->suggestions_url())
809 suggest_url = t_url->suggestions_url()->url();
810 if (t_url->instant_url())
811 instant_url = t_url->instant_url()->url();
812 GURL icon_gurl = t_url->GetFaviconURL();
813 if (!icon_gurl.is_empty())
814 icon_url = icon_gurl.spec();
815 encodings = JoinString(t_url->input_encodings(), ';');
816 short_name = UTF16ToUTF8(t_url->short_name());
817 keyword = UTF16ToUTF8(t_url->keyword());
818 id_string = base::Int64ToString(t_url->id());
819 prepopulate_id = base::Int64ToString(t_url->prepopulate_id());
821 prefs->SetBoolean(prefs::kDefaultSearchProviderEnabled, enabled);
822 prefs->SetString(prefs::kDefaultSearchProviderSearchURL, search_url);
823 prefs->SetString(prefs::kDefaultSearchProviderSuggestURL, suggest_url);
824 prefs->SetString(prefs::kDefaultSearchProviderInstantURL, instant_url);
825 prefs->SetString(prefs::kDefaultSearchProviderIconURL, icon_url);
826 prefs->SetString(prefs::kDefaultSearchProviderEncodings, encodings);
827 prefs->SetString(prefs::kDefaultSearchProviderName, short_name);
828 prefs->SetString(prefs::kDefaultSearchProviderKeyword, keyword);
829 prefs->SetString(prefs::kDefaultSearchProviderID, id_string);
830 prefs->SetString(prefs::kDefaultSearchProviderPrepopulateID, prepopulate_id);
832 prefs->ScheduleSavePersistentPrefs();
835 bool TemplateURLService::LoadDefaultSearchProviderFromPrefs(
836 scoped_ptr<TemplateURL>* default_provider,
837 bool* is_managed) {
838 PrefService* prefs = GetPrefs();
839 if (!prefs || !prefs->HasPrefPath(prefs::kDefaultSearchProviderSearchURL))
840 return false;
842 const PrefService::Preference* pref =
843 prefs->FindPreference(prefs::kDefaultSearchProviderSearchURL);
844 *is_managed = pref && pref->IsManaged();
846 bool enabled =
847 prefs->GetBoolean(prefs::kDefaultSearchProviderEnabled);
848 std::string suggest_url =
849 prefs->GetString(prefs::kDefaultSearchProviderSuggestURL);
850 std::string search_url =
851 prefs->GetString(prefs::kDefaultSearchProviderSearchURL);
852 std::string instant_url =
853 prefs->GetString(prefs::kDefaultSearchProviderInstantURL);
855 if (!enabled || (suggest_url.empty() && search_url.empty())) {
856 // The user doesn't want a default search provider.
857 default_provider->reset(NULL);
858 return true;
861 string16 name =
862 UTF8ToUTF16(prefs->GetString(prefs::kDefaultSearchProviderName));
863 string16 keyword =
864 UTF8ToUTF16(prefs->GetString(prefs::kDefaultSearchProviderKeyword));
865 std::string icon_url =
866 prefs->GetString(prefs::kDefaultSearchProviderIconURL);
867 std::string encodings =
868 prefs->GetString(prefs::kDefaultSearchProviderEncodings);
869 std::string id_string = prefs->GetString(prefs::kDefaultSearchProviderID);
870 std::string prepopulate_id =
871 prefs->GetString(prefs::kDefaultSearchProviderPrepopulateID);
873 default_provider->reset(new TemplateURL());
874 (*default_provider)->set_short_name(name);
875 (*default_provider)->SetURL(search_url, 0, 0);
876 (*default_provider)->SetSuggestionsURL(suggest_url, 0, 0);
877 (*default_provider)->SetInstantURL(instant_url, 0, 0);
878 (*default_provider)->set_keyword(keyword);
879 (*default_provider)->SetFaviconURL(GURL(icon_url));
880 std::vector<std::string> encodings_vector;
881 base::SplitString(encodings, ';', &encodings_vector);
882 (*default_provider)->set_input_encodings(encodings_vector);
883 if (!id_string.empty() && !*is_managed) {
884 int64 value;
885 base::StringToInt64(id_string, &value);
886 (*default_provider)->set_id(value);
888 if (!prepopulate_id.empty() && !*is_managed) {
889 int value;
890 base::StringToInt(prepopulate_id, &value);
891 (*default_provider)->set_prepopulate_id(value);
893 (*default_provider)->set_show_in_default_list(true);
894 return true;
897 bool TemplateURLService::CanReplaceKeywordForHost(
898 const std::string& host,
899 const TemplateURL** to_replace) {
900 const TemplateURLSet* urls = provider_map_.GetURLsForHost(host);
901 if (urls) {
902 for (TemplateURLSet::const_iterator i = urls->begin();
903 i != urls->end(); ++i) {
904 const TemplateURL* url = *i;
905 if (CanReplace(url)) {
906 if (to_replace)
907 *to_replace = url;
908 return true;
913 if (to_replace)
914 *to_replace = NULL;
915 return !urls;
918 bool TemplateURLService::CanReplace(const TemplateURL* t_url) {
919 return (t_url != default_search_provider_ && !t_url->show_in_default_list() &&
920 t_url->safe_for_autoreplace());
923 void TemplateURLService::UpdateNoNotify(const TemplateURL* existing_turl,
924 const TemplateURL& new_values) {
925 DCHECK(loaded_);
926 DCHECK(existing_turl);
927 DCHECK(find(template_urls_.begin(), template_urls_.end(), existing_turl) !=
928 template_urls_.end());
930 if (!existing_turl->keyword().empty())
931 keyword_to_template_map_.erase(existing_turl->keyword());
933 // This call handles copying over the values (while retaining the id).
934 UIThreadSearchTermsData search_terms_data;
935 provider_map_.Update(existing_turl, new_values, search_terms_data);
937 if (!existing_turl->keyword().empty())
938 keyword_to_template_map_[existing_turl->keyword()] = existing_turl;
940 if (service_.get())
941 service_->UpdateKeyword(*existing_turl);
943 if (default_search_provider_ == existing_turl)
944 SetDefaultSearchProviderNoNotify(existing_turl);
947 PrefService* TemplateURLService::GetPrefs() {
948 return profile_ ? profile_->GetPrefs() : NULL;
951 void TemplateURLService::UpdateKeywordSearchTermsForURL(
952 const history::URLVisitedDetails& details) {
953 const history::URLRow& row = details.row;
954 if (!row.url().is_valid() ||
955 !row.url().parsed_for_possibly_invalid_spec().query.is_nonempty()) {
956 return;
959 const TemplateURLSet* urls_for_host =
960 provider_map_.GetURLsForHost(row.url().host());
961 if (!urls_for_host)
962 return;
964 QueryTerms query_terms;
965 bool built_terms = false; // Most URLs won't match a TemplateURLs host;
966 // so we lazily build the query_terms.
967 const std::string path = row.url().path();
969 for (TemplateURLSet::const_iterator i = urls_for_host->begin();
970 i != urls_for_host->end(); ++i) {
971 const TemplateURLRef* search_ref = (*i)->url();
973 // Count the URL against a TemplateURL if the host and path of the
974 // visited URL match that of the TemplateURL as well as the search term's
975 // key of the TemplateURL occurring in the visited url.
977 // NOTE: Even though we're iterating over TemplateURLs indexed by the host
978 // of the URL we still need to call GetHost on the search_ref. In
979 // particular, GetHost returns an empty string if search_ref doesn't support
980 // replacement or isn't valid for use in keyword search terms.
982 if (search_ref && search_ref->GetHost() == row.url().host() &&
983 search_ref->GetPath() == path) {
984 if (!built_terms && !BuildQueryTerms(row.url(), &query_terms)) {
985 // No query terms. No need to continue with the rest of the
986 // TemplateURLs.
987 return;
989 built_terms = true;
991 if (PageTransition::StripQualifier(details.transition) ==
992 PageTransition::KEYWORD) {
993 // The visit is the result of the user entering a keyword, generate a
994 // KEYWORD_GENERATED visit for the KEYWORD so that the keyword typed
995 // count is boosted.
996 AddTabToSearchVisit(**i);
999 QueryTerms::iterator terms_iterator =
1000 query_terms.find(search_ref->GetSearchTermKey());
1001 if (terms_iterator != query_terms.end() &&
1002 !terms_iterator->second.empty()) {
1003 SetKeywordSearchTermsForURL(
1004 *i, row.url(), search_ref->SearchTermToString16(*(*i),
1005 terms_iterator->second));
1011 void TemplateURLService::AddTabToSearchVisit(const TemplateURL& t_url) {
1012 // Only add visits for entries the user hasn't modified. If the user modified
1013 // the entry the keyword may no longer correspond to the host name. It may be
1014 // possible to do something more sophisticated here, but it's so rare as to
1015 // not be worth it.
1016 if (!t_url.safe_for_autoreplace())
1017 return;
1019 if (!profile_)
1020 return;
1022 HistoryService* history =
1023 profile_->GetHistoryService(Profile::EXPLICIT_ACCESS);
1024 if (!history)
1025 return;
1027 GURL url(URLFixerUpper::FixupURL(UTF16ToUTF8(t_url.keyword()),
1028 std::string()));
1029 if (!url.is_valid())
1030 return;
1032 // Synthesize a visit for the keyword. This ensures the url for the keyword is
1033 // autocompleted even if the user doesn't type the url in directly.
1034 history->AddPage(url, NULL, 0, GURL(),
1035 PageTransition::KEYWORD_GENERATED,
1036 history::RedirectList(), history::SOURCE_BROWSED, false);
1039 // static
1040 bool TemplateURLService::BuildQueryTerms(const GURL& url,
1041 QueryTerms* query_terms) {
1042 url_parse::Component query = url.parsed_for_possibly_invalid_spec().query;
1043 url_parse::Component key, value;
1044 size_t valid_term_count = 0;
1045 while (url_parse::ExtractQueryKeyValue(url.spec().c_str(), &query, &key,
1046 &value)) {
1047 if (key.is_nonempty() && value.is_nonempty()) {
1048 std::string key_string = url.spec().substr(key.begin, key.len);
1049 std::string value_string = url.spec().substr(value.begin, value.len);
1050 QueryTerms::iterator query_terms_iterator =
1051 query_terms->find(key_string);
1052 if (query_terms_iterator != query_terms->end()) {
1053 if (!query_terms_iterator->second.empty() &&
1054 query_terms_iterator->second != value_string) {
1055 // The term occurs in multiple places with different values. Treat
1056 // this as if the term doesn't occur by setting the value to an empty
1057 // string.
1058 (*query_terms)[key_string] = std::string();
1059 DCHECK(valid_term_count > 0);
1060 valid_term_count--;
1062 } else {
1063 valid_term_count++;
1064 (*query_terms)[key_string] = value_string;
1068 return (valid_term_count > 0);
1071 void TemplateURLService::GoogleBaseURLChanged() {
1072 bool something_changed = false;
1073 for (size_t i = 0; i < template_urls_.size(); ++i) {
1074 const TemplateURL* t_url = template_urls_[i];
1075 if ((t_url->url() && t_url->url()->HasGoogleBaseURLs()) ||
1076 (t_url->suggestions_url() &&
1077 t_url->suggestions_url()->HasGoogleBaseURLs())) {
1078 RemoveFromKeywordMapByPointer(t_url);
1079 t_url->InvalidateCachedValues();
1080 if (!t_url->keyword().empty())
1081 keyword_to_template_map_[t_url->keyword()] = t_url;
1082 something_changed = true;
1086 if (something_changed && loaded_) {
1087 UIThreadSearchTermsData search_terms_data;
1088 provider_map_.UpdateGoogleBaseURLs(search_terms_data);
1089 NotifyObservers();
1093 void TemplateURLService::UpdateDefaultSearch() {
1094 if (!loaded_) {
1095 // Set |initial_default_search_provider_| from the preferences. We use this
1096 // value for default search provider until the database has been loaded.
1097 if (!LoadDefaultSearchProviderFromPrefs(&initial_default_search_provider_,
1098 &is_default_search_managed_)) {
1099 // Prefs does not specify, so rely on the prepopulated engines. This
1100 // should happen only the first time Chrome is started.
1101 initial_default_search_provider_.reset(
1102 TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(GetPrefs()));
1103 is_default_search_managed_ = false;
1105 return;
1107 // Load the default search specified in prefs.
1108 scoped_ptr<TemplateURL> new_default_from_prefs;
1109 bool new_is_default_managed = false;
1110 // Load the default from prefs. It's possible that it won't succeed
1111 // because we are in the middle of doing SaveDefaultSearchProviderToPrefs()
1112 // and all the preference items have not been saved. In that case, we
1113 // don't have yet a default. It would be much better if we could save
1114 // preferences in batches and trigger notifications at the end.
1115 LoadDefaultSearchProviderFromPrefs(&new_default_from_prefs,
1116 &new_is_default_managed);
1117 if (!is_default_search_managed_ && !new_is_default_managed) {
1118 // We're not interested in cases where the default was and remains
1119 // unmanaged. In that case, preferences have no impact on the default.
1120 return;
1122 if (is_default_search_managed_ && new_is_default_managed) {
1123 // The default was managed and remains managed. Update the default only
1124 // if it has changed; we don't want to respond to changes triggered by
1125 // SaveDefaultSearchProviderToPrefs.
1126 if (TemplateURLsHaveSamePrefs(default_search_provider_,
1127 new_default_from_prefs.get()))
1128 return;
1129 if (new_default_from_prefs.get() == NULL) {
1130 // default_search_provider_ can't be NULL otherwise
1131 // TemplateURLsHaveSamePrefs would have returned true. Remove this now
1132 // invalid value.
1133 const TemplateURL* old_default = default_search_provider_;
1134 SetDefaultSearchProviderNoNotify(NULL);
1135 RemoveNoNotify(old_default);
1136 } else if (default_search_provider_) {
1137 new_default_from_prefs->set_created_by_policy(true);
1138 UpdateNoNotify(default_search_provider_, *new_default_from_prefs.get());
1139 } else {
1140 // AddNoNotify will take ownership of new_template, so it's safe to
1141 // release.
1142 TemplateURL* new_template = new_default_from_prefs.release();
1143 if (new_template) {
1144 new_template->set_created_by_policy(true);
1145 AddNoNotify(new_template);
1147 SetDefaultSearchProviderNoNotify(new_template);
1149 } else if (!is_default_search_managed_ && new_is_default_managed) {
1150 // The default used to be unmanaged and is now managed. Add the new
1151 // managed default to the list of URLs and set it as default.
1152 is_default_search_managed_ = new_is_default_managed;
1153 // AddNoNotify will take ownership of new_template, so it's safe to
1154 // release.
1155 TemplateURL* new_template = new_default_from_prefs.release();
1156 if (new_template) {
1157 new_template->set_created_by_policy(true);
1158 AddNoNotify(new_template);
1160 SetDefaultSearchProviderNoNotify(new_template);
1161 } else {
1162 // The default was managed and is no longer.
1163 DCHECK(is_default_search_managed_ && !new_is_default_managed);
1164 is_default_search_managed_ = new_is_default_managed;
1165 // If we had a default, delete the previous default if created by policy
1166 // and set a likely default.
1167 if (NULL != default_search_provider_ &&
1168 default_search_provider_->created_by_policy()) {
1169 const TemplateURL* old_default = default_search_provider_;
1170 default_search_provider_ = NULL;
1171 RemoveNoNotify(old_default);
1173 SetDefaultSearchProviderNoNotify(FindNewDefaultSearchProvider());
1175 NotifyObservers();
1178 const TemplateURL* TemplateURLService::FindNewDefaultSearchProvider() {
1179 // See if the prepoluated default still exists.
1180 scoped_ptr<TemplateURL> prepopulated_default(
1181 TemplateURLPrepopulateData::GetPrepopulatedDefaultSearch(GetPrefs()));
1182 for (TemplateURLVector::iterator i = template_urls_.begin();
1183 i != template_urls_.end(); ) {
1184 if ((*i)->prepopulate_id() == prepopulated_default->prepopulate_id())
1185 return *i;
1187 // If not, use the first of the templates.
1188 if (!template_urls_.empty()) {
1189 return template_urls_[0];
1191 return NULL;
1194 void TemplateURLService::SetDefaultSearchProviderNoNotify(
1195 const TemplateURL* url) {
1196 DCHECK(!url || find(template_urls_.begin(), template_urls_.end(), url) !=
1197 template_urls_.end());
1198 default_search_provider_ = url;
1200 if (url) {
1201 TemplateURL* modifiable_url = const_cast<TemplateURL*>(url);
1202 // Don't mark the url as edited, otherwise we won't be able to rev the
1203 // template urls we ship with.
1204 modifiable_url->set_show_in_default_list(true);
1205 if (service_.get())
1206 service_.get()->UpdateKeyword(*url);
1208 const TemplateURLRef* url_ref = url->url();
1209 if (url_ref && url_ref->HasGoogleBaseURLs()) {
1210 GoogleURLTracker::RequestServerCheck();
1211 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
1212 // Needs to be evaluated. See http://crbug.com/62328.
1213 base::ThreadRestrictions::ScopedAllowIO allow_io;
1214 RLZTracker::RecordProductEvent(rlz_lib::CHROME,
1215 rlz_lib::CHROME_OMNIBOX,
1216 rlz_lib::SET_TO_GOOGLE);
1217 #endif
1221 if (!is_default_search_managed_)
1222 SaveDefaultSearchProviderToPrefs(url);
1224 if (service_.get())
1225 service_->SetDefaultSearchProvider(url);
1228 void TemplateURLService::AddNoNotify(TemplateURL* template_url) {
1229 DCHECK(template_url);
1230 DCHECK(template_url->id() == 0);
1231 DCHECK(find(template_urls_.begin(), template_urls_.end(), template_url) ==
1232 template_urls_.end());
1233 template_url->set_id(++next_id_);
1234 template_urls_.push_back(template_url);
1235 AddToMaps(template_url);
1237 if (service_.get())
1238 service_->AddKeyword(*template_url);
1241 void TemplateURLService::RemoveNoNotify(const TemplateURL* template_url) {
1242 TemplateURLVector::iterator i = find(template_urls_.begin(),
1243 template_urls_.end(),
1244 template_url);
1245 if (i == template_urls_.end())
1246 return;
1248 if (template_url == default_search_provider_) {
1249 // Should never delete the default search provider.
1250 NOTREACHED();
1251 return;
1254 RemoveFromMaps(template_url);
1256 // Remove it from the vector containing all TemplateURLs.
1257 template_urls_.erase(i);
1259 if (service_.get())
1260 service_->RemoveKeyword(*template_url);
1262 if (profile_) {
1263 Source<Profile> source(profile_);
1264 TemplateURLID id = template_url->id();
1265 NotificationService::current()->Notify(
1266 NotificationType::TEMPLATE_URL_REMOVED,
1267 source,
1268 Details<TemplateURLID>(&id));
1271 // We own the TemplateURL and need to delete it.
1272 delete template_url;
1275 void TemplateURLService::NotifyObservers() {
1276 if (!loaded_)
1277 return;
1279 FOR_EACH_OBSERVER(TemplateURLServiceObserver, model_observers_,
1280 OnTemplateURLServiceChanged());
1283 // |template_urls| are the TemplateURLs loaded from the database.
1284 // |default_search_provider| points to one of them, if it was set in the db.
1285 // |default_from_prefs| is the default search provider from the preferences.
1286 // Check |is_default_search_managed_| to determine if it was set by policy.
1288 // This function removes from the vector and the database all the TemplateURLs
1289 // that were set by policy, unless it is the current default search provider
1290 // and matches what is set by a managed preference.
1291 void TemplateURLService::RemoveProvidersCreatedByPolicy(
1292 std::vector<TemplateURL*>* template_urls,
1293 const TemplateURL** default_search_provider,
1294 const TemplateURL* default_from_prefs) {
1295 DCHECK(template_urls);
1296 DCHECK(default_search_provider);
1297 for (std::vector<TemplateURL*>::iterator i = template_urls->begin();
1298 i != template_urls->end(); ) {
1299 TemplateURL* template_url = *i;
1300 if (template_url->created_by_policy()) {
1301 if (template_url == *default_search_provider &&
1302 is_default_search_managed_ &&
1303 TemplateURLsHaveSamePrefs(template_url,
1304 default_from_prefs)) {
1305 // If the database specified a default search provider that was set
1306 // by policy, and the default search provider from the preferences
1307 // is also set by policy and they are the same, keep the entry in the
1308 // database and the |default_search_provider|.
1309 ++i;
1310 continue;
1313 // The database loaded a managed |default_search_provider|, but it has
1314 // been updated in the prefs. Remove it from the database, and update the
1315 // |default_search_provider| pointer here.
1316 if (*default_search_provider &&
1317 (*default_search_provider)->id() == template_url->id())
1318 *default_search_provider = NULL;
1320 i = template_urls->erase(i);
1321 if (service_.get())
1322 service_->RemoveKeyword(*template_url);
1323 delete template_url;
1324 } else {
1325 ++i;