Home | History | Annotate | Download | only in search_engines
      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