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"
41 typedef SearchHostToURLsMap::TemplateURLSet TemplateURLSet
;
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
54 const char kReplacementTerm
[] = "blah.blah.blah.blah.blah";
56 bool TemplateURLsHaveSamePrefs(const TemplateURL
* url1
,
57 const TemplateURL
* url2
) {
60 return NULL
!= url1
&&
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();
76 class TemplateURLService::LessWithPrefix
{
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
)
103 default_search_provider_(NULL
),
104 is_default_search_managed_(false),
110 TemplateURLService::TemplateURLService(const Initializer
* initializers
,
117 default_search_provider_(NULL
),
118 is_default_search_managed_(false),
120 Init(initializers
, count
);
123 TemplateURLService::~TemplateURLService() {
125 DCHECK(service_
.get());
126 service_
->CancelRequest(load_handle_
);
129 STLDeleteElements(&template_urls_
);
133 string16
TemplateURLService::GenerateKeyword(const GURL
& url
,
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() != "/")))))
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()));
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
)))
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
;
189 GURL
TemplateURLService::GenerateSearchURL(const TemplateURL
* t_url
) {
191 UIThreadSearchTermsData search_terms_data
;
192 return GenerateSearchURLUsingTermsData(t_url
, search_terms_data
);
196 GURL
TemplateURLService::GenerateSearchURLUsingTermsData(
197 const TemplateURL
* t_url
,
198 const SearchTermsData
& search_terms_data
) {
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())
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
,
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
);
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
236 if (url
.is_valid() && !url
.host().empty())
237 return CanReplaceKeywordForHost(url
.host(), template_url_to_replace
);
241 void TemplateURLService::FindMatchingKeywords(
242 const string16
& prefix
,
243 bool support_replacement_only
,
244 std::vector
<string16
>* matches
) const {
245 // Sanity check args.
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;
257 const TemplateURL
* null_url
= NULL
;
260 // Find matching keyword range. Searches the element map for keywords
261 // beginning with |prefix| and stores the endpoints of the resulting set in
263 const std::pair
<KeywordToTemplateMap::const_iterator
,
264 KeywordToTemplateMap::const_iterator
> match_range(
266 keyword_to_template_map_
.begin(), keyword_to_template_map_
.end(),
267 KeywordToTemplateMap::value_type(prefix
, null_url
),
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
);
296 void TemplateURLService::Remove(const TemplateURL
* template_url
) {
297 RemoveNoNotify(template_url
);
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;
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())
330 pending_extension_ids_
.push_back(extension
->id());
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);
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
);
352 AddNoNotify(template_url
.release());
357 void TemplateURLService::UnregisterExtensionKeyword(
358 const Extension
* extension
) {
359 const TemplateURL
* url
= GetTemplateURLForExtension(extension
);
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())
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);
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
);
406 bool TemplateURLService::CanMakeDefault(const TemplateURL
* url
) {
407 return url
!= GetDefaultSearchProvider() &&
409 url
->url()->SupportsReplacement() &&
410 !is_default_search_managed();
413 void TemplateURLService::SetDefaultSearchProvider(const TemplateURL
* url
) {
414 if (is_default_search_managed_
) {
418 if (default_search_provider_
== url
)
420 SetDefaultSearchProviderNoNotify(url
);
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_
)
445 service_
= profile_
->GetWebDataService(Profile::EXPLICIT_ACCESS
);
447 if (service_
.get()) {
448 load_handle_
= service_
->GetKeywords(this);
450 ChangeToLoadedState();
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
463 // Results are null if the database went away or (most likely) wasn't
466 ChangeToLoadedState();
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
,
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.
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
);
522 // If we had a managed default, replace it with the first provider of
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
);
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
);
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.
572 *is_extension_keyword
= template_url
->IsExtensionKeyword();
573 return template_url
->AdjustedShortNameForLocaleDirection();
575 *is_extension_keyword
= false;
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
);
585 visits_to_add_
.push_back(*visit_details
.ptr());
587 UpdateKeywordSearchTermsForURL(*visit_details
.ptr());
588 } else if (type
== NotificationType::GOOGLE_URL_UPDATED
) {
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();
604 void TemplateURLService::RegisterUserPrefs(PrefService
* prefs
) {
605 prefs
->RegisterBooleanPref(prefs::kDefaultSearchProviderEnabled
,
607 PrefService::UNSYNCABLE_PREF
);
608 prefs
->RegisterStringPref(prefs::kDefaultSearchProviderName
,
610 PrefService::UNSYNCABLE_PREF
);
611 prefs
->RegisterStringPref(prefs::kDefaultSearchProviderID
,
613 PrefService::UNSYNCABLE_PREF
);
614 prefs
->RegisterStringPref(prefs::kDefaultSearchProviderPrepopulateID
,
616 PrefService::UNSYNCABLE_PREF
);
617 prefs
->RegisterStringPref(prefs::kDefaultSearchProviderSuggestURL
,
619 PrefService::UNSYNCABLE_PREF
);
620 prefs
->RegisterStringPref(prefs::kDefaultSearchProviderSearchURL
,
622 PrefService::UNSYNCABLE_PREF
);
623 prefs
->RegisterStringPref(prefs::kDefaultSearchProviderInstantURL
,
625 PrefService::UNSYNCABLE_PREF
);
626 prefs
->RegisterStringPref(prefs::kDefaultSearchProviderKeyword
,
628 PrefService::UNSYNCABLE_PREF
);
629 prefs
->RegisterStringPref(prefs::kDefaultSearchProviderIconURL
,
631 PrefService::UNSYNCABLE_PREF
);
632 prefs
->RegisterStringPref(prefs::kDefaultSearchProviderEncodings
,
634 PrefService::UNSYNCABLE_PREF
);
637 void TemplateURLService::SetKeywordSearchTermsForURL(const TemplateURL
* t_url
,
639 const string16
& term
) {
640 HistoryService
* history
= profile_
?
641 profile_
->GetHistoryService(Profile::EXPLICIT_ACCESS
) : NULL
;
644 history
->SetKeywordSearchTermsForURL(url
, t_url
->id(), term
);
647 void TemplateURLService::Init(const Initializer
* initializers
,
648 int num_initializers
) {
649 // Register for notifications.
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
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
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());
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
730 void TemplateURLService::AddToMaps(const TemplateURL
* template_url
) {
731 if (!template_url
->keyword().empty())
732 keyword_to_template_map_
[template_url
->keyword()] = template_url
;
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();
750 next_id_
= std::max(next_id_
, (*i
)->id());
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();
765 void TemplateURLService::ChangeToLoadedState() {
768 UIThreadSearchTermsData search_terms_data
;
769 provider_map_
.Init(template_urls_
, search_terms_data
);
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);
783 RegisterExtensionKeyword(extension
);
785 pending_extension_ids_
.clear();
788 void TemplateURLService::SaveDefaultSearchProviderToPrefs(
789 const TemplateURL
* t_url
) {
790 PrefService
* prefs
= GetPrefs();
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
;
802 std::string id_string
;
803 std::string prepopulate_id
;
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
,
838 PrefService
* prefs
= GetPrefs();
839 if (!prefs
|| !prefs
->HasPrefPath(prefs::kDefaultSearchProviderSearchURL
))
842 const PrefService::Preference
* pref
=
843 prefs
->FindPreference(prefs::kDefaultSearchProviderSearchURL
);
844 *is_managed
= pref
&& pref
->IsManaged();
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
);
862 UTF8ToUTF16(prefs
->GetString(prefs::kDefaultSearchProviderName
));
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
) {
885 base::StringToInt64(id_string
, &value
);
886 (*default_provider
)->set_id(value
);
888 if (!prepopulate_id
.empty() && !*is_managed
) {
890 base::StringToInt(prepopulate_id
, &value
);
891 (*default_provider
)->set_prepopulate_id(value
);
893 (*default_provider
)->set_show_in_default_list(true);
897 bool TemplateURLService::CanReplaceKeywordForHost(
898 const std::string
& host
,
899 const TemplateURL
** to_replace
) {
900 const TemplateURLSet
* urls
= provider_map_
.GetURLsForHost(host
);
902 for (TemplateURLSet::const_iterator i
= urls
->begin();
903 i
!= urls
->end(); ++i
) {
904 const TemplateURL
* url
= *i
;
905 if (CanReplace(url
)) {
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
) {
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
;
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()) {
959 const TemplateURLSet
* urls_for_host
=
960 provider_map_
.GetURLsForHost(row
.url().host());
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
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
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
1016 if (!t_url
.safe_for_autoreplace())
1022 HistoryService
* history
=
1023 profile_
->GetHistoryService(Profile::EXPLICIT_ACCESS
);
1027 GURL
url(URLFixerUpper::FixupURL(UTF16ToUTF8(t_url
.keyword()),
1029 if (!url
.is_valid())
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);
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
,
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
1058 (*query_terms
)[key_string
] = std::string();
1059 DCHECK(valid_term_count
> 0);
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
);
1093 void TemplateURLService::UpdateDefaultSearch() {
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;
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.
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()))
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
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());
1140 // AddNoNotify will take ownership of new_template, so it's safe to
1142 TemplateURL
* new_template
= new_default_from_prefs
.release();
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
1155 TemplateURL
* new_template
= new_default_from_prefs
.release();
1157 new_template
->set_created_by_policy(true);
1158 AddNoNotify(new_template
);
1160 SetDefaultSearchProviderNoNotify(new_template
);
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());
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())
1187 // If not, use the first of the templates.
1188 if (!template_urls_
.empty()) {
1189 return template_urls_
[0];
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
;
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);
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
);
1221 if (!is_default_search_managed_
)
1222 SaveDefaultSearchProviderToPrefs(url
);
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
);
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(),
1245 if (i
== template_urls_
.end())
1248 if (template_url
== default_search_provider_
) {
1249 // Should never delete the default search provider.
1254 RemoveFromMaps(template_url
);
1256 // Remove it from the vector containing all TemplateURLs.
1257 template_urls_
.erase(i
);
1260 service_
->RemoveKeyword(*template_url
);
1263 Source
<Profile
> source(profile_
);
1264 TemplateURLID id
= template_url
->id();
1265 NotificationService::current()->Notify(
1266 NotificationType::TEMPLATE_URL_REMOVED
,
1268 Details
<TemplateURLID
>(&id
));
1271 // We own the TemplateURL and need to delete it.
1272 delete template_url
;
1275 void TemplateURLService::NotifyObservers() {
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|.
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
);
1322 service_
->RemoveKeyword(*template_url
);
1323 delete template_url
;