Home | History | Annotate | Download | only in search_engines
      1 // Copyright (c) 2012 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/basictypes.h"
      8 #include "base/command_line.h"
      9 #include "base/format_macros.h"
     10 #include "base/guid.h"
     11 #include "base/i18n/case_conversion.h"
     12 #include "base/i18n/icu_string_conversions.h"
     13 #include "base/i18n/rtl.h"
     14 #include "base/logging.h"
     15 #include "base/metrics/field_trial.h"
     16 #include "base/rand_util.h"
     17 #include "base/strings/string_number_conversions.h"
     18 #include "base/strings/string_split.h"
     19 #include "base/strings/string_util.h"
     20 #include "base/strings/stringprintf.h"
     21 #include "base/strings/utf_string_conversions.h"
     22 #include "chrome/browser/google/google_util.h"
     23 #include "chrome/browser/search_engines/search_terms_data.h"
     24 #include "chrome/browser/search_engines/template_url_service.h"
     25 #include "chrome/common/chrome_switches.h"
     26 #include "chrome/common/chrome_version_info.h"
     27 #include "chrome/common/url_constants.h"
     28 #include "extensions/common/constants.h"
     29 #include "google_apis/google_api_keys.h"
     30 #include "net/base/escape.h"
     31 #include "net/base/mime_util.h"
     32 #include "ui/base/l10n/l10n_util.h"
     33 
     34 namespace {
     35 
     36 // The TemplateURLRef has any number of terms that need to be replaced. Each of
     37 // the terms is enclosed in braces. If the character preceeding the final
     38 // brace is a ?, it indicates the term is optional and can be replaced with
     39 // an empty string.
     40 const char kStartParameter = '{';
     41 const char kEndParameter = '}';
     42 const char kOptional = '?';
     43 
     44 // Known parameters found in the URL.
     45 const char kSearchTermsParameter[] = "searchTerms";
     46 const char kSearchTermsParameterFull[] = "{searchTerms}";
     47 const char kCountParameter[] = "count";
     48 const char kStartIndexParameter[] = "startIndex";
     49 const char kStartPageParameter[] = "startPage";
     50 const char kLanguageParameter[] = "language";
     51 const char kInputEncodingParameter[] = "inputEncoding";
     52 const char kOutputEncodingParameter[] = "outputEncoding";
     53 
     54 const char kGoogleAssistedQueryStatsParameter[] = "google:assistedQueryStats";
     55 
     56 // Host/Domain Google searches are relative to.
     57 const char kGoogleBaseURLParameter[] = "google:baseURL";
     58 const char kGoogleBaseURLParameterFull[] = "{google:baseURL}";
     59 
     60 // Like google:baseURL, but for the Search Suggest capability.
     61 const char kGoogleBaseSuggestURLParameter[] = "google:baseSuggestURL";
     62 const char kGoogleBaseSuggestURLParameterFull[] = "{google:baseSuggestURL}";
     63 const char kGoogleCursorPositionParameter[] = "google:cursorPosition";
     64 const char kGoogleInstantEnabledParameter[] = "google:instantEnabledParameter";
     65 const char kGoogleInstantExtendedEnabledParameter[] =
     66     "google:instantExtendedEnabledParameter";
     67 const char kGoogleInstantExtendedEnabledKey[] =
     68     "google:instantExtendedEnabledKey";
     69 const char kGoogleInstantExtendedEnabledKeyFull[] =
     70     "{google:instantExtendedEnabledKey}";
     71 const char kGoogleNTPIsThemedParameter[] = "google:ntpIsThemedParameter";
     72 const char kGoogleOmniboxStartMarginParameter[] =
     73     "google:omniboxStartMarginParameter";
     74 const char kGoogleOriginalQueryForSuggestionParameter[] =
     75     "google:originalQueryForSuggestion";
     76 const char kGooglePageClassificationParameter[] = "google:pageClassification";
     77 const char kGoogleRLZParameter[] = "google:RLZ";
     78 const char kGoogleSearchClient[] = "google:searchClient";
     79 const char kGoogleSearchFieldtrialParameter[] =
     80     "google:searchFieldtrialParameter";
     81 const char kGoogleSourceIdParameter[] = "google:sourceId";
     82 const char kGoogleSuggestAPIKeyParameter[] = "google:suggestAPIKeyParameter";
     83 const char kGoogleSuggestClient[] = "google:suggestClient";
     84 const char kGoogleZeroPrefixUrlParameter[] = "google:zeroPrefixUrl";
     85 
     86 // Same as kSearchTermsParameter, with no escaping.
     87 const char kGoogleUnescapedSearchTermsParameter[] =
     88     "google:unescapedSearchTerms";
     89 const char kGoogleUnescapedSearchTermsParameterFull[] =
     90     "{google:unescapedSearchTerms}";
     91 
     92 const char kGoogleImageSearchSource[] = "google:imageSearchSource";
     93 const char kGoogleImageSearchSourceFull[] = "{google:imageSearchSource}";
     94 const char kGoogleImageThumbnailParameter[] = "google:imageThumbnail";
     95 const char kGoogleImageThumbnailParameterFull[] = "{google:imageThumbnail}";
     96 const char kGoogleImageURLParameter[] = "google:imageURL";
     97 const char kGoogleImageURLParameterFull[] = "{google:imageURL}";
     98 
     99 // Display value for kSearchTermsParameter.
    100 const char kDisplaySearchTerms[] = "%s";
    101 
    102 // Display value for kGoogleUnescapedSearchTermsParameter.
    103 const char kDisplayUnescapedSearchTerms[] = "%S";
    104 
    105 // Used if the count parameter is not optional. Indicates we want 10 search
    106 // results.
    107 const char kDefaultCount[] = "10";
    108 
    109 // Used if the parameter kOutputEncodingParameter is required.
    110 const char kOutputEncodingType[] = "UTF-8";
    111 
    112 // Attempts to encode |terms| and |original_query| in |encoding| and escape
    113 // them.  |terms| may be escaped as path or query depending on |is_in_query|;
    114 // |original_query| is always escaped as query.  Returns whether the encoding
    115 // process succeeded.
    116 bool TryEncoding(const string16& terms,
    117                  const string16& original_query,
    118                  const char* encoding,
    119                  bool is_in_query,
    120                  string16* escaped_terms,
    121                  string16* escaped_original_query) {
    122   DCHECK(escaped_terms);
    123   DCHECK(escaped_original_query);
    124   std::string encoded_terms;
    125   if (!base::UTF16ToCodepage(terms, encoding,
    126       base::OnStringConversionError::SKIP, &encoded_terms))
    127     return false;
    128   *escaped_terms = UTF8ToUTF16(is_in_query ?
    129       net::EscapeQueryParamValue(encoded_terms, true) :
    130       net::EscapePath(encoded_terms));
    131   if (original_query.empty())
    132     return true;
    133   std::string encoded_original_query;
    134   if (!base::UTF16ToCodepage(original_query, encoding,
    135       base::OnStringConversionError::SKIP, &encoded_original_query))
    136     return false;
    137   *escaped_original_query =
    138       UTF8ToUTF16(net::EscapeQueryParamValue(encoded_original_query, true));
    139   return true;
    140 }
    141 
    142 // Extract query key and host given a list of parameters coming from the URL
    143 // query or ref.
    144 std::string FindSearchTermsKey(const std::string& params) {
    145   if (params.empty())
    146     return std::string();
    147   url_parse::Component query, key, value;
    148   query.len = static_cast<int>(params.size());
    149   while (url_parse::ExtractQueryKeyValue(params.c_str(), &query, &key,
    150                                          &value)) {
    151     if (key.is_nonempty() && value.is_nonempty()) {
    152       std::string value_string = params.substr(value.begin, value.len);
    153       if (value_string.find(kSearchTermsParameterFull, 0) !=
    154           std::string::npos ||
    155           value_string.find(kGoogleUnescapedSearchTermsParameterFull, 0) !=
    156           std::string::npos) {
    157         return params.substr(key.begin, key.len);
    158       }
    159     }
    160   }
    161   return std::string();
    162 }
    163 
    164 // Returns the string to use for replacements of type
    165 // GOOGLE_IMAGE_SEARCH_SOURCE.
    166 std::string GetGoogleImageSearchSource() {
    167   chrome::VersionInfo version_info;
    168   if (version_info.is_valid()) {
    169     std::string version(version_info.Name() + " " + version_info.Version());
    170     if (version_info.IsOfficialBuild())
    171       version += " (Official)";
    172     version += " " + version_info.OSType();
    173     std::string modifier(version_info.GetVersionStringModifier());
    174     if (!modifier.empty())
    175       version += " " + modifier;
    176     return version;
    177   }
    178   return "unknown";
    179 }
    180 
    181 bool IsTemplateParameterString(const std::string& param) {
    182   return (param.length() > 2) && (*(param.begin()) == kStartParameter) &&
    183       (*(param.rbegin()) == kEndParameter);
    184 }
    185 
    186 }  // namespace
    187 
    188 
    189 // TemplateURLRef::SearchTermsArgs --------------------------------------------
    190 
    191 TemplateURLRef::SearchTermsArgs::SearchTermsArgs(const string16& search_terms)
    192     : search_terms(search_terms),
    193       accepted_suggestion(NO_SUGGESTIONS_AVAILABLE),
    194       cursor_position(string16::npos),
    195       omnibox_start_margin(-1),
    196       page_classification(AutocompleteInput::INVALID_SPEC),
    197       append_extra_query_params(false) {
    198 }
    199 
    200 TemplateURLRef::SearchTermsArgs::~SearchTermsArgs() {
    201 }
    202 
    203 
    204 // TemplateURLRef -------------------------------------------------------------
    205 
    206 TemplateURLRef::TemplateURLRef(TemplateURL* owner, Type type)
    207     : owner_(owner),
    208       type_(type),
    209       index_in_owner_(-1),
    210       parsed_(false),
    211       valid_(false),
    212       supports_replacements_(false),
    213       search_term_key_location_(url_parse::Parsed::QUERY),
    214       prepopulated_(false) {
    215   DCHECK(owner_);
    216   DCHECK_NE(INDEXED, type_);
    217 }
    218 
    219 TemplateURLRef::TemplateURLRef(TemplateURL* owner, size_t index_in_owner)
    220     : owner_(owner),
    221       type_(INDEXED),
    222       index_in_owner_(index_in_owner),
    223       parsed_(false),
    224       valid_(false),
    225       supports_replacements_(false),
    226       search_term_key_location_(url_parse::Parsed::QUERY),
    227       prepopulated_(false) {
    228   DCHECK(owner_);
    229   DCHECK_LT(index_in_owner_, owner_->URLCount());
    230 }
    231 
    232 TemplateURLRef::~TemplateURLRef() {
    233 }
    234 
    235 std::string TemplateURLRef::GetURL() const {
    236   switch (type_) {
    237     case SEARCH:  return owner_->url();
    238     case SUGGEST: return owner_->suggestions_url();
    239     case INSTANT: return owner_->instant_url();
    240     case IMAGE:   return owner_->image_url();
    241     case INDEXED: return owner_->GetURL(index_in_owner_);
    242     default:      NOTREACHED(); return std::string();  // NOLINT
    243   }
    244 }
    245 
    246 std::string TemplateURLRef::GetPostParamsString() const {
    247   switch (type_) {
    248     case INDEXED:
    249     case SEARCH:  return owner_->search_url_post_params();
    250     case SUGGEST: return owner_->suggestions_url_post_params();
    251     case INSTANT: return owner_->instant_url_post_params();
    252     case IMAGE:   return owner_->image_url_post_params();
    253     default:      NOTREACHED(); return std::string();  // NOLINT
    254   }
    255 }
    256 
    257 bool TemplateURLRef::UsesPOSTMethodUsingTermsData(
    258     const SearchTermsData* search_terms_data) const {
    259   if (search_terms_data)
    260     ParseIfNecessaryUsingTermsData(*search_terms_data);
    261   else
    262     ParseIfNecessary();
    263   return !post_params_.empty();
    264 }
    265 
    266 bool TemplateURLRef::EncodeFormData(const PostParams& post_params,
    267                                     PostContent* post_content) const {
    268   if (post_params.empty())
    269     return true;
    270   if (!post_content)
    271     return false;
    272 
    273   const char kUploadDataMIMEType[] = "multipart/form-data; boundary=";
    274   const char kMultipartBoundary[] = "----+*+----%016" PRIx64 "----+*+----";
    275   // Each name/value pair is stored in a body part which is preceded by a
    276   // boundary delimiter line. Uses random number generator here to create
    277   // a unique boundary delimiter for form data encoding.
    278   std::string boundary = base::StringPrintf(kMultipartBoundary,
    279                                             base::RandUint64());
    280   // Sets the content MIME type.
    281   post_content->first = kUploadDataMIMEType;
    282   post_content->first += boundary;
    283   // Encodes the post parameters.
    284   std::string* post_data = &post_content->second;
    285   post_data->clear();
    286   for (PostParams::const_iterator param = post_params.begin();
    287        param != post_params.end(); ++param) {
    288     DCHECK(!param->first.empty());
    289     net::AddMultipartValueForUpload(param->first, param->second, boundary,
    290                                     std::string(), post_data);
    291   }
    292   net::AddMultipartFinalDelimiterForUpload(boundary, post_data);
    293   return true;
    294 }
    295 
    296 bool TemplateURLRef::SupportsReplacement() const {
    297   UIThreadSearchTermsData search_terms_data(owner_->profile());
    298   return SupportsReplacementUsingTermsData(search_terms_data);
    299 }
    300 
    301 bool TemplateURLRef::SupportsReplacementUsingTermsData(
    302     const SearchTermsData& search_terms_data) const {
    303   ParseIfNecessaryUsingTermsData(search_terms_data);
    304   return valid_ && supports_replacements_;
    305 }
    306 
    307 std::string TemplateURLRef::ReplaceSearchTerms(
    308     const SearchTermsArgs& search_terms_args,
    309     PostContent* post_content) const {
    310   UIThreadSearchTermsData search_terms_data(owner_->profile());
    311   return ReplaceSearchTermsUsingTermsData(search_terms_args, search_terms_data,
    312                                           post_content);
    313 }
    314 
    315 std::string TemplateURLRef::ReplaceSearchTermsUsingTermsData(
    316     const SearchTermsArgs& search_terms_args,
    317     const SearchTermsData& search_terms_data,
    318     PostContent* post_content) const {
    319   ParseIfNecessaryUsingTermsData(search_terms_data);
    320   if (!valid_)
    321     return std::string();
    322 
    323   std::string url(HandleReplacements(search_terms_args, search_terms_data,
    324                                      post_content));
    325 
    326   // If the user specified additional query params on the command line, add
    327   // them.
    328   if (search_terms_args.append_extra_query_params) {
    329     std::string query_params(CommandLine::ForCurrentProcess()->
    330         GetSwitchValueASCII(switches::kExtraSearchQueryParams));
    331     GURL gurl(url);
    332     if (!query_params.empty() && gurl.is_valid()) {
    333       GURL::Replacements replacements;
    334       const std::string existing_query_params(gurl.query());
    335       if (!existing_query_params.empty())
    336         query_params += "&" + existing_query_params;
    337       replacements.SetQueryStr(query_params);
    338       return gurl.ReplaceComponents(replacements).possibly_invalid_spec();
    339     }
    340   }
    341 
    342   return url;
    343 }
    344 
    345 bool TemplateURLRef::IsValid() const {
    346   UIThreadSearchTermsData search_terms_data(owner_->profile());
    347   return IsValidUsingTermsData(search_terms_data);
    348 }
    349 
    350 bool TemplateURLRef::IsValidUsingTermsData(
    351     const SearchTermsData& search_terms_data) const {
    352   ParseIfNecessaryUsingTermsData(search_terms_data);
    353   return valid_;
    354 }
    355 
    356 string16 TemplateURLRef::DisplayURL() const {
    357   ParseIfNecessary();
    358   string16 result(UTF8ToUTF16(GetURL()));
    359   if (valid_ && !replacements_.empty()) {
    360     ReplaceSubstringsAfterOffset(&result, 0,
    361                                  ASCIIToUTF16(kSearchTermsParameterFull),
    362                                  ASCIIToUTF16(kDisplaySearchTerms));
    363     ReplaceSubstringsAfterOffset(&result, 0,
    364         ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull),
    365         ASCIIToUTF16(kDisplayUnescapedSearchTerms));
    366   }
    367   return result;
    368 }
    369 
    370 // static
    371 std::string TemplateURLRef::DisplayURLToURLRef(
    372     const string16& display_url) {
    373   string16 result = display_url;
    374   ReplaceSubstringsAfterOffset(&result, 0, ASCIIToUTF16(kDisplaySearchTerms),
    375                                ASCIIToUTF16(kSearchTermsParameterFull));
    376   ReplaceSubstringsAfterOffset(
    377       &result, 0,
    378       ASCIIToUTF16(kDisplayUnescapedSearchTerms),
    379       ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull));
    380   return UTF16ToUTF8(result);
    381 }
    382 
    383 const std::string& TemplateURLRef::GetHost() const {
    384   ParseIfNecessary();
    385   return host_;
    386 }
    387 
    388 const std::string& TemplateURLRef::GetPath() const {
    389   ParseIfNecessary();
    390   return path_;
    391 }
    392 
    393 const std::string& TemplateURLRef::GetSearchTermKey() const {
    394   ParseIfNecessary();
    395   return search_term_key_;
    396 }
    397 
    398 string16 TemplateURLRef::SearchTermToString16(const std::string& term) const {
    399   const std::vector<std::string>& encodings = owner_->input_encodings();
    400   string16 result;
    401 
    402   std::string unescaped = net::UnescapeURLComponent(
    403       term,
    404       net::UnescapeRule::REPLACE_PLUS_WITH_SPACE |
    405       net::UnescapeRule::URL_SPECIAL_CHARS);
    406   for (size_t i = 0; i < encodings.size(); ++i) {
    407     if (base::CodepageToUTF16(unescaped, encodings[i].c_str(),
    408                               base::OnStringConversionError::FAIL, &result))
    409       return result;
    410   }
    411 
    412   // Always fall back on UTF-8 if it works.
    413   if (base::CodepageToUTF16(unescaped, base::kCodepageUTF8,
    414                             base::OnStringConversionError::FAIL, &result))
    415     return result;
    416 
    417   // When nothing worked, just use the escaped text. We have no idea what the
    418   // encoding is. We need to substitute spaces for pluses ourselves since we're
    419   // not sending it through an unescaper.
    420   result = UTF8ToUTF16(term);
    421   std::replace(result.begin(), result.end(), '+', ' ');
    422   return result;
    423 }
    424 
    425 bool TemplateURLRef::HasGoogleBaseURLs() const {
    426   ParseIfNecessary();
    427   for (size_t i = 0; i < replacements_.size(); ++i) {
    428     if ((replacements_[i].type == GOOGLE_BASE_URL) ||
    429         (replacements_[i].type == GOOGLE_BASE_SUGGEST_URL))
    430       return true;
    431   }
    432   return false;
    433 }
    434 
    435 bool TemplateURLRef::ExtractSearchTermsFromURL(
    436     const GURL& url,
    437     string16* search_terms,
    438     const SearchTermsData& search_terms_data,
    439     url_parse::Parsed::ComponentType* search_terms_component,
    440     url_parse::Component* search_terms_position) const {
    441   DCHECK(search_terms);
    442   search_terms->clear();
    443 
    444   ParseIfNecessaryUsingTermsData(search_terms_data);
    445 
    446   // We need a search term in the template URL to extract something.
    447   if (search_term_key_.empty())
    448     return false;
    449 
    450   // TODO(beaudoin): Support patterns of the form http://foo/{searchTerms}/
    451   // See crbug.com/153798
    452 
    453   // Fill-in the replacements. We don't care about search terms in the pattern,
    454   // so we use the empty string.
    455   // Currently we assume the search term only shows in URL, not in post params.
    456   GURL pattern(ReplaceSearchTermsUsingTermsData(
    457       SearchTermsArgs(string16()), search_terms_data, NULL));
    458   // Host, path and port must match.
    459   if (url.port() != pattern.port() ||
    460       url.host() != host_ ||
    461       url.path() != path_) {
    462     return false;
    463   }
    464 
    465   // Parameter must be present either in the query or the ref.
    466   const std::string& params(
    467       (search_term_key_location_ == url_parse::Parsed::QUERY) ?
    468           url.query() : url.ref());
    469 
    470   url_parse::Component query, key, value;
    471   query.len = static_cast<int>(params.size());
    472   bool key_found = false;
    473   while (url_parse::ExtractQueryKeyValue(params.c_str(), &query, &key,
    474                                          &value)) {
    475     if (key.is_nonempty()) {
    476       if (params.substr(key.begin, key.len) == search_term_key_) {
    477         // Fail if search term key is found twice.
    478         if (key_found) {
    479           search_terms->clear();
    480           return false;
    481         }
    482         key_found = true;
    483         // Extract the search term.
    484         *search_terms = net::UnescapeAndDecodeUTF8URLComponent(
    485             params.substr(value.begin, value.len),
    486             net::UnescapeRule::SPACES |
    487                 net::UnescapeRule::URL_SPECIAL_CHARS |
    488                 net::UnescapeRule::REPLACE_PLUS_WITH_SPACE,
    489             NULL);
    490         if (search_terms_component)
    491           *search_terms_component = search_term_key_location_;
    492         if (search_terms_position)
    493           *search_terms_position = value;
    494       }
    495     }
    496   }
    497   return key_found;
    498 }
    499 
    500 void TemplateURLRef::InvalidateCachedValues() const {
    501   supports_replacements_ = valid_ = parsed_ = false;
    502   host_.clear();
    503   path_.clear();
    504   search_term_key_.clear();
    505   replacements_.clear();
    506   post_params_.clear();
    507 }
    508 
    509 bool TemplateURLRef::ParseParameter(size_t start,
    510                                     size_t end,
    511                                     std::string* url,
    512                                     Replacements* replacements) const {
    513   DCHECK(start != std::string::npos &&
    514          end != std::string::npos && end > start);
    515   size_t length = end - start - 1;
    516   bool optional = false;
    517   if ((*url)[end - 1] == kOptional) {
    518     optional = true;
    519     length--;
    520   }
    521   std::string parameter(url->substr(start + 1, length));
    522   std::string full_parameter(url->substr(start, end - start + 1));
    523   // Remove the parameter from the string.  For parameters who replacement is
    524   // constant and already known, just replace them directly.  For other cases,
    525   // like parameters whose values may change over time, use |replacements|.
    526   url->erase(start, end - start + 1);
    527   if (parameter == kSearchTermsParameter) {
    528     replacements->push_back(Replacement(SEARCH_TERMS, start));
    529   } else if (parameter == kCountParameter) {
    530     if (!optional)
    531       url->insert(start, kDefaultCount);
    532   } else if ((parameter == kStartIndexParameter) ||
    533              (parameter == kStartPageParameter)) {
    534     // We don't support these.
    535     if (!optional)
    536       url->insert(start, "1");
    537   } else if (parameter == kLanguageParameter) {
    538     replacements->push_back(Replacement(LANGUAGE, start));
    539   } else if (parameter == kInputEncodingParameter) {
    540     replacements->push_back(Replacement(ENCODING, start));
    541   } else if (parameter == kOutputEncodingParameter) {
    542     if (!optional)
    543       url->insert(start, kOutputEncodingType);
    544   } else if (parameter == kGoogleAssistedQueryStatsParameter) {
    545     replacements->push_back(Replacement(GOOGLE_ASSISTED_QUERY_STATS, start));
    546   } else if (parameter == kGoogleBaseURLParameter) {
    547     replacements->push_back(Replacement(GOOGLE_BASE_URL, start));
    548   } else if (parameter == kGoogleBaseSuggestURLParameter) {
    549     replacements->push_back(Replacement(GOOGLE_BASE_SUGGEST_URL, start));
    550   } else if (parameter == kGoogleCursorPositionParameter) {
    551     replacements->push_back(Replacement(GOOGLE_CURSOR_POSITION, start));
    552   } else if (parameter == kGoogleInstantEnabledParameter) {
    553     replacements->push_back(Replacement(GOOGLE_INSTANT_ENABLED, start));
    554   } else if (parameter == kGoogleInstantExtendedEnabledParameter) {
    555     replacements->push_back(Replacement(GOOGLE_INSTANT_EXTENDED_ENABLED,
    556                                         start));
    557   } else if (parameter == kGoogleInstantExtendedEnabledKey) {
    558     url->insert(start, google_util::kInstantExtendedAPIParam);
    559   } else if (parameter == kGoogleNTPIsThemedParameter) {
    560     replacements->push_back(Replacement(GOOGLE_NTP_IS_THEMED, start));
    561   } else if (parameter == kGoogleOmniboxStartMarginParameter) {
    562     replacements->push_back(Replacement(GOOGLE_OMNIBOX_START_MARGIN, start));
    563   } else if (parameter == kGoogleOriginalQueryForSuggestionParameter) {
    564     replacements->push_back(Replacement(GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION,
    565                                         start));
    566   } else if (parameter == kGooglePageClassificationParameter) {
    567     replacements->push_back(Replacement(GOOGLE_PAGE_CLASSIFICATION, start));
    568   } else if (parameter == kGoogleRLZParameter) {
    569     replacements->push_back(Replacement(GOOGLE_RLZ, start));
    570   } else if (parameter == kGoogleSearchClient) {
    571     replacements->push_back(Replacement(GOOGLE_SEARCH_CLIENT, start));
    572   } else if (parameter == kGoogleSearchFieldtrialParameter) {
    573     replacements->push_back(Replacement(GOOGLE_SEARCH_FIELDTRIAL_GROUP, start));
    574   } else if (parameter == kGoogleSuggestClient) {
    575     replacements->push_back(Replacement(GOOGLE_SUGGEST_CLIENT, start));
    576   } else if (parameter == kGoogleZeroPrefixUrlParameter) {
    577     replacements->push_back(Replacement(GOOGLE_ZERO_PREFIX_URL, start));
    578   } else if (parameter == kGoogleSuggestAPIKeyParameter) {
    579     url->insert(start,
    580                 net::EscapeQueryParamValue(google_apis::GetAPIKey(), false));
    581   } else if (parameter == kGoogleSourceIdParameter) {
    582 #if defined(OS_ANDROID)
    583     url->insert(start, "sourceid=chrome-mobile&");
    584 #else
    585     url->insert(start, "sourceid=chrome&");
    586 #endif
    587   } else if (parameter == kGoogleUnescapedSearchTermsParameter) {
    588     replacements->push_back(Replacement(GOOGLE_UNESCAPED_SEARCH_TERMS, start));
    589   } else if (parameter == kGoogleImageSearchSource) {
    590     url->insert(start, GetGoogleImageSearchSource());
    591   } else if (parameter == kGoogleImageThumbnailParameter) {
    592     replacements->push_back(
    593         Replacement(TemplateURLRef::GOOGLE_IMAGE_THUMBNAIL, start));
    594   } else if (parameter == kGoogleImageURLParameter) {
    595     replacements->push_back(Replacement(TemplateURLRef::GOOGLE_IMAGE_URL,
    596                                         start));
    597   } else if (!prepopulated_) {
    598     // If it's a prepopulated URL, we know that it's safe to remove unknown
    599     // parameters, so just ignore this and return true below. Otherwise it could
    600     // be some garbage but can also be a javascript block. Put it back.
    601     url->insert(start, full_parameter);
    602     return false;
    603   }
    604   return true;
    605 }
    606 
    607 std::string TemplateURLRef::ParseURL(const std::string& url,
    608                                      Replacements* replacements,
    609                                      PostParams* post_params,
    610                                      bool* valid) const {
    611   *valid = false;
    612   std::string parsed_url = url;
    613   for (size_t last = 0; last != std::string::npos; ) {
    614     last = parsed_url.find(kStartParameter, last);
    615     if (last != std::string::npos) {
    616       size_t template_end = parsed_url.find(kEndParameter, last);
    617       if (template_end != std::string::npos) {
    618         // Since we allow Javascript in the URL, {} pairs could be nested. Match
    619         // only leaf pairs with supported parameters.
    620         size_t next_template_start = parsed_url.find(kStartParameter, last + 1);
    621         if (next_template_start == std::string::npos ||
    622             next_template_start > template_end) {
    623           // If successful, ParseParameter erases from the string as such no
    624           // need to update |last|. If failed, move |last| to the end of pair.
    625           if (!ParseParameter(last, template_end, &parsed_url, replacements)) {
    626             // |template_end| + 1 may be beyond the end of the string.
    627             last = template_end;
    628           }
    629         } else {
    630           last = next_template_start;
    631         }
    632       } else {
    633         // Open brace without a closing brace, return.
    634         return std::string();
    635       }
    636     }
    637   }
    638 
    639   // Handles the post parameters.
    640   const std::string& post_params_string = GetPostParamsString();
    641   if (!post_params_string.empty()) {
    642     typedef std::vector<std::string> Strings;
    643     Strings param_list;
    644     base::SplitString(post_params_string, ',', &param_list);
    645 
    646     for (Strings::const_iterator iterator = param_list.begin();
    647          iterator != param_list.end(); ++iterator) {
    648       Strings parts;
    649       // The '=' delimiter is required and the name must be not empty.
    650       base::SplitString(*iterator, '=', &parts);
    651       if ((parts.size() != 2U) || parts[0].empty())
    652         return std::string();
    653 
    654       std::string& value = parts[1];
    655       size_t replacements_size = replacements->size();
    656       if (IsTemplateParameterString(value))
    657         ParseParameter(0, value.length() - 1, &value, replacements);
    658       post_params->push_back(std::make_pair(parts[0], value));
    659       // If there was a replacement added, points its index to last added
    660       // PostParam.
    661       if (replacements->size() > replacements_size) {
    662         DCHECK_EQ(replacements_size + 1, replacements->size());
    663         Replacement* r = &replacements->back();
    664         r->is_post_param = true;
    665         r->index = post_params->size() - 1;
    666       }
    667     }
    668     DCHECK(!post_params->empty());
    669   }
    670 
    671   *valid = true;
    672   return parsed_url;
    673 }
    674 
    675 void TemplateURLRef::ParseIfNecessary() const {
    676   UIThreadSearchTermsData search_terms_data(owner_->profile());
    677   ParseIfNecessaryUsingTermsData(search_terms_data);
    678 }
    679 
    680 void TemplateURLRef::ParseIfNecessaryUsingTermsData(
    681     const SearchTermsData& search_terms_data) const {
    682   if (!parsed_) {
    683     InvalidateCachedValues();
    684     parsed_ = true;
    685     parsed_url_ = ParseURL(GetURL(), &replacements_, &post_params_, &valid_);
    686     supports_replacements_ = false;
    687     if (valid_) {
    688       bool has_only_one_search_term = false;
    689       for (Replacements::const_iterator i = replacements_.begin();
    690            i != replacements_.end(); ++i) {
    691         if ((i->type == SEARCH_TERMS) ||
    692             (i->type == GOOGLE_UNESCAPED_SEARCH_TERMS)) {
    693           if (has_only_one_search_term) {
    694             has_only_one_search_term = false;
    695             break;
    696           }
    697           has_only_one_search_term = true;
    698           supports_replacements_ = true;
    699         }
    700       }
    701       // Only parse the host/key if there is one search term. Technically there
    702       // could be more than one term, but it's uncommon; so we punt.
    703       if (has_only_one_search_term)
    704         ParseHostAndSearchTermKey(search_terms_data);
    705     }
    706   }
    707 }
    708 
    709 void TemplateURLRef::ParseHostAndSearchTermKey(
    710     const SearchTermsData& search_terms_data) const {
    711   std::string url_string(GetURL());
    712   ReplaceSubstringsAfterOffset(&url_string, 0,
    713                                kGoogleBaseURLParameterFull,
    714                                search_terms_data.GoogleBaseURLValue());
    715   ReplaceSubstringsAfterOffset(&url_string, 0,
    716                                kGoogleBaseSuggestURLParameterFull,
    717                                search_terms_data.GoogleBaseSuggestURLValue());
    718 
    719   search_term_key_.clear();
    720   host_.clear();
    721   path_.clear();
    722   search_term_key_location_ = url_parse::Parsed::REF;
    723 
    724   GURL url(url_string);
    725   if (!url.is_valid())
    726     return;
    727 
    728   std::string query_key = FindSearchTermsKey(url.query());
    729   std::string ref_key = FindSearchTermsKey(url.ref());
    730   if (query_key.empty() == ref_key.empty())
    731     return;  // No key or multiple keys found.  We only handle having one key.
    732   search_term_key_ = query_key.empty() ? ref_key : query_key;
    733   search_term_key_location_ = query_key.empty() ?
    734       url_parse::Parsed::REF : url_parse::Parsed::QUERY;
    735   host_ = url.host();
    736   path_ = url.path();
    737 }
    738 
    739 void TemplateURLRef::HandleReplacement(const std::string& name,
    740                                        const std::string& value,
    741                                        const Replacement& replacement,
    742                                        std::string* url) const {
    743   size_t pos = replacement.index;
    744   if (replacement.is_post_param) {
    745     DCHECK_LT(pos, post_params_.size());
    746     DCHECK(!post_params_[pos].first.empty());
    747     post_params_[pos].second = value;
    748   } else {
    749     url->insert(pos, name.empty() ? value : (name + "=" + value + "&"));
    750   }
    751 }
    752 
    753 std::string TemplateURLRef::HandleReplacements(
    754     const SearchTermsArgs& search_terms_args,
    755     const SearchTermsData& search_terms_data,
    756     PostContent* post_content) const {
    757   if (replacements_.empty()) {
    758     if (!post_params_.empty())
    759       EncodeFormData(post_params_, post_content);
    760     return parsed_url_;
    761   }
    762 
    763   // Determine if the search terms are in the query or before. We're escaping
    764   // space as '+' in the former case and as '%20' in the latter case.
    765   bool is_in_query = true;
    766   for (Replacements::iterator i = replacements_.begin();
    767        i != replacements_.end(); ++i) {
    768     if (i->type == SEARCH_TERMS) {
    769       string16::size_type query_start = parsed_url_.find('?');
    770       is_in_query = query_start != string16::npos &&
    771           (static_cast<string16::size_type>(i->index) > query_start);
    772       break;
    773     }
    774   }
    775 
    776   std::string input_encoding;
    777   string16 encoded_terms;
    778   string16 encoded_original_query;
    779   owner_->EncodeSearchTerms(search_terms_args, is_in_query, &input_encoding,
    780                             &encoded_terms, &encoded_original_query);
    781 
    782   std::string url = parsed_url_;
    783 
    784   // replacements_ is ordered in ascending order, as such we need to iterate
    785   // from the back.
    786   for (Replacements::reverse_iterator i = replacements_.rbegin();
    787        i != replacements_.rend(); ++i) {
    788     switch (i->type) {
    789       case ENCODING:
    790         HandleReplacement(std::string(), input_encoding, *i, &url);
    791         break;
    792 
    793       case GOOGLE_ASSISTED_QUERY_STATS:
    794         DCHECK(!i->is_post_param);
    795         if (!search_terms_args.assisted_query_stats.empty()) {
    796           // Get the base URL without substituting AQS to avoid infinite
    797           // recursion.  We need the URL to find out if it meets all
    798           // AQS requirements (e.g. HTTPS protocol check).
    799           // See TemplateURLRef::SearchTermsArgs for more details.
    800           SearchTermsArgs search_terms_args_without_aqs(search_terms_args);
    801           search_terms_args_without_aqs.assisted_query_stats.clear();
    802           GURL base_url(ReplaceSearchTermsUsingTermsData(
    803               search_terms_args_without_aqs, search_terms_data, NULL));
    804           if (base_url.SchemeIs(chrome::kHttpsScheme)) {
    805             HandleReplacement(
    806                 "aqs", search_terms_args.assisted_query_stats, *i, &url);
    807           }
    808         }
    809         break;
    810 
    811       case GOOGLE_BASE_URL:
    812         DCHECK(!i->is_post_param);
    813         HandleReplacement(
    814             std::string(), search_terms_data.GoogleBaseURLValue(), *i, &url);
    815         break;
    816 
    817       case GOOGLE_BASE_SUGGEST_URL:
    818         DCHECK(!i->is_post_param);
    819         HandleReplacement(
    820             std::string(), search_terms_data.GoogleBaseSuggestURLValue(), *i,
    821             &url);
    822         break;
    823 
    824       case GOOGLE_CURSOR_POSITION:
    825         DCHECK(!i->is_post_param);
    826         if (search_terms_args.cursor_position != string16::npos)
    827           HandleReplacement(
    828               "cp",
    829               base::StringPrintf("%" PRIuS, search_terms_args.cursor_position),
    830               *i,
    831               &url);
    832         break;
    833 
    834       case GOOGLE_INSTANT_ENABLED:
    835         DCHECK(!i->is_post_param);
    836         HandleReplacement(
    837             std::string(), search_terms_data.InstantEnabledParam(), *i, &url);
    838         break;
    839 
    840       case GOOGLE_INSTANT_EXTENDED_ENABLED:
    841         DCHECK(!i->is_post_param);
    842         HandleReplacement(
    843             std::string(), search_terms_data.InstantExtendedEnabledParam(), *i,
    844             &url);
    845         break;
    846 
    847       case GOOGLE_NTP_IS_THEMED:
    848         DCHECK(!i->is_post_param);
    849         HandleReplacement(
    850             std::string(), search_terms_data.NTPIsThemedParam(), *i, &url);
    851         break;
    852 
    853       case GOOGLE_OMNIBOX_START_MARGIN:
    854         DCHECK(!i->is_post_param);
    855         if (search_terms_args.omnibox_start_margin >= 0) {
    856           HandleReplacement(
    857               "es_sm",
    858               base::IntToString(search_terms_args.omnibox_start_margin),
    859               *i,
    860               &url);
    861         }
    862         break;
    863 
    864       case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION:
    865         DCHECK(!i->is_post_param);
    866         if (search_terms_args.accepted_suggestion >= 0 ||
    867             !search_terms_args.assisted_query_stats.empty()) {
    868           HandleReplacement(
    869               "oq", UTF16ToUTF8(encoded_original_query), *i, &url);
    870         }
    871         break;
    872 
    873       case GOOGLE_PAGE_CLASSIFICATION:
    874         if (search_terms_args.page_classification !=
    875             AutocompleteInput::INVALID_SPEC) {
    876           HandleReplacement(
    877               "pgcl", base::IntToString(search_terms_args.page_classification),
    878               *i, &url);
    879         }
    880         break;
    881 
    882       case GOOGLE_RLZ: {
    883         DCHECK(!i->is_post_param);
    884         // On platforms that don't have RLZ, we still want this branch
    885         // to happen so that we replace the RLZ template with the
    886         // empty string.  (If we don't handle this case, we hit a
    887         // NOTREACHED below.)
    888         string16 rlz_string = search_terms_data.GetRlzParameterValue();
    889         if (!rlz_string.empty()) {
    890           HandleReplacement("rlz", UTF16ToUTF8(rlz_string), *i, &url);
    891         }
    892         break;
    893       }
    894 
    895       case GOOGLE_SEARCH_CLIENT: {
    896         DCHECK(!i->is_post_param);
    897         std::string client = search_terms_data.GetSearchClient();
    898         if (!client.empty())
    899           HandleReplacement("client", client, *i, &url);
    900         break;
    901       }
    902 
    903       case GOOGLE_SEARCH_FIELDTRIAL_GROUP:
    904         // We are not currently running any fieldtrials that modulate the search
    905         // url.  If we do, then we'd have some conditional insert such as:
    906         // url.insert(i->index, used_www ? "gcx=w&" : "gcx=c&");
    907         break;
    908 
    909       case GOOGLE_SUGGEST_CLIENT:
    910         HandleReplacement(
    911             std::string(), search_terms_data.GetSuggestClient(), *i, &url);
    912         break;
    913 
    914       case GOOGLE_UNESCAPED_SEARCH_TERMS: {
    915         std::string unescaped_terms;
    916         base::UTF16ToCodepage(search_terms_args.search_terms,
    917                               input_encoding.c_str(),
    918                               base::OnStringConversionError::SKIP,
    919                               &unescaped_terms);
    920         HandleReplacement(std::string(), unescaped_terms, *i, &url);
    921         break;
    922       }
    923 
    924       case GOOGLE_ZERO_PREFIX_URL:
    925         DCHECK(!i->is_post_param);
    926         if (!search_terms_args.zero_prefix_url.empty()) {
    927           const std::string& escaped_zero_prefix_url =
    928               net::EscapeQueryParamValue(search_terms_args.zero_prefix_url,
    929                                          true);
    930           HandleReplacement("url", escaped_zero_prefix_url, *i, &url);
    931         }
    932 
    933         break;
    934 
    935       case LANGUAGE:
    936         HandleReplacement(
    937             std::string(), search_terms_data.GetApplicationLocale(), *i, &url);
    938         break;
    939 
    940       case SEARCH_TERMS:
    941         HandleReplacement(std::string(), UTF16ToUTF8(encoded_terms), *i, &url);
    942         break;
    943 
    944       case GOOGLE_IMAGE_THUMBNAIL:
    945         HandleReplacement(
    946             std::string(), search_terms_args.image_thumbnail_content, *i, &url);
    947         break;
    948 
    949       case GOOGLE_IMAGE_URL:
    950         if (search_terms_args.image_url.is_valid()) {
    951           HandleReplacement(
    952               std::string(), search_terms_args.image_url.spec(), *i, &url);
    953         }
    954         break;
    955 
    956       default:
    957         NOTREACHED();
    958         break;
    959     }
    960   }
    961 
    962   if (!post_params_.empty())
    963     EncodeFormData(post_params_, post_content);
    964 
    965   return url;
    966 }
    967 
    968 
    969 // TemplateURLData ------------------------------------------------------------
    970 
    971 TemplateURLData::TemplateURLData()
    972     : show_in_default_list(false),
    973       safe_for_autoreplace(false),
    974       id(0),
    975       date_created(base::Time::Now()),
    976       last_modified(base::Time::Now()),
    977       created_by_policy(false),
    978       usage_count(0),
    979       prepopulate_id(0),
    980       sync_guid(base::GenerateGUID()),
    981       keyword_(ASCIIToUTF16("dummy")),
    982       url_("x") {
    983 }
    984 
    985 TemplateURLData::~TemplateURLData() {
    986 }
    987 
    988 void TemplateURLData::SetKeyword(const string16& keyword) {
    989   DCHECK(!keyword.empty());
    990 
    991   // Case sensitive keyword matching is confusing. As such, we force all
    992   // keywords to be lower case.
    993   keyword_ = base::i18n::ToLower(keyword);
    994 }
    995 
    996 void TemplateURLData::SetURL(const std::string& url) {
    997   DCHECK(!url.empty());
    998   url_ = url;
    999 }
   1000 
   1001 
   1002 // TemplateURL ----------------------------------------------------------------
   1003 
   1004 TemplateURL::TemplateURL(Profile* profile, const TemplateURLData& data)
   1005     : profile_(profile),
   1006       data_(data),
   1007       url_ref_(this, TemplateURLRef::SEARCH),
   1008       suggestions_url_ref_(this,
   1009                            TemplateURLRef::SUGGEST),
   1010       instant_url_ref_(this,
   1011                        TemplateURLRef::INSTANT),
   1012       image_url_ref_(this, TemplateURLRef::IMAGE) {
   1013   SetPrepopulateId(data_.prepopulate_id);
   1014 
   1015   if (data_.search_terms_replacement_key ==
   1016       kGoogleInstantExtendedEnabledKeyFull) {
   1017     data_.search_terms_replacement_key = google_util::kInstantExtendedAPIParam;
   1018   }
   1019 }
   1020 
   1021 TemplateURL::~TemplateURL() {
   1022 }
   1023 
   1024 // static
   1025 GURL TemplateURL::GenerateFaviconURL(const GURL& url) {
   1026   DCHECK(url.is_valid());
   1027   GURL::Replacements rep;
   1028 
   1029   const char favicon_path[] = "/favicon.ico";
   1030   int favicon_path_len = arraysize(favicon_path) - 1;
   1031 
   1032   rep.SetPath(favicon_path, url_parse::Component(0, favicon_path_len));
   1033   rep.ClearUsername();
   1034   rep.ClearPassword();
   1035   rep.ClearQuery();
   1036   rep.ClearRef();
   1037   return url.ReplaceComponents(rep);
   1038 }
   1039 
   1040 string16 TemplateURL::AdjustedShortNameForLocaleDirection() const {
   1041   string16 bidi_safe_short_name = data_.short_name;
   1042   base::i18n::AdjustStringForLocaleDirection(&bidi_safe_short_name);
   1043   return bidi_safe_short_name;
   1044 }
   1045 
   1046 bool TemplateURL::ShowInDefaultList() const {
   1047   return data_.show_in_default_list && url_ref_.SupportsReplacement();
   1048 }
   1049 
   1050 bool TemplateURL::SupportsReplacement() const {
   1051   UIThreadSearchTermsData search_terms_data(profile_);
   1052   return SupportsReplacementUsingTermsData(search_terms_data);
   1053 }
   1054 
   1055 bool TemplateURL::SupportsReplacementUsingTermsData(
   1056     const SearchTermsData& search_terms_data) const {
   1057   return url_ref_.SupportsReplacementUsingTermsData(search_terms_data);
   1058 }
   1059 
   1060 bool TemplateURL::IsGoogleSearchURLWithReplaceableKeyword() const {
   1061   return !IsExtensionKeyword() && url_ref_.HasGoogleBaseURLs() &&
   1062       google_util::IsGoogleHostname(UTF16ToUTF8(data_.keyword()),
   1063                                     google_util::DISALLOW_SUBDOMAIN);
   1064 }
   1065 
   1066 bool TemplateURL::HasSameKeywordAs(const TemplateURL& other) const {
   1067   return (data_.keyword() == other.data_.keyword()) ||
   1068       (IsGoogleSearchURLWithReplaceableKeyword() &&
   1069        other.IsGoogleSearchURLWithReplaceableKeyword());
   1070 }
   1071 
   1072 std::string TemplateURL::GetExtensionId() const {
   1073   DCHECK(IsExtensionKeyword());
   1074   return GURL(data_.url()).host();
   1075 }
   1076 
   1077 bool TemplateURL::IsExtensionKeyword() const {
   1078   return GURL(data_.url()).SchemeIs(extensions::kExtensionScheme);
   1079 }
   1080 
   1081 size_t TemplateURL::URLCount() const {
   1082   // Add 1 for the regular search URL.
   1083   return data_.alternate_urls.size() + 1;
   1084 }
   1085 
   1086 const std::string& TemplateURL::GetURL(size_t index) const {
   1087   DCHECK_LT(index, URLCount());
   1088 
   1089   return (index < data_.alternate_urls.size()) ?
   1090       data_.alternate_urls[index] : url();
   1091 }
   1092 
   1093 bool TemplateURL::ExtractSearchTermsFromURL(
   1094     const GURL& url,
   1095     string16* search_terms) {
   1096   UIThreadSearchTermsData search_terms_data(profile_);
   1097   return ExtractSearchTermsFromURLUsingTermsData(url, search_terms,
   1098                                                  search_terms_data);
   1099 }
   1100 
   1101 bool TemplateURL::ExtractSearchTermsFromURLUsingTermsData(
   1102     const GURL& url,
   1103     string16* search_terms,
   1104     const SearchTermsData& search_terms_data) {
   1105   return FindSearchTermsInURL(url, search_terms_data, search_terms, NULL, NULL);
   1106 }
   1107 
   1108 
   1109 bool TemplateURL::IsSearchURL(const GURL& url) {
   1110   UIThreadSearchTermsData search_terms_data(profile_);
   1111   return IsSearchURLUsingTermsData(url, search_terms_data);
   1112 }
   1113 
   1114 bool TemplateURL::IsSearchURLUsingTermsData(
   1115     const GURL& url,
   1116     const SearchTermsData& search_terms_data) {
   1117   string16 search_terms;
   1118   return ExtractSearchTermsFromURLUsingTermsData(
   1119       url, &search_terms, search_terms_data) && !search_terms.empty();
   1120 }
   1121 
   1122 bool TemplateURL::HasSearchTermsReplacementKey(const GURL& url) const {
   1123   // Look for the key both in the query and the ref.
   1124   std::string params[] = {url.query(), url.ref()};
   1125 
   1126   for (int i = 0; i < 2; ++i) {
   1127     url_parse::Component query, key, value;
   1128     query.len = static_cast<int>(params[i].size());
   1129     while (url_parse::ExtractQueryKeyValue(params[i].c_str(), &query, &key,
   1130                                            &value)) {
   1131       if (key.is_nonempty() &&
   1132           params[i].substr(key.begin, key.len) ==
   1133               search_terms_replacement_key()) {
   1134         return true;
   1135       }
   1136     }
   1137   }
   1138   return false;
   1139 }
   1140 
   1141 bool TemplateURL::ReplaceSearchTermsInURL(
   1142     const GURL& url,
   1143     const TemplateURLRef::SearchTermsArgs& search_terms_args,
   1144     GURL* result) {
   1145   UIThreadSearchTermsData search_terms_data(profile_);
   1146   // TODO(beaudoin): Use AQS from |search_terms_args| too.
   1147   url_parse::Parsed::ComponentType search_term_component;
   1148   url_parse::Component search_terms_position;
   1149   string16 search_terms;
   1150   if (!FindSearchTermsInURL(url, search_terms_data, &search_terms,
   1151                             &search_term_component, &search_terms_position)) {
   1152     return false;
   1153   }
   1154   DCHECK(search_terms_position.is_nonempty());
   1155 
   1156   // FindSearchTermsInURL only returns true for search terms in the query or
   1157   // ref, so we can call EncodeSearchTerm with |is_in_query| = true, since query
   1158   // and ref are encoded in the same way.
   1159   std::string input_encoding;
   1160   string16 encoded_terms;
   1161   string16 encoded_original_query;
   1162   EncodeSearchTerms(search_terms_args, true, &input_encoding,
   1163                     &encoded_terms, &encoded_original_query);
   1164 
   1165   std::string old_params((search_term_component == url_parse::Parsed::REF) ?
   1166       url.ref() : url.query());
   1167   std::string new_params(old_params, 0, search_terms_position.begin);
   1168   new_params += UTF16ToUTF8(search_terms_args.search_terms);
   1169   new_params += old_params.substr(search_terms_position.end());
   1170   url_canon::StdStringReplacements<std::string> replacements;
   1171   if (search_term_component == url_parse::Parsed::REF)
   1172     replacements.SetRefStr(new_params);
   1173   else
   1174     replacements.SetQueryStr(new_params);
   1175   *result = url.ReplaceComponents(replacements);
   1176   return true;
   1177 }
   1178 
   1179 void TemplateURL::EncodeSearchTerms(
   1180     const TemplateURLRef::SearchTermsArgs& search_terms_args,
   1181     bool is_in_query,
   1182     std::string* input_encoding,
   1183     string16* encoded_terms,
   1184     string16* encoded_original_query) const {
   1185 
   1186   std::vector<std::string> encodings(input_encodings());
   1187   if (std::find(encodings.begin(), encodings.end(), "UTF-8") == encodings.end())
   1188     encodings.push_back("UTF-8");
   1189   for (std::vector<std::string>::const_iterator i(encodings.begin());
   1190        i != encodings.end(); ++i) {
   1191     if (TryEncoding(search_terms_args.search_terms,
   1192                     search_terms_args.original_query, i->c_str(),
   1193                     is_in_query, encoded_terms, encoded_original_query)) {
   1194       *input_encoding = *i;
   1195       return;
   1196     }
   1197   }
   1198   NOTREACHED();
   1199 }
   1200 
   1201 void TemplateURL::CopyFrom(const TemplateURL& other) {
   1202   if (this == &other)
   1203     return;
   1204 
   1205   profile_ = other.profile_;
   1206   data_ = other.data_;
   1207   url_ref_.InvalidateCachedValues();
   1208   suggestions_url_ref_.InvalidateCachedValues();
   1209   instant_url_ref_.InvalidateCachedValues();
   1210   SetPrepopulateId(other.data_.prepopulate_id);
   1211 }
   1212 
   1213 void TemplateURL::SetURL(const std::string& url) {
   1214   data_.SetURL(url);
   1215   url_ref_.InvalidateCachedValues();
   1216 }
   1217 
   1218 void TemplateURL::SetPrepopulateId(int id) {
   1219   data_.prepopulate_id = id;
   1220   const bool prepopulated = id > 0;
   1221   url_ref_.prepopulated_ = prepopulated;
   1222   suggestions_url_ref_.prepopulated_ = prepopulated;
   1223   instant_url_ref_.prepopulated_ = prepopulated;
   1224 }
   1225 
   1226 void TemplateURL::ResetKeywordIfNecessary(bool force) {
   1227   if (IsGoogleSearchURLWithReplaceableKeyword() || force) {
   1228     DCHECK(!IsExtensionKeyword());
   1229     GURL url(TemplateURLService::GenerateSearchURL(this));
   1230     if (url.is_valid())
   1231       data_.SetKeyword(TemplateURLService::GenerateKeyword(url));
   1232   }
   1233 }
   1234 
   1235 bool TemplateURL::FindSearchTermsInURL(
   1236     const GURL& url,
   1237     const SearchTermsData& search_terms_data,
   1238     string16* search_terms,
   1239     url_parse::Parsed::ComponentType* search_term_component,
   1240     url_parse::Component* search_terms_position) {
   1241   DCHECK(search_terms);
   1242   search_terms->clear();
   1243 
   1244   // Try to match with every pattern.
   1245   for (size_t i = 0; i < URLCount(); ++i) {
   1246     TemplateURLRef ref(this, i);
   1247     if (ref.ExtractSearchTermsFromURL(url, search_terms, search_terms_data,
   1248         search_term_component, search_terms_position)) {
   1249       // If ExtractSearchTermsFromURL() returns true and |search_terms| is empty
   1250       // it means the pattern matched but no search terms were present. In this
   1251       // case we fail immediately without looking for matches in subsequent
   1252       // patterns. This means that given patterns
   1253       //    [ "http://foo/#q={searchTerms}", "http://foo/?q={searchTerms}" ],
   1254       // calling ExtractSearchTermsFromURL() on "http://foo/?q=bar#q=' would
   1255       // return false. This is important for at least Google, where such URLs
   1256       // are invalid.
   1257       return !search_terms->empty();
   1258     }
   1259   }
   1260   return false;
   1261 }
   1262