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.h" 6 7 #include "base/i18n/icu_string_conversions.h" 8 #include "base/i18n/rtl.h" 9 #include "base/logging.h" 10 #include "base/string_number_conversions.h" 11 #include "base/utf_string_conversions.h" 12 #include "chrome/browser/metrics/user_metrics.h" 13 #include "chrome/browser/search_engines/search_engine_type.h" 14 #include "chrome/browser/search_engines/search_terms_data.h" 15 #include "chrome/browser/search_engines/template_url_model.h" 16 #include "chrome/common/url_constants.h" 17 #include "chrome/installer/util/google_update_settings.h" 18 #include "net/base/escape.h" 19 #include "ui/base/l10n/l10n_util.h" 20 #include "ui/gfx/favicon_size.h" 21 // TODO(pastarmovj): Remove google_update_settings and user_metrics when the 22 // CollectRLZMetrics function is not needed anymore. 23 24 // The TemplateURLRef has any number of terms that need to be replaced. Each of 25 // the terms is enclosed in braces. If the character preceeding the final 26 // brace is a ?, it indicates the term is optional and can be replaced with 27 // an empty string. 28 static const char kStartParameter = '{'; 29 static const char kEndParameter = '}'; 30 static const char kOptional = '?'; 31 32 // Known parameters found in the URL. 33 static const char kSearchTermsParameter[] = "searchTerms"; 34 static const char kSearchTermsParameterFull[] = "{searchTerms}"; 35 static const char kCountParameter[] = "count"; 36 static const char kStartIndexParameter[] = "startIndex"; 37 static const char kStartPageParameter[] = "startPage"; 38 static const char kLanguageParameter[] = "language"; 39 static const char kInputEncodingParameter[] = "inputEncoding"; 40 static const char kOutputEncodingParameter[] = "outputEncoding"; 41 42 static const char kGoogleAcceptedSuggestionParameter[] = 43 "google:acceptedSuggestion"; 44 // Host/Domain Google searches are relative to. 45 static const char kGoogleBaseURLParameter[] = "google:baseURL"; 46 static const char kGoogleBaseURLParameterFull[] = "{google:baseURL}"; 47 // Like google:baseURL, but for the Search Suggest capability. 48 static const char kGoogleBaseSuggestURLParameter[] = 49 "google:baseSuggestURL"; 50 static const char kGoogleBaseSuggestURLParameterFull[] = 51 "{google:baseSuggestURL}"; 52 static const char kGoogleOriginalQueryForSuggestionParameter[] = 53 "google:originalQueryForSuggestion"; 54 static const char kGoogleRLZParameter[] = "google:RLZ"; 55 // Same as kSearchTermsParameter, with no escaping. 56 static const char kGoogleUnescapedSearchTermsParameter[] = 57 "google:unescapedSearchTerms"; 58 static const char kGoogleUnescapedSearchTermsParameterFull[] = 59 "{google:unescapedSearchTerms}"; 60 61 // Display value for kSearchTermsParameter. 62 static const char kDisplaySearchTerms[] = "%s"; 63 64 // Display value for kGoogleUnescapedSearchTermsParameter. 65 static const char kDisplayUnescapedSearchTerms[] = "%S"; 66 67 // Used if the count parameter is not optional. Indicates we want 10 search 68 // results. 69 static const char kDefaultCount[] = "10"; 70 71 // Used if the parameter kOutputEncodingParameter is required. 72 static const char kOutputEncodingType[] = "UTF-8"; 73 74 TemplateURLRef::TemplateURLRef() { 75 Set(std::string(), 0, 0); 76 } 77 78 TemplateURLRef::TemplateURLRef(const std::string& url, 79 int index_offset, 80 int page_offset) 81 : url_(url), 82 index_offset_(index_offset), 83 page_offset_(page_offset), 84 parsed_(false), 85 valid_(false), 86 supports_replacements_(false) { 87 } 88 89 void TemplateURLRef::Set(const std::string& url, 90 int index_offset, 91 int page_offset) { 92 url_ = url; 93 index_offset_ = index_offset; 94 page_offset_ = page_offset; 95 InvalidateCachedValues(); 96 } 97 98 TemplateURLRef::~TemplateURLRef() { 99 } 100 101 bool TemplateURLRef::ParseParameter(size_t start, 102 size_t end, 103 std::string* url, 104 Replacements* replacements) const { 105 DCHECK(start != std::string::npos && 106 end != std::string::npos && end > start); 107 size_t length = end - start - 1; 108 bool optional = false; 109 if ((*url)[end - 1] == kOptional) { 110 optional = true; 111 length--; 112 } 113 std::string parameter(url->substr(start + 1, length)); 114 std::string full_parameter(url->substr(start, end - start + 1)); 115 // Remove the parameter from the string. 116 url->erase(start, end - start + 1); 117 if (parameter == kSearchTermsParameter) { 118 replacements->push_back(Replacement(SEARCH_TERMS, start)); 119 } else if (parameter == kCountParameter) { 120 if (!optional) 121 url->insert(start, kDefaultCount); 122 } else if (parameter == kStartIndexParameter) { 123 if (!optional) { 124 url->insert(start, base::IntToString(index_offset_)); 125 } 126 } else if (parameter == kStartPageParameter) { 127 if (!optional) { 128 url->insert(start, base::IntToString(page_offset_)); 129 } 130 } else if (parameter == kLanguageParameter) { 131 replacements->push_back(Replacement(LANGUAGE, start)); 132 } else if (parameter == kInputEncodingParameter) { 133 replacements->push_back(Replacement(ENCODING, start)); 134 } else if (parameter == kOutputEncodingParameter) { 135 if (!optional) 136 url->insert(start, kOutputEncodingType); 137 } else if (parameter == kGoogleAcceptedSuggestionParameter) { 138 replacements->push_back(Replacement(GOOGLE_ACCEPTED_SUGGESTION, start)); 139 } else if (parameter == kGoogleBaseURLParameter) { 140 replacements->push_back(Replacement(GOOGLE_BASE_URL, start)); 141 } else if (parameter == kGoogleBaseSuggestURLParameter) { 142 replacements->push_back(Replacement(GOOGLE_BASE_SUGGEST_URL, start)); 143 } else if (parameter == kGoogleOriginalQueryForSuggestionParameter) { 144 replacements->push_back(Replacement(GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION, 145 start)); 146 } else if (parameter == kGoogleRLZParameter) { 147 replacements->push_back(Replacement(GOOGLE_RLZ, start)); 148 } else if (parameter == kGoogleUnescapedSearchTermsParameter) { 149 replacements->push_back(Replacement(GOOGLE_UNESCAPED_SEARCH_TERMS, start)); 150 } else { 151 // It can be some garbage but can also be a javascript block. Put it back. 152 url->insert(start, full_parameter); 153 return false; 154 } 155 return true; 156 } 157 158 std::string TemplateURLRef::ParseURL(const std::string& url, 159 Replacements* replacements, 160 bool* valid) const { 161 *valid = false; 162 std::string parsed_url = url; 163 for (size_t last = 0; last != std::string::npos; ) { 164 last = parsed_url.find(kStartParameter, last); 165 if (last != std::string::npos) { 166 size_t template_end = parsed_url.find(kEndParameter, last); 167 if (template_end != std::string::npos) { 168 // Since we allow Javascript in the URL, {} pairs could be nested. Match 169 // only leaf pairs with supported parameters. 170 size_t next_template_start = parsed_url.find(kStartParameter, last + 1); 171 if (next_template_start == std::string::npos || 172 next_template_start > template_end) { 173 // If successful, ParseParameter erases from the string as such no 174 // need to update |last|. If failed, move |last| to the end of pair. 175 if (!ParseParameter(last, template_end, &parsed_url, replacements)) { 176 // |template_end| + 1 may be beyond the end of the string. 177 last = template_end; 178 } 179 } else { 180 last = next_template_start; 181 } 182 } else { 183 // Open brace without a closing brace, return. 184 return std::string(); 185 } 186 } 187 } 188 *valid = true; 189 return parsed_url; 190 } 191 192 void TemplateURLRef::ParseIfNecessary() const { 193 UIThreadSearchTermsData search_terms_data; 194 ParseIfNecessaryUsingTermsData(search_terms_data); 195 } 196 197 void TemplateURLRef::ParseIfNecessaryUsingTermsData( 198 const SearchTermsData& search_terms_data) const { 199 if (!parsed_) { 200 parsed_ = true; 201 parsed_url_ = ParseURL(url_, &replacements_, &valid_); 202 supports_replacements_ = false; 203 if (valid_) { 204 bool has_only_one_search_term = false; 205 for (Replacements::const_iterator i = replacements_.begin(); 206 i != replacements_.end(); ++i) { 207 if ((i->type == SEARCH_TERMS) || 208 (i->type == GOOGLE_UNESCAPED_SEARCH_TERMS)) { 209 if (has_only_one_search_term) { 210 has_only_one_search_term = false; 211 break; 212 } 213 has_only_one_search_term = true; 214 supports_replacements_ = true; 215 } 216 } 217 // Only parse the host/key if there is one search term. Technically there 218 // could be more than one term, but it's uncommon; so we punt. 219 if (has_only_one_search_term) 220 ParseHostAndSearchTermKey(search_terms_data); 221 } 222 } 223 } 224 225 void TemplateURLRef::ParseHostAndSearchTermKey( 226 const SearchTermsData& search_terms_data) const { 227 std::string url_string = url_; 228 ReplaceSubstringsAfterOffset(&url_string, 0, 229 kGoogleBaseURLParameterFull, 230 search_terms_data.GoogleBaseURLValue()); 231 ReplaceSubstringsAfterOffset(&url_string, 0, 232 kGoogleBaseSuggestURLParameterFull, 233 search_terms_data.GoogleBaseSuggestURLValue()); 234 235 GURL url(url_string); 236 if (!url.is_valid()) 237 return; 238 239 std::string query_string = url.query(); 240 if (query_string.empty()) 241 return; 242 243 url_parse::Component query, key, value; 244 query.len = static_cast<int>(query_string.size()); 245 while (url_parse::ExtractQueryKeyValue(query_string.c_str(), &query, &key, 246 &value)) { 247 if (key.is_nonempty() && value.is_nonempty()) { 248 std::string value_string = query_string.substr(value.begin, value.len); 249 if (value_string.find(kSearchTermsParameterFull, 0) != 250 std::string::npos || 251 value_string.find(kGoogleUnescapedSearchTermsParameterFull, 0) != 252 std::string::npos) { 253 search_term_key_ = query_string.substr(key.begin, key.len); 254 host_ = url.host(); 255 path_ = url.path(); 256 break; 257 } 258 } 259 } 260 } 261 262 // static 263 void TemplateURLRef::SetGoogleBaseURL(std::string* google_base_url) { 264 UIThreadSearchTermsData::SetGoogleBaseURL(google_base_url); 265 } 266 267 std::string TemplateURLRef::ReplaceSearchTerms( 268 const TemplateURL& host, 269 const string16& terms, 270 int accepted_suggestion, 271 const string16& original_query_for_suggestion) const { 272 UIThreadSearchTermsData search_terms_data; 273 return ReplaceSearchTermsUsingTermsData(host, 274 terms, 275 accepted_suggestion, 276 original_query_for_suggestion, 277 search_terms_data); 278 } 279 280 std::string TemplateURLRef::ReplaceSearchTermsUsingTermsData( 281 const TemplateURL& host, 282 const string16& terms, 283 int accepted_suggestion, 284 const string16& original_query_for_suggestion, 285 const SearchTermsData& search_terms_data) const { 286 ParseIfNecessaryUsingTermsData(search_terms_data); 287 if (!valid_) 288 return std::string(); 289 290 if (replacements_.empty()) 291 return parsed_url_; 292 293 // Determine if the search terms are in the query or before. We're escaping 294 // space as '+' in the former case and as '%20' in the latter case. 295 bool is_in_query = true; 296 for (Replacements::iterator i = replacements_.begin(); 297 i != replacements_.end(); ++i) { 298 if (i->type == SEARCH_TERMS) { 299 string16::size_type query_start = parsed_url_.find('?'); 300 is_in_query = query_start != string16::npos && 301 (static_cast<string16::size_type>(i->index) > query_start); 302 break; 303 } 304 } 305 306 string16 encoded_terms; 307 string16 encoded_original_query; 308 std::string input_encoding; 309 // If the search terms are in query - escape them respecting the encoding. 310 if (is_in_query) { 311 // Encode the search terms so that we know the encoding. 312 const std::vector<std::string>& encodings = host.input_encodings(); 313 for (size_t i = 0; i < encodings.size(); ++i) { 314 if (EscapeQueryParamValue(terms, 315 encodings[i].c_str(), true, 316 &encoded_terms)) { 317 if (!original_query_for_suggestion.empty()) { 318 EscapeQueryParamValue(original_query_for_suggestion, 319 encodings[i].c_str(), 320 true, 321 &encoded_original_query); 322 } 323 input_encoding = encodings[i]; 324 break; 325 } 326 } 327 if (input_encoding.empty()) { 328 encoded_terms = EscapeQueryParamValueUTF8(terms, true); 329 if (!original_query_for_suggestion.empty()) { 330 encoded_original_query = 331 EscapeQueryParamValueUTF8(original_query_for_suggestion, true); 332 } 333 input_encoding = "UTF-8"; 334 } 335 } else { 336 encoded_terms = UTF8ToUTF16(EscapePath(UTF16ToUTF8(terms))); 337 input_encoding = "UTF-8"; 338 } 339 340 std::string url = parsed_url_; 341 342 // replacements_ is ordered in ascending order, as such we need to iterate 343 // from the back. 344 for (Replacements::reverse_iterator i = replacements_.rbegin(); 345 i != replacements_.rend(); ++i) { 346 switch (i->type) { 347 case ENCODING: 348 url.insert(i->index, input_encoding); 349 break; 350 351 case GOOGLE_ACCEPTED_SUGGESTION: 352 if (accepted_suggestion == NO_SUGGESTION_CHOSEN) 353 url.insert(i->index, "aq=f&"); 354 else if (accepted_suggestion != NO_SUGGESTIONS_AVAILABLE) 355 url.insert(i->index, StringPrintf("aq=%d&", accepted_suggestion)); 356 break; 357 358 case GOOGLE_BASE_URL: 359 url.insert(i->index, search_terms_data.GoogleBaseURLValue()); 360 break; 361 362 case GOOGLE_BASE_SUGGEST_URL: 363 url.insert(i->index, search_terms_data.GoogleBaseSuggestURLValue()); 364 break; 365 366 case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION: 367 if (accepted_suggestion >= 0) 368 url.insert(i->index, "oq=" + UTF16ToUTF8(encoded_original_query) + 369 "&"); 370 break; 371 372 case GOOGLE_RLZ: { 373 // On platforms that don't have RLZ, we still want this branch 374 // to happen so that we replace the RLZ template with the 375 // empty string. (If we don't handle this case, we hit a 376 // NOTREACHED below.) 377 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) 378 string16 rlz_string = search_terms_data.GetRlzParameterValue(); 379 if (!rlz_string.empty()) { 380 rlz_string = L"rlz=" + rlz_string + L"&"; 381 url.insert(i->index, UTF16ToUTF8(rlz_string)); 382 } 383 #endif 384 break; 385 } 386 387 case GOOGLE_UNESCAPED_SEARCH_TERMS: { 388 std::string unescaped_terms; 389 base::UTF16ToCodepage(terms, input_encoding.c_str(), 390 base::OnStringConversionError::SKIP, 391 &unescaped_terms); 392 url.insert(i->index, std::string(unescaped_terms.begin(), 393 unescaped_terms.end())); 394 break; 395 } 396 397 case LANGUAGE: 398 url.insert(i->index, search_terms_data.GetApplicationLocale()); 399 break; 400 401 case SEARCH_TERMS: 402 url.insert(i->index, UTF16ToUTF8(encoded_terms)); 403 break; 404 405 default: 406 NOTREACHED(); 407 break; 408 } 409 } 410 411 return url; 412 } 413 414 bool TemplateURLRef::SupportsReplacement() const { 415 UIThreadSearchTermsData search_terms_data; 416 return SupportsReplacementUsingTermsData(search_terms_data); 417 } 418 419 bool TemplateURLRef::SupportsReplacementUsingTermsData( 420 const SearchTermsData& search_terms_data) const { 421 ParseIfNecessaryUsingTermsData(search_terms_data); 422 return valid_ && supports_replacements_; 423 } 424 425 bool TemplateURLRef::IsValid() const { 426 UIThreadSearchTermsData search_terms_data; 427 return IsValidUsingTermsData(search_terms_data); 428 } 429 430 bool TemplateURLRef::IsValidUsingTermsData( 431 const SearchTermsData& search_terms_data) const { 432 ParseIfNecessaryUsingTermsData(search_terms_data); 433 return valid_; 434 } 435 436 string16 TemplateURLRef::DisplayURL() const { 437 ParseIfNecessary(); 438 if (!valid_ || replacements_.empty()) 439 return UTF8ToUTF16(url_); 440 441 string16 result = UTF8ToUTF16(url_); 442 ReplaceSubstringsAfterOffset(&result, 0, 443 ASCIIToUTF16(kSearchTermsParameterFull), 444 ASCIIToUTF16(kDisplaySearchTerms)); 445 446 ReplaceSubstringsAfterOffset( 447 &result, 0, 448 ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull), 449 ASCIIToUTF16(kDisplayUnescapedSearchTerms)); 450 451 return result; 452 } 453 454 // static 455 std::string TemplateURLRef::DisplayURLToURLRef( 456 const string16& display_url) { 457 string16 result = display_url; 458 ReplaceSubstringsAfterOffset(&result, 0, ASCIIToUTF16(kDisplaySearchTerms), 459 ASCIIToUTF16(kSearchTermsParameterFull)); 460 ReplaceSubstringsAfterOffset( 461 &result, 0, 462 ASCIIToUTF16(kDisplayUnescapedSearchTerms), 463 ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull)); 464 return UTF16ToUTF8(result); 465 } 466 467 const std::string& TemplateURLRef::GetHost() const { 468 ParseIfNecessary(); 469 return host_; 470 } 471 472 const std::string& TemplateURLRef::GetPath() const { 473 ParseIfNecessary(); 474 return path_; 475 } 476 477 const std::string& TemplateURLRef::GetSearchTermKey() const { 478 ParseIfNecessary(); 479 return search_term_key_; 480 } 481 482 string16 TemplateURLRef::SearchTermToString16(const TemplateURL& host, 483 const std::string& term) const { 484 const std::vector<std::string>& encodings = host.input_encodings(); 485 string16 result; 486 487 std::string unescaped = 488 UnescapeURLComponent(term, UnescapeRule::REPLACE_PLUS_WITH_SPACE | 489 UnescapeRule::URL_SPECIAL_CHARS); 490 for (size_t i = 0; i < encodings.size(); ++i) { 491 if (base::CodepageToUTF16(unescaped, encodings[i].c_str(), 492 base::OnStringConversionError::FAIL, &result)) 493 return result; 494 } 495 496 // Always fall back on UTF-8 if it works. 497 if (base::CodepageToUTF16(unescaped, base::kCodepageUTF8, 498 base::OnStringConversionError::FAIL, &result)) 499 return result; 500 501 // When nothing worked, just use the escaped text. We have no idea what the 502 // encoding is. We need to substitute spaces for pluses ourselves since we're 503 // not sending it through an unescaper. 504 result = UTF8ToUTF16(term); 505 std::replace(result.begin(), result.end(), '+', ' '); 506 return result; 507 } 508 509 bool TemplateURLRef::HasGoogleBaseURLs() const { 510 ParseIfNecessary(); 511 for (size_t i = 0; i < replacements_.size(); ++i) { 512 if ((replacements_[i].type == GOOGLE_BASE_URL) || 513 (replacements_[i].type == GOOGLE_BASE_SUGGEST_URL)) 514 return true; 515 } 516 return false; 517 } 518 519 // static 520 bool TemplateURLRef::SameUrlRefs(const TemplateURLRef* ref1, 521 const TemplateURLRef* ref2) { 522 return ref1 == ref2 || (ref1 && ref2 && ref1->url() == ref2->url()); 523 } 524 525 void TemplateURLRef::CollectRLZMetrics() const { 526 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD) 527 ParseIfNecessary(); 528 for (size_t i = 0; i < replacements_.size(); ++i) { 529 // We are interesed in searches that were supposed to send the RLZ token. 530 if (replacements_[i].type == GOOGLE_RLZ) { 531 string16 brand; 532 // We only have RLZ tocken on a branded browser version. 533 if (GoogleUpdateSettings::GetBrand(&brand) && !brand.empty() && 534 !GoogleUpdateSettings::IsOrganic(brand)) { 535 // Now we know we should have had RLZ token check if there was one. 536 if (url().find("rlz=") != std::string::npos) 537 UserMetrics::RecordAction(UserMetricsAction("SearchWithRLZ")); 538 else 539 UserMetrics::RecordAction(UserMetricsAction("SearchWithoutRLZ")); 540 } 541 return; 542 } 543 } 544 #endif 545 } 546 547 void TemplateURLRef::InvalidateCachedValues() const { 548 supports_replacements_ = valid_ = parsed_ = false; 549 host_.clear(); 550 path_.clear(); 551 search_term_key_.clear(); 552 replacements_.clear(); 553 } 554 555 // TemplateURL ---------------------------------------------------------------- 556 557 // static 558 GURL TemplateURL::GenerateFaviconURL(const GURL& url) { 559 DCHECK(url.is_valid()); 560 GURL::Replacements rep; 561 562 const char favicon_path[] = "/favicon.ico"; 563 int favicon_path_len = arraysize(favicon_path) - 1; 564 565 rep.SetPath(favicon_path, url_parse::Component(0, favicon_path_len)); 566 rep.ClearUsername(); 567 rep.ClearPassword(); 568 rep.ClearQuery(); 569 rep.ClearRef(); 570 return url.ReplaceComponents(rep); 571 } 572 573 // static 574 bool TemplateURL::SupportsReplacement(const TemplateURL* turl) { 575 UIThreadSearchTermsData search_terms_data; 576 return SupportsReplacementUsingTermsData(turl, search_terms_data); 577 } 578 579 // static 580 bool TemplateURL::SupportsReplacementUsingTermsData( 581 const TemplateURL* turl, 582 const SearchTermsData& search_terms_data) { 583 return turl && turl->url() && 584 turl->url()->SupportsReplacementUsingTermsData(search_terms_data); 585 } 586 587 TemplateURL::TemplateURL() 588 : autogenerate_keyword_(false), 589 keyword_generated_(false), 590 show_in_default_list_(false), 591 safe_for_autoreplace_(false), 592 id_(0), 593 date_created_(base::Time::Now()), 594 created_by_policy_(false), 595 usage_count_(0), 596 search_engine_type_(SEARCH_ENGINE_OTHER), 597 logo_id_(kNoSearchEngineLogo), 598 prepopulate_id_(0) { 599 } 600 601 TemplateURL::~TemplateURL() { 602 } 603 604 string16 TemplateURL::AdjustedShortNameForLocaleDirection() const { 605 string16 bidi_safe_short_name = short_name_; 606 base::i18n::AdjustStringForLocaleDirection(&bidi_safe_short_name); 607 return bidi_safe_short_name; 608 } 609 610 void TemplateURL::SetSuggestionsURL(const std::string& suggestions_url, 611 int index_offset, 612 int page_offset) { 613 suggestions_url_.Set(suggestions_url, index_offset, page_offset); 614 } 615 616 void TemplateURL::SetURL(const std::string& url, 617 int index_offset, 618 int page_offset) { 619 url_.Set(url, index_offset, page_offset); 620 } 621 622 void TemplateURL::SetInstantURL(const std::string& url, 623 int index_offset, 624 int page_offset) { 625 instant_url_.Set(url, index_offset, page_offset); 626 } 627 628 void TemplateURL::set_keyword(const string16& keyword) { 629 // Case sensitive keyword matching is confusing. As such, we force all 630 // keywords to be lower case. 631 keyword_ = l10n_util::ToLower(keyword); 632 autogenerate_keyword_ = false; 633 } 634 635 string16 TemplateURL::keyword() const { 636 EnsureKeyword(); 637 return keyword_; 638 } 639 640 void TemplateURL::EnsureKeyword() const { 641 if (autogenerate_keyword_ && !keyword_generated_) { 642 // Generate a keyword and cache it. 643 keyword_ = TemplateURLModel::GenerateKeyword( 644 TemplateURLModel::GenerateSearchURL(this).GetWithEmptyPath(), true); 645 keyword_generated_ = true; 646 } 647 } 648 649 bool TemplateURL::ShowInDefaultList() const { 650 return show_in_default_list() && url() && url()->SupportsReplacement(); 651 } 652 653 void TemplateURL::SetFaviconURL(const GURL& url) { 654 for (std::vector<ImageRef>::iterator i = image_refs_.begin(); 655 i != image_refs_.end(); ++i) { 656 if (i->type == "image/x-icon" && 657 i->width == kFaviconSize && i->height == kFaviconSize) { 658 if (!url.is_valid()) 659 image_refs_.erase(i); 660 else 661 i->url = url; 662 return; 663 } 664 } 665 // Don't have one yet, add it. 666 if (url.is_valid()) { 667 add_image_ref( 668 TemplateURL::ImageRef("image/x-icon", kFaviconSize, 669 kFaviconSize, url)); 670 } 671 } 672 673 GURL TemplateURL::GetFaviconURL() const { 674 for (std::vector<ImageRef>::const_iterator i = image_refs_.begin(); 675 i != image_refs_.end(); ++i) { 676 if ((i->type == "image/x-icon" || i->type == "image/vnd.microsoft.icon") 677 && i->width == kFaviconSize && i->height == kFaviconSize) { 678 return i->url; 679 } 680 } 681 return GURL(); 682 } 683 684 void TemplateURL::InvalidateCachedValues() const { 685 url_.InvalidateCachedValues(); 686 suggestions_url_.InvalidateCachedValues(); 687 if (autogenerate_keyword_) { 688 keyword_.clear(); 689 keyword_generated_ = false; 690 } 691 } 692 693 std::string TemplateURL::GetExtensionId() const { 694 DCHECK(IsExtensionKeyword()); 695 return GURL(url_.url()).host(); 696 } 697 698 bool TemplateURL::IsExtensionKeyword() const { 699 return GURL(url_.url()).SchemeIs(chrome::kExtensionScheme); 700 } 701