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.
5 #include "chrome/browser/search_engines/template_url.h"
10 #include "base/basictypes.h"
11 #include "base/command_line.h"
12 #include "base/format_macros.h"
13 #include "base/guid.h"
14 #include "base/i18n/case_conversion.h"
15 #include "base/i18n/icu_string_conversions.h"
16 #include "base/i18n/rtl.h"
17 #include "base/logging.h"
18 #include "base/metrics/field_trial.h"
19 #include "base/rand_util.h"
20 #include "base/strings/string_number_conversions.h"
21 #include "base/strings/string_split.h"
22 #include "base/strings/string_util.h"
23 #include "base/strings/stringprintf.h"
24 #include "base/strings/utf_string_conversions.h"
25 #include "chrome/browser/google/google_util.h"
26 #include "chrome/browser/search/search.h"
27 #include "chrome/browser/search_engines/search_terms_data.h"
28 #include "chrome/browser/search_engines/template_url_service.h"
29 #include "chrome/common/chrome_switches.h"
30 #include "chrome/common/chrome_version_info.h"
31 #include "chrome/common/url_constants.h"
32 #include "extensions/common/constants.h"
33 #include "google_apis/google_api_keys.h"
34 #include "net/base/escape.h"
35 #include "net/base/mime_util.h"
36 #include "ui/base/l10n/l10n_util.h"
40 // The TemplateURLRef has any number of terms that need to be replaced. Each of
41 // the terms is enclosed in braces. If the character preceeding the final
42 // brace is a ?, it indicates the term is optional and can be replaced with
44 const char kStartParameter
= '{';
45 const char kEndParameter
= '}';
46 const char kOptional
= '?';
48 // Known parameters found in the URL.
49 const char kSearchTermsParameter
[] = "searchTerms";
50 const char kSearchTermsParameterFull
[] = "{searchTerms}";
51 const char kCountParameter
[] = "count";
52 const char kStartIndexParameter
[] = "startIndex";
53 const char kStartPageParameter
[] = "startPage";
54 const char kLanguageParameter
[] = "language";
55 const char kInputEncodingParameter
[] = "inputEncoding";
56 const char kOutputEncodingParameter
[] = "outputEncoding";
58 const char kGoogleAssistedQueryStatsParameter
[] = "google:assistedQueryStats";
60 // Host/Domain Google searches are relative to.
61 const char kGoogleBaseURLParameter
[] = "google:baseURL";
62 const char kGoogleBaseURLParameterFull
[] = "{google:baseURL}";
64 // Like google:baseURL, but for the Search Suggest capability.
65 const char kGoogleBaseSuggestURLParameter
[] = "google:baseSuggestURL";
66 const char kGoogleBaseSuggestURLParameterFull
[] = "{google:baseSuggestURL}";
67 const char kGoogleBookmarkBarPinnedParameter
[] = "google:bookmarkBarPinned";
68 const char kGoogleCurrentPageUrlParameter
[] = "google:currentPageUrl";
69 const char kGoogleCursorPositionParameter
[] = "google:cursorPosition";
70 const char kGoogleForceInstantResultsParameter
[] = "google:forceInstantResults";
71 const char kGoogleInstantExtendedEnabledParameter
[] =
72 "google:instantExtendedEnabledParameter";
73 const char kGoogleInstantExtendedEnabledKey
[] =
74 "google:instantExtendedEnabledKey";
75 const char kGoogleInstantExtendedEnabledKeyFull
[] =
76 "{google:instantExtendedEnabledKey}";
77 const char kGoogleNTPIsThemedParameter
[] = "google:ntpIsThemedParameter";
78 const char kGoogleOmniboxStartMarginParameter
[] =
79 "google:omniboxStartMarginParameter";
80 const char kGoogleOriginalQueryForSuggestionParameter
[] =
81 "google:originalQueryForSuggestion";
82 const char kGooglePageClassificationParameter
[] = "google:pageClassification";
83 const char kGoogleRLZParameter
[] = "google:RLZ";
84 const char kGoogleSearchClient
[] = "google:searchClient";
85 const char kGoogleSearchFieldtrialParameter
[] =
86 "google:searchFieldtrialParameter";
87 const char kGoogleSourceIdParameter
[] = "google:sourceId";
88 const char kGoogleSuggestAPIKeyParameter
[] = "google:suggestAPIKeyParameter";
89 const char kGoogleSuggestClient
[] = "google:suggestClient";
90 const char kGoogleSuggestRequestId
[] = "google:suggestRid";
92 // Same as kSearchTermsParameter, with no escaping.
93 const char kGoogleUnescapedSearchTermsParameter
[] =
94 "google:unescapedSearchTerms";
95 const char kGoogleUnescapedSearchTermsParameterFull
[] =
96 "{google:unescapedSearchTerms}";
98 const char kGoogleImageSearchSource
[] = "google:imageSearchSource";
99 const char kGoogleImageThumbnailParameter
[] = "google:imageThumbnail";
100 const char kGoogleImageURLParameter
[] = "google:imageURL";
101 const char kGoogleImageOriginalWidth
[] = "google:imageOriginalWidth";
102 const char kGoogleImageOriginalHeight
[] = "google:imageOriginalHeight";
104 // Display value for kSearchTermsParameter.
105 const char kDisplaySearchTerms
[] = "%s";
107 // Display value for kGoogleUnescapedSearchTermsParameter.
108 const char kDisplayUnescapedSearchTerms
[] = "%S";
110 // Used if the count parameter is not optional. Indicates we want 10 search
112 const char kDefaultCount
[] = "10";
114 // Used if the parameter kOutputEncodingParameter is required.
115 const char kOutputEncodingType
[] = "UTF-8";
117 // Attempts to encode |terms| and |original_query| in |encoding| and escape
118 // them. |terms| may be escaped as path or query depending on |is_in_query|;
119 // |original_query| is always escaped as query. Returns whether the encoding
120 // process succeeded.
121 bool TryEncoding(const base::string16
& terms
,
122 const base::string16
& original_query
,
123 const char* encoding
,
125 base::string16
* escaped_terms
,
126 base::string16
* escaped_original_query
) {
127 DCHECK(escaped_terms
);
128 DCHECK(escaped_original_query
);
129 std::string encoded_terms
;
130 if (!base::UTF16ToCodepage(terms
, encoding
,
131 base::OnStringConversionError::SKIP
, &encoded_terms
))
133 *escaped_terms
= base::UTF8ToUTF16(is_in_query
?
134 net::EscapeQueryParamValue(encoded_terms
, true) :
135 net::EscapePath(encoded_terms
));
136 if (original_query
.empty())
138 std::string encoded_original_query
;
139 if (!base::UTF16ToCodepage(original_query
, encoding
,
140 base::OnStringConversionError::SKIP
, &encoded_original_query
))
142 *escaped_original_query
= base::UTF8ToUTF16(
143 net::EscapeQueryParamValue(encoded_original_query
, true));
147 // Extract query key and host given a list of parameters coming from the URL
149 std::string
FindSearchTermsKey(const std::string
& params
) {
151 return std::string();
152 url_parse::Component query
, key
, value
;
153 query
.len
= static_cast<int>(params
.size());
154 while (url_parse::ExtractQueryKeyValue(params
.c_str(), &query
, &key
,
156 if (key
.is_nonempty() && value
.is_nonempty()) {
157 std::string value_string
= params
.substr(value
.begin
, value
.len
);
158 if (value_string
.find(kSearchTermsParameterFull
, 0) !=
160 value_string
.find(kGoogleUnescapedSearchTermsParameterFull
, 0) !=
162 return params
.substr(key
.begin
, key
.len
);
166 return std::string();
169 // Returns the string to use for replacements of type
170 // GOOGLE_IMAGE_SEARCH_SOURCE.
171 std::string
GetGoogleImageSearchSource() {
172 chrome::VersionInfo version_info
;
173 if (version_info
.is_valid()) {
174 std::string
version(version_info
.Name() + " " + version_info
.Version());
175 if (version_info
.IsOfficialBuild())
176 version
+= " (Official)";
177 version
+= " " + version_info
.OSType();
178 std::string
modifier(version_info
.GetVersionStringModifier());
179 if (!modifier
.empty())
180 version
+= " " + modifier
;
186 bool IsTemplateParameterString(const std::string
& param
) {
187 return (param
.length() > 2) && (*(param
.begin()) == kStartParameter
) &&
188 (*(param
.rbegin()) == kEndParameter
);
191 bool ShowingSearchTermsOnSRP() {
192 return chrome::IsInstantExtendedAPIEnabled() &&
193 chrome::IsQueryExtractionEnabled();
199 // TemplateURLRef::SearchTermsArgs --------------------------------------------
201 TemplateURLRef::SearchTermsArgs::SearchTermsArgs(
202 const base::string16
& search_terms
)
203 : search_terms(search_terms
),
204 accepted_suggestion(NO_SUGGESTIONS_AVAILABLE
),
205 cursor_position(base::string16::npos
),
206 omnibox_start_margin(-1),
207 page_classification(AutocompleteInput::INVALID_SPEC
),
208 bookmark_bar_pinned(false),
209 append_extra_query_params(false),
210 force_instant_results(false) {
213 TemplateURLRef::SearchTermsArgs::~SearchTermsArgs() {
217 // TemplateURLRef -------------------------------------------------------------
219 TemplateURLRef::TemplateURLRef(TemplateURL
* owner
, Type type
)
225 supports_replacements_(false),
226 search_term_key_location_(url_parse::Parsed::QUERY
),
227 prepopulated_(false),
228 showing_search_terms_(ShowingSearchTermsOnSRP()) {
230 DCHECK_NE(INDEXED
, type_
);
233 TemplateURLRef::TemplateURLRef(TemplateURL
* owner
, size_t index_in_owner
)
236 index_in_owner_(index_in_owner
),
239 supports_replacements_(false),
240 search_term_key_location_(url_parse::Parsed::QUERY
),
241 prepopulated_(false),
242 showing_search_terms_(ShowingSearchTermsOnSRP()) {
244 DCHECK_LT(index_in_owner_
, owner_
->URLCount());
247 TemplateURLRef::~TemplateURLRef() {
250 std::string
TemplateURLRef::GetURL() const {
252 case SEARCH
: return owner_
->url();
253 case SUGGEST
: return owner_
->suggestions_url();
254 case INSTANT
: return owner_
->instant_url();
255 case IMAGE
: return owner_
->image_url();
256 case NEW_TAB
: return owner_
->new_tab_url();
257 case INDEXED
: return owner_
->GetURL(index_in_owner_
);
258 default: NOTREACHED(); return std::string(); // NOLINT
262 std::string
TemplateURLRef::GetPostParamsString() const {
265 case SEARCH
: return owner_
->search_url_post_params();
266 case SUGGEST
: return owner_
->suggestions_url_post_params();
267 case INSTANT
: return owner_
->instant_url_post_params();
268 case NEW_TAB
: return std::string();
269 case IMAGE
: return owner_
->image_url_post_params();
270 default: NOTREACHED(); return std::string(); // NOLINT
274 bool TemplateURLRef::UsesPOSTMethodUsingTermsData(
275 const SearchTermsData
* search_terms_data
) const {
276 if (search_terms_data
)
277 ParseIfNecessaryUsingTermsData(*search_terms_data
);
280 return !post_params_
.empty();
283 bool TemplateURLRef::EncodeFormData(const PostParams
& post_params
,
284 PostContent
* post_content
) const {
285 if (post_params
.empty())
290 const char kUploadDataMIMEType
[] = "multipart/form-data; boundary=";
291 const char kMultipartBoundary
[] = "----+*+----%016" PRIx64
"----+*+----";
292 // Each name/value pair is stored in a body part which is preceded by a
293 // boundary delimiter line. Uses random number generator here to create
294 // a unique boundary delimiter for form data encoding.
295 std::string boundary
= base::StringPrintf(kMultipartBoundary
,
297 // Sets the content MIME type.
298 post_content
->first
= kUploadDataMIMEType
;
299 post_content
->first
+= boundary
;
300 // Encodes the post parameters.
301 std::string
* post_data
= &post_content
->second
;
303 for (PostParams::const_iterator param
= post_params
.begin();
304 param
!= post_params
.end(); ++param
) {
305 DCHECK(!param
->first
.empty());
306 net::AddMultipartValueForUpload(param
->first
, param
->second
, boundary
,
307 std::string(), post_data
);
309 net::AddMultipartFinalDelimiterForUpload(boundary
, post_data
);
313 bool TemplateURLRef::SupportsReplacement() const {
314 UIThreadSearchTermsData
search_terms_data(owner_
->profile());
315 return SupportsReplacementUsingTermsData(search_terms_data
);
318 bool TemplateURLRef::SupportsReplacementUsingTermsData(
319 const SearchTermsData
& search_terms_data
) const {
320 ParseIfNecessaryUsingTermsData(search_terms_data
);
321 return valid_
&& supports_replacements_
;
324 std::string
TemplateURLRef::ReplaceSearchTerms(
325 const SearchTermsArgs
& search_terms_args
,
326 PostContent
* post_content
) const {
327 UIThreadSearchTermsData
search_terms_data(owner_
->profile());
328 return ReplaceSearchTermsUsingTermsData(search_terms_args
, search_terms_data
,
332 std::string
TemplateURLRef::ReplaceSearchTermsUsingTermsData(
333 const SearchTermsArgs
& search_terms_args
,
334 const SearchTermsData
& search_terms_data
,
335 PostContent
* post_content
) const {
336 ParseIfNecessaryUsingTermsData(search_terms_data
);
338 return std::string();
340 std::string
url(HandleReplacements(search_terms_args
, search_terms_data
,
344 if (!gurl
.is_valid())
347 std::vector
<std::string
> query_params
;
348 if (search_terms_args
.append_extra_query_params
) {
349 std::string
extra_params(
350 CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
351 switches::kExtraSearchQueryParams
));
352 if (!extra_params
.empty())
353 query_params
.push_back(extra_params
);
355 if (!search_terms_args
.suggest_query_params
.empty())
356 query_params
.push_back(search_terms_args
.suggest_query_params
);
357 if (!gurl
.query().empty())
358 query_params
.push_back(gurl
.query());
360 if (query_params
.empty())
363 GURL::Replacements replacements
;
364 std::string query_str
= JoinString(query_params
, "&");
365 replacements
.SetQueryStr(query_str
);
366 return gurl
.ReplaceComponents(replacements
).possibly_invalid_spec();
369 bool TemplateURLRef::IsValid() const {
370 UIThreadSearchTermsData
search_terms_data(owner_
->profile());
371 return IsValidUsingTermsData(search_terms_data
);
374 bool TemplateURLRef::IsValidUsingTermsData(
375 const SearchTermsData
& search_terms_data
) const {
376 ParseIfNecessaryUsingTermsData(search_terms_data
);
380 base::string16
TemplateURLRef::DisplayURL() const {
382 base::string16
result(base::UTF8ToUTF16(GetURL()));
383 if (valid_
&& !replacements_
.empty()) {
384 ReplaceSubstringsAfterOffset(&result
, 0,
385 base::ASCIIToUTF16(kSearchTermsParameterFull
),
386 base::ASCIIToUTF16(kDisplaySearchTerms
));
387 ReplaceSubstringsAfterOffset(&result
, 0,
388 base::ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull
),
389 base::ASCIIToUTF16(kDisplayUnescapedSearchTerms
));
395 std::string
TemplateURLRef::DisplayURLToURLRef(
396 const base::string16
& display_url
) {
397 base::string16 result
= display_url
;
398 ReplaceSubstringsAfterOffset(&result
, 0,
399 base::ASCIIToUTF16(kDisplaySearchTerms
),
400 base::ASCIIToUTF16(kSearchTermsParameterFull
));
401 ReplaceSubstringsAfterOffset(
403 base::ASCIIToUTF16(kDisplayUnescapedSearchTerms
),
404 base::ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull
));
405 return base::UTF16ToUTF8(result
);
408 const std::string
& TemplateURLRef::GetHost() const {
413 const std::string
& TemplateURLRef::GetPath() const {
418 const std::string
& TemplateURLRef::GetSearchTermKey() const {
420 return search_term_key_
;
423 base::string16
TemplateURLRef::SearchTermToString16(
424 const std::string
& term
) const {
425 const std::vector
<std::string
>& encodings
= owner_
->input_encodings();
426 base::string16 result
;
428 std::string unescaped
= net::UnescapeURLComponent(
430 net::UnescapeRule::REPLACE_PLUS_WITH_SPACE
|
431 net::UnescapeRule::URL_SPECIAL_CHARS
);
432 for (size_t i
= 0; i
< encodings
.size(); ++i
) {
433 if (base::CodepageToUTF16(unescaped
, encodings
[i
].c_str(),
434 base::OnStringConversionError::FAIL
, &result
))
438 // Always fall back on UTF-8 if it works.
439 if (base::CodepageToUTF16(unescaped
, base::kCodepageUTF8
,
440 base::OnStringConversionError::FAIL
, &result
))
443 // When nothing worked, just use the escaped text. We have no idea what the
444 // encoding is. We need to substitute spaces for pluses ourselves since we're
445 // not sending it through an unescaper.
446 result
= base::UTF8ToUTF16(term
);
447 std::replace(result
.begin(), result
.end(), '+', ' ');
451 bool TemplateURLRef::HasGoogleBaseURLs() const {
453 for (size_t i
= 0; i
< replacements_
.size(); ++i
) {
454 if ((replacements_
[i
].type
== GOOGLE_BASE_URL
) ||
455 (replacements_
[i
].type
== GOOGLE_BASE_SUGGEST_URL
))
461 bool TemplateURLRef::ExtractSearchTermsFromURL(
463 base::string16
* search_terms
,
464 const SearchTermsData
& search_terms_data
,
465 url_parse::Parsed::ComponentType
* search_terms_component
,
466 url_parse::Component
* search_terms_position
) const {
467 DCHECK(search_terms
);
468 search_terms
->clear();
470 ParseIfNecessaryUsingTermsData(search_terms_data
);
472 // We need a search term in the template URL to extract something.
473 if (search_term_key_
.empty())
476 // TODO(beaudoin): Support patterns of the form http://foo/{searchTerms}/
477 // See crbug.com/153798
479 // Fill-in the replacements. We don't care about search terms in the pattern,
480 // so we use the empty string.
481 // Currently we assume the search term only shows in URL, not in post params.
482 GURL
pattern(ReplaceSearchTermsUsingTermsData(
483 SearchTermsArgs(base::string16()), search_terms_data
, NULL
));
484 // Host, path and port must match.
485 if (url
.port() != pattern
.port() ||
486 url
.host() != host_
||
487 url
.path() != path_
) {
491 // Parameter must be present either in the query or the ref.
492 const std::string
& params(
493 (search_term_key_location_
== url_parse::Parsed::QUERY
) ?
494 url
.query() : url
.ref());
496 url_parse::Component query
, key
, value
;
497 query
.len
= static_cast<int>(params
.size());
498 bool key_found
= false;
499 while (url_parse::ExtractQueryKeyValue(params
.c_str(), &query
, &key
,
501 if (key
.is_nonempty()) {
502 if (params
.substr(key
.begin
, key
.len
) == search_term_key_
) {
503 // Fail if search term key is found twice.
505 search_terms
->clear();
509 // Extract the search term.
510 *search_terms
= net::UnescapeAndDecodeUTF8URLComponent(
511 params
.substr(value
.begin
, value
.len
),
512 net::UnescapeRule::SPACES
|
513 net::UnescapeRule::URL_SPECIAL_CHARS
|
514 net::UnescapeRule::REPLACE_PLUS_WITH_SPACE
,
516 if (search_terms_component
)
517 *search_terms_component
= search_term_key_location_
;
518 if (search_terms_position
)
519 *search_terms_position
= value
;
526 void TemplateURLRef::InvalidateCachedValues() const {
527 supports_replacements_
= valid_
= parsed_
= false;
530 search_term_key_
.clear();
531 replacements_
.clear();
532 post_params_
.clear();
535 bool TemplateURLRef::ParseParameter(size_t start
,
538 Replacements
* replacements
) const {
539 DCHECK(start
!= std::string::npos
&&
540 end
!= std::string::npos
&& end
> start
);
541 size_t length
= end
- start
- 1;
542 bool optional
= false;
543 if ((*url
)[end
- 1] == kOptional
) {
547 std::string
parameter(url
->substr(start
+ 1, length
));
548 std::string
full_parameter(url
->substr(start
, end
- start
+ 1));
549 // Remove the parameter from the string. For parameters who replacement is
550 // constant and already known, just replace them directly. For other cases,
551 // like parameters whose values may change over time, use |replacements|.
552 url
->erase(start
, end
- start
+ 1);
553 if (parameter
== kSearchTermsParameter
) {
554 replacements
->push_back(Replacement(SEARCH_TERMS
, start
));
555 } else if (parameter
== kCountParameter
) {
557 url
->insert(start
, kDefaultCount
);
558 } else if (parameter
== kGoogleAssistedQueryStatsParameter
) {
559 replacements
->push_back(Replacement(GOOGLE_ASSISTED_QUERY_STATS
, start
));
560 } else if (parameter
== kGoogleBaseURLParameter
) {
561 replacements
->push_back(Replacement(GOOGLE_BASE_URL
, start
));
562 } else if (parameter
== kGoogleBaseSuggestURLParameter
) {
563 replacements
->push_back(Replacement(GOOGLE_BASE_SUGGEST_URL
, start
));
564 } else if (parameter
== kGoogleBookmarkBarPinnedParameter
) {
565 replacements
->push_back(Replacement(GOOGLE_BOOKMARK_BAR_PINNED
, start
));
566 } else if (parameter
== kGoogleCurrentPageUrlParameter
) {
567 replacements
->push_back(Replacement(GOOGLE_CURRENT_PAGE_URL
, start
));
568 } else if (parameter
== kGoogleCursorPositionParameter
) {
569 replacements
->push_back(Replacement(GOOGLE_CURSOR_POSITION
, start
));
570 } else if (parameter
== kGoogleImageOriginalHeight
) {
571 replacements
->push_back(
572 Replacement(TemplateURLRef::GOOGLE_IMAGE_ORIGINAL_HEIGHT
, start
));
573 } else if (parameter
== kGoogleImageOriginalWidth
) {
574 replacements
->push_back(
575 Replacement(TemplateURLRef::GOOGLE_IMAGE_ORIGINAL_WIDTH
, start
));
576 } else if (parameter
== kGoogleImageSearchSource
) {
577 url
->insert(start
, GetGoogleImageSearchSource());
578 } else if (parameter
== kGoogleImageThumbnailParameter
) {
579 replacements
->push_back(
580 Replacement(TemplateURLRef::GOOGLE_IMAGE_THUMBNAIL
, start
));
581 } else if (parameter
== kGoogleImageURLParameter
) {
582 replacements
->push_back(Replacement(TemplateURLRef::GOOGLE_IMAGE_URL
,
584 } else if (parameter
== kGoogleForceInstantResultsParameter
) {
585 replacements
->push_back(Replacement(GOOGLE_FORCE_INSTANT_RESULTS
, start
));
586 } else if (parameter
== kGoogleInstantExtendedEnabledParameter
) {
587 replacements
->push_back(Replacement(GOOGLE_INSTANT_EXTENDED_ENABLED
,
589 } else if (parameter
== kGoogleInstantExtendedEnabledKey
) {
590 url
->insert(start
, google_util::kInstantExtendedAPIParam
);
591 } else if (parameter
== kGoogleNTPIsThemedParameter
) {
592 replacements
->push_back(Replacement(GOOGLE_NTP_IS_THEMED
, start
));
593 } else if (parameter
== kGoogleOmniboxStartMarginParameter
) {
594 replacements
->push_back(Replacement(GOOGLE_OMNIBOX_START_MARGIN
, start
));
595 } else if (parameter
== kGoogleOriginalQueryForSuggestionParameter
) {
596 replacements
->push_back(Replacement(GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION
,
598 } else if (parameter
== kGooglePageClassificationParameter
) {
599 replacements
->push_back(Replacement(GOOGLE_PAGE_CLASSIFICATION
, start
));
600 } else if (parameter
== kGoogleRLZParameter
) {
601 replacements
->push_back(Replacement(GOOGLE_RLZ
, start
));
602 } else if (parameter
== kGoogleSearchClient
) {
603 replacements
->push_back(Replacement(GOOGLE_SEARCH_CLIENT
, start
));
604 } else if (parameter
== kGoogleSearchFieldtrialParameter
) {
605 replacements
->push_back(Replacement(GOOGLE_SEARCH_FIELDTRIAL_GROUP
, start
));
606 } else if (parameter
== kGoogleSourceIdParameter
) {
607 #if defined(OS_ANDROID)
608 url
->insert(start
, "sourceid=chrome-mobile&");
610 url
->insert(start
, "sourceid=chrome&");
612 } else if (parameter
== kGoogleSuggestAPIKeyParameter
) {
614 net::EscapeQueryParamValue(google_apis::GetAPIKey(), false));
615 } else if (parameter
== kGoogleSuggestClient
) {
616 replacements
->push_back(Replacement(GOOGLE_SUGGEST_CLIENT
, start
));
617 } else if (parameter
== kGoogleSuggestRequestId
) {
618 replacements
->push_back(Replacement(GOOGLE_SUGGEST_REQUEST_ID
, start
));
619 } else if (parameter
== kGoogleUnescapedSearchTermsParameter
) {
620 replacements
->push_back(Replacement(GOOGLE_UNESCAPED_SEARCH_TERMS
, start
));
621 } else if (parameter
== kInputEncodingParameter
) {
622 replacements
->push_back(Replacement(ENCODING
, start
));
623 } else if (parameter
== kLanguageParameter
) {
624 replacements
->push_back(Replacement(LANGUAGE
, start
));
625 } else if (parameter
== kOutputEncodingParameter
) {
627 url
->insert(start
, kOutputEncodingType
);
628 } else if ((parameter
== kStartIndexParameter
) ||
629 (parameter
== kStartPageParameter
)) {
630 // We don't support these.
632 url
->insert(start
, "1");
633 } else if (!prepopulated_
) {
634 // If it's a prepopulated URL, we know that it's safe to remove unknown
635 // parameters, so just ignore this and return true below. Otherwise it could
636 // be some garbage but can also be a javascript block. Put it back.
637 url
->insert(start
, full_parameter
);
643 std::string
TemplateURLRef::ParseURL(const std::string
& url
,
644 Replacements
* replacements
,
645 PostParams
* post_params
,
648 std::string parsed_url
= url
;
649 for (size_t last
= 0; last
!= std::string::npos
; ) {
650 last
= parsed_url
.find(kStartParameter
, last
);
651 if (last
!= std::string::npos
) {
652 size_t template_end
= parsed_url
.find(kEndParameter
, last
);
653 if (template_end
!= std::string::npos
) {
654 // Since we allow Javascript in the URL, {} pairs could be nested. Match
655 // only leaf pairs with supported parameters.
656 size_t next_template_start
= parsed_url
.find(kStartParameter
, last
+ 1);
657 if (next_template_start
== std::string::npos
||
658 next_template_start
> template_end
) {
659 // If successful, ParseParameter erases from the string as such no
660 // need to update |last|. If failed, move |last| to the end of pair.
661 if (!ParseParameter(last
, template_end
, &parsed_url
, replacements
)) {
662 // |template_end| + 1 may be beyond the end of the string.
666 last
= next_template_start
;
669 // Open brace without a closing brace, return.
670 return std::string();
675 // Handles the post parameters.
676 const std::string
& post_params_string
= GetPostParamsString();
677 if (!post_params_string
.empty()) {
678 typedef std::vector
<std::string
> Strings
;
680 base::SplitString(post_params_string
, ',', ¶m_list
);
682 for (Strings::const_iterator iterator
= param_list
.begin();
683 iterator
!= param_list
.end(); ++iterator
) {
685 // The '=' delimiter is required and the name must be not empty.
686 base::SplitString(*iterator
, '=', &parts
);
687 if ((parts
.size() != 2U) || parts
[0].empty())
688 return std::string();
690 std::string
& value
= parts
[1];
691 size_t replacements_size
= replacements
->size();
692 if (IsTemplateParameterString(value
))
693 ParseParameter(0, value
.length() - 1, &value
, replacements
);
694 post_params
->push_back(std::make_pair(parts
[0], value
));
695 // If there was a replacement added, points its index to last added
697 if (replacements
->size() > replacements_size
) {
698 DCHECK_EQ(replacements_size
+ 1, replacements
->size());
699 Replacement
* r
= &replacements
->back();
700 r
->is_post_param
= true;
701 r
->index
= post_params
->size() - 1;
704 DCHECK(!post_params
->empty());
711 void TemplateURLRef::ParseIfNecessary() const {
712 UIThreadSearchTermsData
search_terms_data(owner_
->profile());
713 ParseIfNecessaryUsingTermsData(search_terms_data
);
716 void TemplateURLRef::ParseIfNecessaryUsingTermsData(
717 const SearchTermsData
& search_terms_data
) const {
719 InvalidateCachedValues();
721 parsed_url_
= ParseURL(GetURL(), &replacements_
, &post_params_
, &valid_
);
722 supports_replacements_
= false;
724 bool has_only_one_search_term
= false;
725 for (Replacements::const_iterator i
= replacements_
.begin();
726 i
!= replacements_
.end(); ++i
) {
727 if ((i
->type
== SEARCH_TERMS
) ||
728 (i
->type
== GOOGLE_UNESCAPED_SEARCH_TERMS
)) {
729 if (has_only_one_search_term
) {
730 has_only_one_search_term
= false;
733 has_only_one_search_term
= true;
734 supports_replacements_
= true;
737 // Only parse the host/key if there is one search term. Technically there
738 // could be more than one term, but it's uncommon; so we punt.
739 if (has_only_one_search_term
)
740 ParseHostAndSearchTermKey(search_terms_data
);
745 void TemplateURLRef::ParseHostAndSearchTermKey(
746 const SearchTermsData
& search_terms_data
) const {
747 std::string
url_string(GetURL());
748 ReplaceSubstringsAfterOffset(&url_string
, 0,
749 kGoogleBaseURLParameterFull
,
750 search_terms_data
.GoogleBaseURLValue());
751 ReplaceSubstringsAfterOffset(&url_string
, 0,
752 kGoogleBaseSuggestURLParameterFull
,
753 search_terms_data
.GoogleBaseSuggestURLValue());
755 search_term_key_
.clear();
758 search_term_key_location_
= url_parse::Parsed::REF
;
760 GURL
url(url_string
);
764 std::string query_key
= FindSearchTermsKey(url
.query());
765 std::string ref_key
= FindSearchTermsKey(url
.ref());
766 if (query_key
.empty() == ref_key
.empty())
767 return; // No key or multiple keys found. We only handle having one key.
768 search_term_key_
= query_key
.empty() ? ref_key
: query_key
;
769 search_term_key_location_
= query_key
.empty() ?
770 url_parse::Parsed::REF
: url_parse::Parsed::QUERY
;
775 void TemplateURLRef::HandleReplacement(const std::string
& name
,
776 const std::string
& value
,
777 const Replacement
& replacement
,
778 std::string
* url
) const {
779 size_t pos
= replacement
.index
;
780 if (replacement
.is_post_param
) {
781 DCHECK_LT(pos
, post_params_
.size());
782 DCHECK(!post_params_
[pos
].first
.empty());
783 post_params_
[pos
].second
= value
;
785 url
->insert(pos
, name
.empty() ? value
: (name
+ "=" + value
+ "&"));
789 std::string
TemplateURLRef::HandleReplacements(
790 const SearchTermsArgs
& search_terms_args
,
791 const SearchTermsData
& search_terms_data
,
792 PostContent
* post_content
) const {
793 if (replacements_
.empty()) {
794 if (!post_params_
.empty())
795 EncodeFormData(post_params_
, post_content
);
799 // Determine if the search terms are in the query or before. We're escaping
800 // space as '+' in the former case and as '%20' in the latter case.
801 bool is_in_query
= true;
802 for (Replacements::iterator i
= replacements_
.begin();
803 i
!= replacements_
.end(); ++i
) {
804 if (i
->type
== SEARCH_TERMS
) {
805 base::string16::size_type query_start
= parsed_url_
.find('?');
806 is_in_query
= query_start
!= base::string16::npos
&&
807 (static_cast<base::string16::size_type
>(i
->index
) > query_start
);
812 std::string input_encoding
;
813 base::string16 encoded_terms
;
814 base::string16 encoded_original_query
;
815 owner_
->EncodeSearchTerms(search_terms_args
, is_in_query
, &input_encoding
,
816 &encoded_terms
, &encoded_original_query
);
818 std::string url
= parsed_url_
;
820 // replacements_ is ordered in ascending order, as such we need to iterate
822 for (Replacements::reverse_iterator i
= replacements_
.rbegin();
823 i
!= replacements_
.rend(); ++i
) {
826 HandleReplacement(std::string(), input_encoding
, *i
, &url
);
829 case GOOGLE_ASSISTED_QUERY_STATS
:
830 DCHECK(!i
->is_post_param
);
831 if (!search_terms_args
.assisted_query_stats
.empty()) {
832 // Get the base URL without substituting AQS to avoid infinite
833 // recursion. We need the URL to find out if it meets all
834 // AQS requirements (e.g. HTTPS protocol check).
835 // See TemplateURLRef::SearchTermsArgs for more details.
836 SearchTermsArgs
search_terms_args_without_aqs(search_terms_args
);
837 search_terms_args_without_aqs
.assisted_query_stats
.clear();
838 GURL
base_url(ReplaceSearchTermsUsingTermsData(
839 search_terms_args_without_aqs
, search_terms_data
, NULL
));
840 if (base_url
.SchemeIs(content::kHttpsScheme
)) {
842 "aqs", search_terms_args
.assisted_query_stats
, *i
, &url
);
847 case GOOGLE_BASE_URL
:
848 DCHECK(!i
->is_post_param
);
850 std::string(), search_terms_data
.GoogleBaseURLValue(), *i
, &url
);
853 case GOOGLE_BASE_SUGGEST_URL
:
854 DCHECK(!i
->is_post_param
);
856 std::string(), search_terms_data
.GoogleBaseSuggestURLValue(), *i
,
860 case GOOGLE_BOOKMARK_BAR_PINNED
:
861 if (showing_search_terms_
) {
862 // Log whether the bookmark bar is pinned when the user is seeing
863 // InstantExtended on the SRP.
864 DCHECK(!i
->is_post_param
);
866 "bmbp", search_terms_args
.bookmark_bar_pinned
? "1" : "0", *i
,
871 case GOOGLE_CURRENT_PAGE_URL
:
872 DCHECK(!i
->is_post_param
);
873 if (!search_terms_args
.current_page_url
.empty()) {
874 const std::string
& escaped_current_page_url
=
875 net::EscapeQueryParamValue(search_terms_args
.current_page_url
,
877 HandleReplacement("url", escaped_current_page_url
, *i
, &url
);
881 case GOOGLE_CURSOR_POSITION
:
882 DCHECK(!i
->is_post_param
);
883 if (search_terms_args
.cursor_position
!= base::string16::npos
)
886 base::StringPrintf("%" PRIuS
, search_terms_args
.cursor_position
),
891 case GOOGLE_FORCE_INSTANT_RESULTS
:
892 DCHECK(!i
->is_post_param
);
893 HandleReplacement(std::string(),
894 search_terms_data
.ForceInstantResultsParam(
895 search_terms_args
.force_instant_results
),
900 case GOOGLE_INSTANT_EXTENDED_ENABLED
:
901 DCHECK(!i
->is_post_param
);
903 std::string(), search_terms_data
.InstantExtendedEnabledParam(), *i
,
907 case GOOGLE_NTP_IS_THEMED
:
908 DCHECK(!i
->is_post_param
);
910 std::string(), search_terms_data
.NTPIsThemedParam(), *i
, &url
);
913 case GOOGLE_OMNIBOX_START_MARGIN
:
914 DCHECK(!i
->is_post_param
);
915 if (search_terms_args
.omnibox_start_margin
>= 0) {
918 base::IntToString(search_terms_args
.omnibox_start_margin
),
924 case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION
:
925 DCHECK(!i
->is_post_param
);
926 if (search_terms_args
.accepted_suggestion
>= 0 ||
927 !search_terms_args
.assisted_query_stats
.empty()) {
929 "oq", base::UTF16ToUTF8(encoded_original_query
), *i
, &url
);
933 case GOOGLE_PAGE_CLASSIFICATION
:
934 if (search_terms_args
.page_classification
!=
935 AutocompleteInput::INVALID_SPEC
) {
937 "pgcl", base::IntToString(search_terms_args
.page_classification
),
943 DCHECK(!i
->is_post_param
);
944 // On platforms that don't have RLZ, we still want this branch
945 // to happen so that we replace the RLZ template with the
946 // empty string. (If we don't handle this case, we hit a
947 // NOTREACHED below.)
948 base::string16 rlz_string
= search_terms_data
.GetRlzParameterValue();
949 if (!rlz_string
.empty()) {
950 HandleReplacement("rlz", base::UTF16ToUTF8(rlz_string
), *i
, &url
);
955 case GOOGLE_SEARCH_CLIENT
: {
956 DCHECK(!i
->is_post_param
);
957 std::string client
= search_terms_data
.GetSearchClient();
959 HandleReplacement("client", client
, *i
, &url
);
963 case GOOGLE_SEARCH_FIELDTRIAL_GROUP
:
964 // We are not currently running any fieldtrials that modulate the search
965 // url. If we do, then we'd have some conditional insert such as:
966 // url.insert(i->index, used_www ? "gcx=w&" : "gcx=c&");
969 case GOOGLE_SUGGEST_CLIENT
:
971 std::string(), search_terms_data
.GetSuggestClient(), *i
, &url
);
974 case GOOGLE_SUGGEST_REQUEST_ID
:
976 std::string(), search_terms_data
.GetSuggestRequestIdentifier(), *i
,
980 case GOOGLE_UNESCAPED_SEARCH_TERMS
: {
981 std::string unescaped_terms
;
982 base::UTF16ToCodepage(search_terms_args
.search_terms
,
983 input_encoding
.c_str(),
984 base::OnStringConversionError::SKIP
,
986 HandleReplacement(std::string(), unescaped_terms
, *i
, &url
);
992 std::string(), search_terms_data
.GetApplicationLocale(), *i
, &url
);
997 std::string(), base::UTF16ToUTF8(encoded_terms
), *i
, &url
);
1000 case GOOGLE_IMAGE_THUMBNAIL
:
1002 std::string(), search_terms_args
.image_thumbnail_content
, *i
, &url
);
1005 case GOOGLE_IMAGE_URL
:
1006 if (search_terms_args
.image_url
.is_valid()) {
1008 std::string(), search_terms_args
.image_url
.spec(), *i
, &url
);
1012 case GOOGLE_IMAGE_ORIGINAL_WIDTH
:
1013 if (!search_terms_args
.image_original_size
.IsEmpty()) {
1016 base::IntToString(search_terms_args
.image_original_size
.width()),
1021 case GOOGLE_IMAGE_ORIGINAL_HEIGHT
:
1022 if (!search_terms_args
.image_original_size
.IsEmpty()) {
1025 base::IntToString(search_terms_args
.image_original_size
.height()),
1036 if (!post_params_
.empty())
1037 EncodeFormData(post_params_
, post_content
);
1043 // TemplateURLData ------------------------------------------------------------
1045 TemplateURLData::TemplateURLData()
1046 : show_in_default_list(false),
1047 safe_for_autoreplace(false),
1049 date_created(base::Time::Now()),
1050 last_modified(base::Time::Now()),
1051 created_by_policy(false),
1054 sync_guid(base::GenerateGUID()),
1055 keyword_(base::ASCIIToUTF16("dummy")),
1059 TemplateURLData::~TemplateURLData() {
1062 void TemplateURLData::SetKeyword(const base::string16
& keyword
) {
1063 DCHECK(!keyword
.empty());
1065 // Case sensitive keyword matching is confusing. As such, we force all
1066 // keywords to be lower case.
1067 keyword_
= base::i18n::ToLower(keyword
);
1070 void TemplateURLData::SetURL(const std::string
& url
) {
1071 DCHECK(!url
.empty());
1076 // TemplateURL ----------------------------------------------------------------
1078 TemplateURL::TemplateURL(Profile
* profile
, const TemplateURLData
& data
)
1079 : profile_(profile
),
1081 url_ref_(this, TemplateURLRef::SEARCH
),
1082 suggestions_url_ref_(this,
1083 TemplateURLRef::SUGGEST
),
1084 instant_url_ref_(this,
1085 TemplateURLRef::INSTANT
),
1086 image_url_ref_(this, TemplateURLRef::IMAGE
),
1087 new_tab_url_ref_(this, TemplateURLRef::NEW_TAB
) {
1088 SetPrepopulateId(data_
.prepopulate_id
);
1090 if (data_
.search_terms_replacement_key
==
1091 kGoogleInstantExtendedEnabledKeyFull
) {
1092 data_
.search_terms_replacement_key
= google_util::kInstantExtendedAPIParam
;
1096 TemplateURL::~TemplateURL() {
1100 GURL
TemplateURL::GenerateFaviconURL(const GURL
& url
) {
1101 DCHECK(url
.is_valid());
1102 GURL::Replacements rep
;
1104 const char favicon_path
[] = "/favicon.ico";
1105 int favicon_path_len
= arraysize(favicon_path
) - 1;
1107 rep
.SetPath(favicon_path
, url_parse::Component(0, favicon_path_len
));
1108 rep
.ClearUsername();
1109 rep
.ClearPassword();
1112 return url
.ReplaceComponents(rep
);
1115 base::string16
TemplateURL::AdjustedShortNameForLocaleDirection() const {
1116 base::string16 bidi_safe_short_name
= data_
.short_name
;
1117 base::i18n::AdjustStringForLocaleDirection(&bidi_safe_short_name
);
1118 return bidi_safe_short_name
;
1121 bool TemplateURL::ShowInDefaultList() const {
1122 return data_
.show_in_default_list
&& url_ref_
.SupportsReplacement();
1125 bool TemplateURL::SupportsReplacement() const {
1126 UIThreadSearchTermsData
search_terms_data(profile_
);
1127 return SupportsReplacementUsingTermsData(search_terms_data
);
1130 bool TemplateURL::SupportsReplacementUsingTermsData(
1131 const SearchTermsData
& search_terms_data
) const {
1132 return url_ref_
.SupportsReplacementUsingTermsData(search_terms_data
);
1135 bool TemplateURL::IsGoogleSearchURLWithReplaceableKeyword() const {
1136 return (GetType() == NORMAL
) && url_ref_
.HasGoogleBaseURLs() &&
1137 google_util::IsGoogleHostname(base::UTF16ToUTF8(data_
.keyword()),
1138 google_util::DISALLOW_SUBDOMAIN
);
1141 bool TemplateURL::HasSameKeywordAs(const TemplateURL
& other
) const {
1142 return (data_
.keyword() == other
.data_
.keyword()) ||
1143 (IsGoogleSearchURLWithReplaceableKeyword() &&
1144 other
.IsGoogleSearchURLWithReplaceableKeyword());
1147 TemplateURL::Type
TemplateURL::GetType() const {
1148 if (extension_info_
)
1149 return NORMAL_CONTROLLED_BY_EXTENSION
;
1150 return GURL(data_
.url()).SchemeIs(extensions::kExtensionScheme
) ?
1151 OMNIBOX_API_EXTENSION
: NORMAL
;
1154 std::string
TemplateURL::GetExtensionId() const {
1155 DCHECK_NE(NORMAL
, GetType());
1156 return extension_info_
?
1157 extension_info_
->extension_id
: GURL(data_
.url()).host();
1160 size_t TemplateURL::URLCount() const {
1161 // Add 1 for the regular search URL.
1162 return data_
.alternate_urls
.size() + 1;
1165 const std::string
& TemplateURL::GetURL(size_t index
) const {
1166 DCHECK_LT(index
, URLCount());
1168 return (index
< data_
.alternate_urls
.size()) ?
1169 data_
.alternate_urls
[index
] : url();
1172 bool TemplateURL::ExtractSearchTermsFromURL(
1174 base::string16
* search_terms
) {
1175 UIThreadSearchTermsData
search_terms_data(profile_
);
1176 return ExtractSearchTermsFromURLUsingTermsData(url
, search_terms
,
1180 bool TemplateURL::ExtractSearchTermsFromURLUsingTermsData(
1182 base::string16
* search_terms
,
1183 const SearchTermsData
& search_terms_data
) {
1184 return FindSearchTermsInURL(url
, search_terms_data
, search_terms
, NULL
, NULL
);
1188 bool TemplateURL::IsSearchURL(const GURL
& url
) {
1189 UIThreadSearchTermsData
search_terms_data(profile_
);
1190 return IsSearchURLUsingTermsData(url
, search_terms_data
);
1193 bool TemplateURL::IsSearchURLUsingTermsData(
1195 const SearchTermsData
& search_terms_data
) {
1196 base::string16 search_terms
;
1197 return ExtractSearchTermsFromURLUsingTermsData(
1198 url
, &search_terms
, search_terms_data
) && !search_terms
.empty();
1201 bool TemplateURL::HasSearchTermsReplacementKey(const GURL
& url
) const {
1202 // Look for the key both in the query and the ref.
1203 std::string params
[] = {url
.query(), url
.ref()};
1205 for (int i
= 0; i
< 2; ++i
) {
1206 url_parse::Component query
, key
, value
;
1207 query
.len
= static_cast<int>(params
[i
].size());
1208 while (url_parse::ExtractQueryKeyValue(params
[i
].c_str(), &query
, &key
,
1210 if (key
.is_nonempty() &&
1211 params
[i
].substr(key
.begin
, key
.len
) ==
1212 search_terms_replacement_key()) {
1220 bool TemplateURL::ReplaceSearchTermsInURL(
1222 const TemplateURLRef::SearchTermsArgs
& search_terms_args
,
1224 UIThreadSearchTermsData
search_terms_data(profile_
);
1225 // TODO(beaudoin): Use AQS from |search_terms_args| too.
1226 url_parse::Parsed::ComponentType search_term_component
;
1227 url_parse::Component search_terms_position
;
1228 base::string16 search_terms
;
1229 if (!FindSearchTermsInURL(url
, search_terms_data
, &search_terms
,
1230 &search_term_component
, &search_terms_position
)) {
1233 DCHECK(search_terms_position
.is_nonempty());
1235 // FindSearchTermsInURL only returns true for search terms in the query or
1236 // ref, so we can call EncodeSearchTerm with |is_in_query| = true, since query
1237 // and ref are encoded in the same way.
1238 std::string input_encoding
;
1239 base::string16 encoded_terms
;
1240 base::string16 encoded_original_query
;
1241 EncodeSearchTerms(search_terms_args
, true, &input_encoding
,
1242 &encoded_terms
, &encoded_original_query
);
1244 std::string
old_params((search_term_component
== url_parse::Parsed::REF
) ?
1245 url
.ref() : url
.query());
1246 std::string
new_params(old_params
, 0, search_terms_position
.begin
);
1247 new_params
+= base::UTF16ToUTF8(search_terms_args
.search_terms
);
1248 new_params
+= old_params
.substr(search_terms_position
.end());
1249 url_canon::StdStringReplacements
<std::string
> replacements
;
1250 if (search_term_component
== url_parse::Parsed::REF
)
1251 replacements
.SetRefStr(new_params
);
1253 replacements
.SetQueryStr(new_params
);
1254 *result
= url
.ReplaceComponents(replacements
);
1258 void TemplateURL::EncodeSearchTerms(
1259 const TemplateURLRef::SearchTermsArgs
& search_terms_args
,
1261 std::string
* input_encoding
,
1262 base::string16
* encoded_terms
,
1263 base::string16
* encoded_original_query
) const {
1265 std::vector
<std::string
> encodings(input_encodings());
1266 if (std::find(encodings
.begin(), encodings
.end(), "UTF-8") == encodings
.end())
1267 encodings
.push_back("UTF-8");
1268 for (std::vector
<std::string
>::const_iterator
i(encodings
.begin());
1269 i
!= encodings
.end(); ++i
) {
1270 if (TryEncoding(search_terms_args
.search_terms
,
1271 search_terms_args
.original_query
, i
->c_str(),
1272 is_in_query
, encoded_terms
, encoded_original_query
)) {
1273 *input_encoding
= *i
;
1280 void TemplateURL::CopyFrom(const TemplateURL
& other
) {
1284 profile_
= other
.profile_
;
1285 data_
= other
.data_
;
1286 url_ref_
.InvalidateCachedValues();
1287 suggestions_url_ref_
.InvalidateCachedValues();
1288 instant_url_ref_
.InvalidateCachedValues();
1289 SetPrepopulateId(other
.data_
.prepopulate_id
);
1292 void TemplateURL::SetURL(const std::string
& url
) {
1294 url_ref_
.InvalidateCachedValues();
1297 void TemplateURL::SetPrepopulateId(int id
) {
1298 data_
.prepopulate_id
= id
;
1299 const bool prepopulated
= id
> 0;
1300 url_ref_
.prepopulated_
= prepopulated
;
1301 suggestions_url_ref_
.prepopulated_
= prepopulated
;
1302 instant_url_ref_
.prepopulated_
= prepopulated
;
1305 void TemplateURL::ResetKeywordIfNecessary(bool force
) {
1306 if (IsGoogleSearchURLWithReplaceableKeyword() || force
) {
1307 DCHECK(GetType() != OMNIBOX_API_EXTENSION
);
1308 GURL
url(TemplateURLService::GenerateSearchURL(this));
1310 data_
.SetKeyword(TemplateURLService::GenerateKeyword(url
));
1314 bool TemplateURL::FindSearchTermsInURL(
1316 const SearchTermsData
& search_terms_data
,
1317 base::string16
* search_terms
,
1318 url_parse::Parsed::ComponentType
* search_term_component
,
1319 url_parse::Component
* search_terms_position
) {
1320 DCHECK(search_terms
);
1321 search_terms
->clear();
1323 // Try to match with every pattern.
1324 for (size_t i
= 0; i
< URLCount(); ++i
) {
1325 TemplateURLRef
ref(this, i
);
1326 if (ref
.ExtractSearchTermsFromURL(url
, search_terms
, search_terms_data
,
1327 search_term_component
, search_terms_position
)) {
1328 // If ExtractSearchTermsFromURL() returns true and |search_terms| is empty
1329 // it means the pattern matched but no search terms were present. In this
1330 // case we fail immediately without looking for matches in subsequent
1331 // patterns. This means that given patterns
1332 // [ "http://foo/#q={searchTerms}", "http://foo/?q={searchTerms}" ],
1333 // calling ExtractSearchTermsFromURL() on "http://foo/?q=bar#q=' would
1334 // return false. This is important for at least Google, where such URLs
1336 return !search_terms
->empty();