OLD | NEW |
1 // Copyright (c) 2012 The Chromium Authors. All rights reserved. | 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 | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "chrome/browser/search_engines/template_url.h" | 5 #include "chrome/browser/search_engines/template_url.h" |
6 | 6 |
| 7 #include "base/command_line.h" |
7 #include "base/format_macros.h" | 8 #include "base/format_macros.h" |
8 #include "base/guid.h" | 9 #include "base/guid.h" |
9 #include "base/i18n/case_conversion.h" | 10 #include "base/i18n/case_conversion.h" |
10 #include "base/i18n/icu_string_conversions.h" | 11 #include "base/i18n/icu_string_conversions.h" |
11 #include "base/i18n/rtl.h" | 12 #include "base/i18n/rtl.h" |
12 #include "base/logging.h" | 13 #include "base/logging.h" |
13 #include "base/metrics/field_trial.h" | 14 #include "base/metrics/field_trial.h" |
14 #include "base/strings/string_number_conversions.h" | 15 #include "base/strings/string_number_conversions.h" |
15 #include "base/strings/string_util.h" | 16 #include "base/strings/string_util.h" |
16 #include "base/strings/stringprintf.h" | 17 #include "base/strings/stringprintf.h" |
17 #include "base/strings/utf_string_conversions.h" | 18 #include "base/strings/utf_string_conversions.h" |
18 #include "chrome/browser/google/google_util.h" | 19 #include "chrome/browser/google/google_util.h" |
19 #include "chrome/browser/search_engines/search_terms_data.h" | 20 #include "chrome/browser/search_engines/search_terms_data.h" |
20 #include "chrome/browser/search_engines/template_url_service.h" | 21 #include "chrome/browser/search_engines/template_url_service.h" |
| 22 #include "chrome/common/chrome_switches.h" |
21 #include "chrome/common/url_constants.h" | 23 #include "chrome/common/url_constants.h" |
22 #include "extensions/common/constants.h" | 24 #include "extensions/common/constants.h" |
23 #include "google_apis/google_api_keys.h" | 25 #include "google_apis/google_api_keys.h" |
24 #include "net/base/escape.h" | 26 #include "net/base/escape.h" |
25 #include "ui/base/l10n/l10n_util.h" | 27 #include "ui/base/l10n/l10n_util.h" |
26 | 28 |
27 namespace { | 29 namespace { |
28 | 30 |
29 // The TemplateURLRef has any number of terms that need to be replaced. Each of | 31 // The TemplateURLRef has any number of terms that need to be replaced. Each of |
30 // the terms is enclosed in braces. If the character preceeding the final | 32 // the terms is enclosed in braces. If the character preceeding the final |
(...skipping 117 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
148 | 150 |
149 } // namespace | 151 } // namespace |
150 | 152 |
151 | 153 |
152 // TemplateURLRef::SearchTermsArgs -------------------------------------------- | 154 // TemplateURLRef::SearchTermsArgs -------------------------------------------- |
153 | 155 |
154 TemplateURLRef::SearchTermsArgs::SearchTermsArgs(const string16& search_terms) | 156 TemplateURLRef::SearchTermsArgs::SearchTermsArgs(const string16& search_terms) |
155 : search_terms(search_terms), | 157 : search_terms(search_terms), |
156 accepted_suggestion(NO_SUGGESTIONS_AVAILABLE), | 158 accepted_suggestion(NO_SUGGESTIONS_AVAILABLE), |
157 cursor_position(string16::npos), | 159 cursor_position(string16::npos), |
158 omnibox_start_margin(-1) { | 160 omnibox_start_margin(-1), |
| 161 append_extra_query_params(false) { |
159 } | 162 } |
160 | 163 |
161 TemplateURLRef::SearchTermsArgs::~SearchTermsArgs() { | 164 TemplateURLRef::SearchTermsArgs::~SearchTermsArgs() { |
162 } | 165 } |
163 | 166 |
164 | 167 |
165 // TemplateURLRef ------------------------------------------------------------- | 168 // TemplateURLRef ------------------------------------------------------------- |
166 | 169 |
167 TemplateURLRef::TemplateURLRef(TemplateURL* owner, Type type) | 170 TemplateURLRef::TemplateURLRef(TemplateURL* owner, Type type) |
168 : owner_(owner), | 171 : owner_(owner), |
(...skipping 51 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
220 return ReplaceSearchTermsUsingTermsData(search_terms_args, search_terms_data); | 223 return ReplaceSearchTermsUsingTermsData(search_terms_args, search_terms_data); |
221 } | 224 } |
222 | 225 |
223 std::string TemplateURLRef::ReplaceSearchTermsUsingTermsData( | 226 std::string TemplateURLRef::ReplaceSearchTermsUsingTermsData( |
224 const SearchTermsArgs& search_terms_args, | 227 const SearchTermsArgs& search_terms_args, |
225 const SearchTermsData& search_terms_data) const { | 228 const SearchTermsData& search_terms_data) const { |
226 ParseIfNecessaryUsingTermsData(search_terms_data); | 229 ParseIfNecessaryUsingTermsData(search_terms_data); |
227 if (!valid_) | 230 if (!valid_) |
228 return std::string(); | 231 return std::string(); |
229 | 232 |
230 if (replacements_.empty()) | 233 std::string url(HandleReplacements(search_terms_args, search_terms_data)); |
231 return parsed_url_; | |
232 | 234 |
233 // Determine if the search terms are in the query or before. We're escaping | 235 // If the user specified additional query params on the command line, add |
234 // space as '+' in the former case and as '%20' in the latter case. | 236 // them. |
235 bool is_in_query = true; | 237 if (search_terms_args.append_extra_query_params) { |
236 for (Replacements::iterator i = replacements_.begin(); | 238 std::string query_params(CommandLine::ForCurrentProcess()-> |
237 i != replacements_.end(); ++i) { | 239 GetSwitchValueASCII(switches::kExtraSearchQueryParams)); |
238 if (i->type == SEARCH_TERMS) { | 240 GURL gurl(url); |
239 string16::size_type query_start = parsed_url_.find('?'); | 241 if (!query_params.empty() && gurl.is_valid()) { |
240 is_in_query = query_start != string16::npos && | 242 GURL::Replacements replacements; |
241 (static_cast<string16::size_type>(i->index) > query_start); | 243 const std::string existing_query_params(gurl.query()); |
242 break; | 244 if (!existing_query_params.empty()) |
243 } | 245 query_params += "&" + existing_query_params; |
244 } | 246 replacements.SetQueryStr(query_params); |
245 | 247 return gurl.ReplaceComponents(replacements).possibly_invalid_spec(); |
246 std::string input_encoding; | |
247 string16 encoded_terms; | |
248 string16 encoded_original_query; | |
249 owner_->EncodeSearchTerms(search_terms_args, is_in_query, &input_encoding, | |
250 &encoded_terms, &encoded_original_query); | |
251 | |
252 std::string url = parsed_url_; | |
253 | |
254 // replacements_ is ordered in ascending order, as such we need to iterate | |
255 // from the back. | |
256 for (Replacements::reverse_iterator i = replacements_.rbegin(); | |
257 i != replacements_.rend(); ++i) { | |
258 switch (i->type) { | |
259 case ENCODING: | |
260 url.insert(i->index, input_encoding); | |
261 break; | |
262 | |
263 case GOOGLE_ASSISTED_QUERY_STATS: | |
264 if (!search_terms_args.assisted_query_stats.empty()) { | |
265 // Get the base URL without substituting AQS to avoid infinite | |
266 // recursion. We need the URL to find out if it meets all | |
267 // AQS requirements (e.g. HTTPS protocol check). | |
268 // See TemplateURLRef::SearchTermsArgs for more details. | |
269 SearchTermsArgs search_terms_args_without_aqs(search_terms_args); | |
270 search_terms_args_without_aqs.assisted_query_stats.clear(); | |
271 GURL base_url(ReplaceSearchTermsUsingTermsData( | |
272 search_terms_args_without_aqs, search_terms_data)); | |
273 if (base_url.SchemeIs(chrome::kHttpsScheme)) { | |
274 url.insert(i->index, | |
275 "aqs=" + search_terms_args.assisted_query_stats + "&"); | |
276 } | |
277 } | |
278 break; | |
279 | |
280 case GOOGLE_BASE_URL: | |
281 url.insert(i->index, search_terms_data.GoogleBaseURLValue()); | |
282 break; | |
283 | |
284 case GOOGLE_BASE_SUGGEST_URL: | |
285 url.insert(i->index, search_terms_data.GoogleBaseSuggestURLValue()); | |
286 break; | |
287 | |
288 case GOOGLE_CURSOR_POSITION: | |
289 if (search_terms_args.cursor_position != string16::npos) | |
290 url.insert(i->index, | |
291 base::StringPrintf("cp=%" PRIuS "&", | |
292 search_terms_args.cursor_position)); | |
293 break; | |
294 | |
295 case GOOGLE_INSTANT_ENABLED: | |
296 url.insert(i->index, search_terms_data.InstantEnabledParam()); | |
297 break; | |
298 | |
299 case GOOGLE_INSTANT_EXTENDED_ENABLED: | |
300 url.insert(i->index, search_terms_data.InstantExtendedEnabledParam()); | |
301 break; | |
302 | |
303 case GOOGLE_NTP_IS_THEMED: | |
304 url.insert(i->index, search_terms_data.NTPIsThemedParam()); | |
305 break; | |
306 | |
307 case GOOGLE_OMNIBOX_START_MARGIN: | |
308 if (search_terms_args.omnibox_start_margin >= 0) { | |
309 url.insert(i->index, "es_sm=" + | |
310 base::IntToString(search_terms_args.omnibox_start_margin) + "&"); | |
311 } | |
312 break; | |
313 | |
314 case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION: | |
315 if (search_terms_args.accepted_suggestion >= 0 || | |
316 !search_terms_args.assisted_query_stats.empty()) { | |
317 url.insert(i->index, "oq=" + UTF16ToUTF8(encoded_original_query) + | |
318 "&"); | |
319 } | |
320 break; | |
321 | |
322 case GOOGLE_RLZ: { | |
323 // On platforms that don't have RLZ, we still want this branch | |
324 // to happen so that we replace the RLZ template with the | |
325 // empty string. (If we don't handle this case, we hit a | |
326 // NOTREACHED below.) | |
327 string16 rlz_string = search_terms_data.GetRlzParameterValue(); | |
328 if (!rlz_string.empty()) { | |
329 url.insert(i->index, "rlz=" + UTF16ToUTF8(rlz_string) + "&"); | |
330 } | |
331 break; | |
332 } | |
333 | |
334 case GOOGLE_SEARCH_CLIENT: { | |
335 std::string client = search_terms_data.GetSearchClient(); | |
336 if (!client.empty()) | |
337 url.insert(i->index, "client=" + client + "&"); | |
338 break; | |
339 } | |
340 | |
341 case GOOGLE_SEARCH_FIELDTRIAL_GROUP: | |
342 // We are not currently running any fieldtrials that modulate the search | |
343 // url. If we do, then we'd have some conditional insert such as: | |
344 // url.insert(i->index, used_www ? "gcx=w&" : "gcx=c&"); | |
345 break; | |
346 | |
347 case GOOGLE_SUGGEST_CLIENT: | |
348 url.insert(i->index, search_terms_data.GetSuggestClient()); | |
349 break; | |
350 | |
351 case GOOGLE_UNESCAPED_SEARCH_TERMS: { | |
352 std::string unescaped_terms; | |
353 base::UTF16ToCodepage(search_terms_args.search_terms, | |
354 input_encoding.c_str(), | |
355 base::OnStringConversionError::SKIP, | |
356 &unescaped_terms); | |
357 url.insert(i->index, std::string(unescaped_terms.begin(), | |
358 unescaped_terms.end())); | |
359 break; | |
360 } | |
361 | |
362 case GOOGLE_ZERO_PREFIX_URL: | |
363 if (!search_terms_args.zero_prefix_url.empty()) { | |
364 const std::string& escaped_zero_prefix_url = | |
365 net::EscapeQueryParamValue(search_terms_args.zero_prefix_url, | |
366 true); | |
367 url.insert(i->index, "url=" + escaped_zero_prefix_url + "&"); | |
368 } | |
369 | |
370 break; | |
371 | |
372 case LANGUAGE: | |
373 url.insert(i->index, search_terms_data.GetApplicationLocale()); | |
374 break; | |
375 | |
376 case SEARCH_TERMS: | |
377 url.insert(i->index, UTF16ToUTF8(encoded_terms)); | |
378 break; | |
379 | |
380 default: | |
381 NOTREACHED(); | |
382 break; | |
383 } | 248 } |
384 } | 249 } |
385 | 250 |
386 return url; | 251 return url; |
387 } | 252 } |
388 | 253 |
389 bool TemplateURLRef::IsValid() const { | 254 bool TemplateURLRef::IsValid() const { |
390 UIThreadSearchTermsData search_terms_data(owner_->profile()); | 255 UIThreadSearchTermsData search_terms_data(owner_->profile()); |
391 return IsValidUsingTermsData(search_terms_data); | 256 return IsValidUsingTermsData(search_terms_data); |
392 } | 257 } |
(...skipping 333 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
726 std::string ref_key = FindSearchTermsKey(url.ref()); | 591 std::string ref_key = FindSearchTermsKey(url.ref()); |
727 if (query_key.empty() == ref_key.empty()) | 592 if (query_key.empty() == ref_key.empty()) |
728 return; // No key or multiple keys found. We only handle having one key. | 593 return; // No key or multiple keys found. We only handle having one key. |
729 search_term_key_ = query_key.empty() ? ref_key : query_key; | 594 search_term_key_ = query_key.empty() ? ref_key : query_key; |
730 search_term_key_location_ = query_key.empty() ? | 595 search_term_key_location_ = query_key.empty() ? |
731 url_parse::Parsed::REF : url_parse::Parsed::QUERY; | 596 url_parse::Parsed::REF : url_parse::Parsed::QUERY; |
732 host_ = url.host(); | 597 host_ = url.host(); |
733 path_ = url.path(); | 598 path_ = url.path(); |
734 } | 599 } |
735 | 600 |
| 601 std::string TemplateURLRef::HandleReplacements( |
| 602 const SearchTermsArgs& search_terms_args, |
| 603 const SearchTermsData& search_terms_data) const { |
| 604 if (replacements_.empty()) |
| 605 return parsed_url_; |
| 606 |
| 607 // Determine if the search terms are in the query or before. We're escaping |
| 608 // space as '+' in the former case and as '%20' in the latter case. |
| 609 bool is_in_query = true; |
| 610 for (Replacements::iterator i = replacements_.begin(); |
| 611 i != replacements_.end(); ++i) { |
| 612 if (i->type == SEARCH_TERMS) { |
| 613 string16::size_type query_start = parsed_url_.find('?'); |
| 614 is_in_query = query_start != string16::npos && |
| 615 (static_cast<string16::size_type>(i->index) > query_start); |
| 616 break; |
| 617 } |
| 618 } |
| 619 |
| 620 std::string input_encoding; |
| 621 string16 encoded_terms; |
| 622 string16 encoded_original_query; |
| 623 owner_->EncodeSearchTerms(search_terms_args, is_in_query, &input_encoding, |
| 624 &encoded_terms, &encoded_original_query); |
| 625 |
| 626 std::string url = parsed_url_; |
| 627 |
| 628 // replacements_ is ordered in ascending order, as such we need to iterate |
| 629 // from the back. |
| 630 for (Replacements::reverse_iterator i = replacements_.rbegin(); |
| 631 i != replacements_.rend(); ++i) { |
| 632 switch (i->type) { |
| 633 case ENCODING: |
| 634 url.insert(i->index, input_encoding); |
| 635 break; |
| 636 |
| 637 case GOOGLE_ASSISTED_QUERY_STATS: |
| 638 if (!search_terms_args.assisted_query_stats.empty()) { |
| 639 // Get the base URL without substituting AQS to avoid infinite |
| 640 // recursion. We need the URL to find out if it meets all |
| 641 // AQS requirements (e.g. HTTPS protocol check). |
| 642 // See TemplateURLRef::SearchTermsArgs for more details. |
| 643 SearchTermsArgs search_terms_args_without_aqs(search_terms_args); |
| 644 search_terms_args_without_aqs.assisted_query_stats.clear(); |
| 645 GURL base_url(ReplaceSearchTermsUsingTermsData( |
| 646 search_terms_args_without_aqs, search_terms_data)); |
| 647 if (base_url.SchemeIs(chrome::kHttpsScheme)) { |
| 648 url.insert(i->index, |
| 649 "aqs=" + search_terms_args.assisted_query_stats + "&"); |
| 650 } |
| 651 } |
| 652 break; |
| 653 |
| 654 case GOOGLE_BASE_URL: |
| 655 url.insert(i->index, search_terms_data.GoogleBaseURLValue()); |
| 656 break; |
| 657 |
| 658 case GOOGLE_BASE_SUGGEST_URL: |
| 659 url.insert(i->index, search_terms_data.GoogleBaseSuggestURLValue()); |
| 660 break; |
| 661 |
| 662 case GOOGLE_CURSOR_POSITION: |
| 663 if (search_terms_args.cursor_position != string16::npos) |
| 664 url.insert(i->index, |
| 665 base::StringPrintf("cp=%" PRIuS "&", |
| 666 search_terms_args.cursor_position)); |
| 667 break; |
| 668 |
| 669 case GOOGLE_INSTANT_ENABLED: |
| 670 url.insert(i->index, search_terms_data.InstantEnabledParam()); |
| 671 break; |
| 672 |
| 673 case GOOGLE_INSTANT_EXTENDED_ENABLED: |
| 674 url.insert(i->index, search_terms_data.InstantExtendedEnabledParam()); |
| 675 break; |
| 676 |
| 677 case GOOGLE_NTP_IS_THEMED: |
| 678 url.insert(i->index, search_terms_data.NTPIsThemedParam()); |
| 679 break; |
| 680 |
| 681 case GOOGLE_OMNIBOX_START_MARGIN: |
| 682 if (search_terms_args.omnibox_start_margin >= 0) { |
| 683 url.insert(i->index, "es_sm=" + |
| 684 base::IntToString(search_terms_args.omnibox_start_margin) + "&"); |
| 685 } |
| 686 break; |
| 687 |
| 688 case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION: |
| 689 if (search_terms_args.accepted_suggestion >= 0 || |
| 690 !search_terms_args.assisted_query_stats.empty()) { |
| 691 url.insert(i->index, "oq=" + UTF16ToUTF8(encoded_original_query) + |
| 692 "&"); |
| 693 } |
| 694 break; |
| 695 |
| 696 case GOOGLE_RLZ: { |
| 697 // On platforms that don't have RLZ, we still want this branch |
| 698 // to happen so that we replace the RLZ template with the |
| 699 // empty string. (If we don't handle this case, we hit a |
| 700 // NOTREACHED below.) |
| 701 string16 rlz_string = search_terms_data.GetRlzParameterValue(); |
| 702 if (!rlz_string.empty()) { |
| 703 url.insert(i->index, "rlz=" + UTF16ToUTF8(rlz_string) + "&"); |
| 704 } |
| 705 break; |
| 706 } |
| 707 |
| 708 case GOOGLE_SEARCH_CLIENT: { |
| 709 std::string client = search_terms_data.GetSearchClient(); |
| 710 if (!client.empty()) |
| 711 url.insert(i->index, "client=" + client + "&"); |
| 712 break; |
| 713 } |
| 714 |
| 715 case GOOGLE_SEARCH_FIELDTRIAL_GROUP: |
| 716 // We are not currently running any fieldtrials that modulate the search |
| 717 // url. If we do, then we'd have some conditional insert such as: |
| 718 // url.insert(i->index, used_www ? "gcx=w&" : "gcx=c&"); |
| 719 break; |
| 720 |
| 721 case GOOGLE_SUGGEST_CLIENT: |
| 722 url.insert(i->index, search_terms_data.GetSuggestClient()); |
| 723 break; |
| 724 |
| 725 case GOOGLE_UNESCAPED_SEARCH_TERMS: { |
| 726 std::string unescaped_terms; |
| 727 base::UTF16ToCodepage(search_terms_args.search_terms, |
| 728 input_encoding.c_str(), |
| 729 base::OnStringConversionError::SKIP, |
| 730 &unescaped_terms); |
| 731 url.insert(i->index, std::string(unescaped_terms.begin(), |
| 732 unescaped_terms.end())); |
| 733 break; |
| 734 } |
| 735 |
| 736 case GOOGLE_ZERO_PREFIX_URL: |
| 737 if (!search_terms_args.zero_prefix_url.empty()) { |
| 738 const std::string& escaped_zero_prefix_url = |
| 739 net::EscapeQueryParamValue(search_terms_args.zero_prefix_url, |
| 740 true); |
| 741 url.insert(i->index, "url=" + escaped_zero_prefix_url + "&"); |
| 742 } |
| 743 |
| 744 break; |
| 745 |
| 746 case LANGUAGE: |
| 747 url.insert(i->index, search_terms_data.GetApplicationLocale()); |
| 748 break; |
| 749 |
| 750 case SEARCH_TERMS: |
| 751 url.insert(i->index, UTF16ToUTF8(encoded_terms)); |
| 752 break; |
| 753 |
| 754 default: |
| 755 NOTREACHED(); |
| 756 break; |
| 757 } |
| 758 } |
| 759 |
| 760 return url; |
| 761 } |
| 762 |
736 | 763 |
737 // TemplateURLData ------------------------------------------------------------ | 764 // TemplateURLData ------------------------------------------------------------ |
738 | 765 |
739 TemplateURLData::TemplateURLData() | 766 TemplateURLData::TemplateURLData() |
740 : show_in_default_list(false), | 767 : show_in_default_list(false), |
741 safe_for_autoreplace(false), | 768 safe_for_autoreplace(false), |
742 id(0), | 769 id(0), |
743 date_created(base::Time::Now()), | 770 date_created(base::Time::Now()), |
744 last_modified(base::Time::Now()), | 771 last_modified(base::Time::Now()), |
745 created_by_policy(false), | 772 created_by_policy(false), |
(...skipping 273 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1019 // patterns. This means that given patterns | 1046 // patterns. This means that given patterns |
1020 // [ "http://foo/#q={searchTerms}", "http://foo/?q={searchTerms}" ], | 1047 // [ "http://foo/#q={searchTerms}", "http://foo/?q={searchTerms}" ], |
1021 // calling ExtractSearchTermsFromURL() on "http://foo/?q=bar#q=' would | 1048 // calling ExtractSearchTermsFromURL() on "http://foo/?q=bar#q=' would |
1022 // return false. This is important for at least Google, where such URLs | 1049 // return false. This is important for at least Google, where such URLs |
1023 // are invalid. | 1050 // are invalid. |
1024 return !search_terms->empty(); | 1051 return !search_terms->empty(); |
1025 } | 1052 } |
1026 } | 1053 } |
1027 return false; | 1054 return false; |
1028 } | 1055 } |
OLD | NEW |