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, ',', ¶m_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