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