blob: 015a2f34c7bdc8ab8d7a00fbed504a363b6bbafd [file] [log] [blame]
[email protected]56ad3792010-05-28 17:45:331// Copyright (c) 2010 The Chromium Authors. All rights reserved.
[email protected]d82443b2009-01-15 19:54:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
[email protected]d54e03a52009-01-16 00:31:045#include "chrome/browser/search_engines/template_url.h"
[email protected]d82443b2009-01-15 19:54:566
[email protected]a92b8642009-05-05 23:38:567#include "app/l10n_util.h"
[email protected]d6e58c6e2009-10-10 20:40:508#include "base/i18n/icu_string_conversions.h"
[email protected]7cf1b6ce2010-03-20 06:37:019#include "base/i18n/rtl.h"
[email protected]d82443b2009-01-15 19:54:5610#include "base/logging.h"
[email protected]528c56d2010-07-30 19:28:4411#include "base/string_number_conversions.h"
[email protected]64048bd2010-03-08 23:28:5812#include "base/utf_string_conversions.h"
[email protected]375bd7312010-08-30 22:18:1313#include "chrome/browser/search_engines/search_terms_data.h"
[email protected]f63ae312009-02-04 17:58:4614#include "chrome/browser/search_engines/template_url_model.h"
[email protected]56ad3792010-05-28 17:45:3315#include "chrome/common/url_constants.h"
[email protected]cd818412010-03-19 03:23:1516#include "gfx/favicon_size.h"
[email protected]d82443b2009-01-15 19:54:5617#include "net/base/escape.h"
18
[email protected]d82443b2009-01-15 19:54:5619// The TemplateURLRef has any number of terms that need to be replaced. Each of
20// the terms is enclosed in braces. If the character preceeding the final
21// brace is a ?, it indicates the term is optional and can be replaced with
22// an empty string.
[email protected]ddd231e2010-06-29 20:35:1923static const char kStartParameter = '{';
24static const char kEndParameter = '}';
25static const char kOptional = '?';
[email protected]d82443b2009-01-15 19:54:5626
27// Known parameters found in the URL.
[email protected]ddd231e2010-06-29 20:35:1928static const char kSearchTermsParameter[] = "searchTerms";
[email protected]dbf476d2009-03-03 01:21:0929static const char kSearchTermsParameterFull[] = "{searchTerms}";
[email protected]ddd231e2010-06-29 20:35:1930static const char kCountParameter[] = "count";
31static const char kStartIndexParameter[] = "startIndex";
32static const char kStartPageParameter[] = "startPage";
33static const char kLanguageParameter[] = "language";
34static const char kInputEncodingParameter[] = "inputEncoding";
35static const char kOutputEncodingParameter[] = "outputEncoding";
[email protected]d82443b2009-01-15 19:54:5636
[email protected]ddd231e2010-06-29 20:35:1937static const char kGoogleAcceptedSuggestionParameter[] =
38 "google:acceptedSuggestion";
[email protected]d82443b2009-01-15 19:54:5639// Host/Domain Google searches are relative to.
[email protected]ddd231e2010-06-29 20:35:1940static const char kGoogleBaseURLParameter[] = "google:baseURL";
[email protected]dbf476d2009-03-03 01:21:0941static const char kGoogleBaseURLParameterFull[] = "{google:baseURL}";
[email protected]d82443b2009-01-15 19:54:5642// Like google:baseURL, but for the Search Suggest capability.
[email protected]dbf476d2009-03-03 01:21:0943static const char kGoogleBaseSuggestURLParameter[] =
44 "google:baseSuggestURL";
45static const char kGoogleBaseSuggestURLParameterFull[] =
46 "{google:baseSuggestURL}";
[email protected]ddd231e2010-06-29 20:35:1947static const char kGoogleOriginalQueryForSuggestionParameter[] =
48 "google:originalQueryForSuggestion";
49static const char kGoogleRLZParameter[] = "google:RLZ";
[email protected]d82443b2009-01-15 19:54:5650// Same as kSearchTermsParameter, with no escaping.
[email protected]ddd231e2010-06-29 20:35:1951static const char kGoogleUnescapedSearchTermsParameter[] =
52 "google:unescapedSearchTerms";
[email protected]dbf476d2009-03-03 01:21:0953static const char kGoogleUnescapedSearchTermsParameterFull[] =
54 "{google:unescapedSearchTerms}";
[email protected]d82443b2009-01-15 19:54:5655
56// Display value for kSearchTermsParameter.
[email protected]dbf476d2009-03-03 01:21:0957static const char kDisplaySearchTerms[] = "%s";
[email protected]d82443b2009-01-15 19:54:5658
59// Display value for kGoogleUnescapedSearchTermsParameter.
[email protected]dbf476d2009-03-03 01:21:0960static const char kDisplayUnescapedSearchTerms[] = "%S";
[email protected]d82443b2009-01-15 19:54:5661
62// Used if the count parameter is not optional. Indicates we want 10 search
63// results.
[email protected]ddd231e2010-06-29 20:35:1964static const char kDefaultCount[] = "10";
[email protected]d82443b2009-01-15 19:54:5665
66// Used if the parameter kOutputEncodingParameter is required.
[email protected]ddd231e2010-06-29 20:35:1967static const char kOutputEncodingType[] = "UTF-8";
[email protected]d82443b2009-01-15 19:54:5668
[email protected]d82443b2009-01-15 19:54:5669TemplateURLRef::TemplateURLRef() {
[email protected]ddd231e2010-06-29 20:35:1970 Set(std::string(), 0, 0);
[email protected]d82443b2009-01-15 19:54:5671}
72
[email protected]dcd869c2010-08-30 20:15:2573TemplateURLRef::TemplateURLRef(const std::string& url,
74 int index_offset,
75 int page_offset)
76 : url_(url),
77 index_offset_(index_offset),
78 page_offset_(page_offset),
79 parsed_(false),
80 valid_(false),
81 supports_replacements_(false) {
82}
83
[email protected]ddd231e2010-06-29 20:35:1984void TemplateURLRef::Set(const std::string& url,
[email protected]d82443b2009-01-15 19:54:5685 int index_offset,
86 int page_offset) {
87 url_ = url;
88 index_offset_ = index_offset;
89 page_offset_ = page_offset;
90 InvalidateCachedValues();
91}
92
[email protected]dcd869c2010-08-30 20:15:2593TemplateURLRef::~TemplateURLRef() {
94}
95
[email protected]d82443b2009-01-15 19:54:5696bool TemplateURLRef::ParseParameter(size_t start,
97 size_t end,
[email protected]ddd231e2010-06-29 20:35:1998 std::string* url,
[email protected]d82443b2009-01-15 19:54:5699 Replacements* replacements) const {
100 DCHECK(start != std::string::npos &&
101 end != std::string::npos && end > start);
102 size_t length = end - start - 1;
103 bool optional = false;
104 if ((*url)[end - 1] == kOptional) {
105 optional = true;
106 length--;
107 }
[email protected]ddd231e2010-06-29 20:35:19108 std::string parameter(url->substr(start + 1, length));
109 std::string full_parameter(url->substr(start, end - start + 1));
[email protected]d82443b2009-01-15 19:54:56110 // Remove the parameter from the string.
111 url->erase(start, end - start + 1);
112 if (parameter == kSearchTermsParameter) {
[email protected]4c66d2a2010-05-11 04:36:05113 replacements->push_back(Replacement(SEARCH_TERMS, start));
[email protected]d82443b2009-01-15 19:54:56114 } else if (parameter == kCountParameter) {
115 if (!optional)
116 url->insert(start, kDefaultCount);
117 } else if (parameter == kStartIndexParameter) {
118 if (!optional) {
[email protected]528c56d2010-07-30 19:28:44119 url->insert(start, base::IntToString(index_offset_));
[email protected]d82443b2009-01-15 19:54:56120 }
121 } else if (parameter == kStartPageParameter) {
122 if (!optional) {
[email protected]528c56d2010-07-30 19:28:44123 url->insert(start, base::IntToString(page_offset_));
[email protected]d82443b2009-01-15 19:54:56124 }
125 } else if (parameter == kLanguageParameter) {
[email protected]4c66d2a2010-05-11 04:36:05126 replacements->push_back(Replacement(LANGUAGE, start));
[email protected]d82443b2009-01-15 19:54:56127 } else if (parameter == kInputEncodingParameter) {
[email protected]4c66d2a2010-05-11 04:36:05128 replacements->push_back(Replacement(ENCODING, start));
[email protected]d82443b2009-01-15 19:54:56129 } else if (parameter == kOutputEncodingParameter) {
130 if (!optional)
131 url->insert(start, kOutputEncodingType);
132 } else if (parameter == kGoogleAcceptedSuggestionParameter) {
[email protected]4c66d2a2010-05-11 04:36:05133 replacements->push_back(Replacement(GOOGLE_ACCEPTED_SUGGESTION, start));
[email protected]d82443b2009-01-15 19:54:56134 } else if (parameter == kGoogleBaseURLParameter) {
[email protected]4c66d2a2010-05-11 04:36:05135 replacements->push_back(Replacement(GOOGLE_BASE_URL, start));
[email protected]ddd231e2010-06-29 20:35:19136 } else if (parameter == kGoogleBaseSuggestURLParameter) {
[email protected]4c66d2a2010-05-11 04:36:05137 replacements->push_back(Replacement(GOOGLE_BASE_SUGGEST_URL, start));
[email protected]d82443b2009-01-15 19:54:56138 } else if (parameter == kGoogleOriginalQueryForSuggestionParameter) {
139 replacements->push_back(Replacement(GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION,
[email protected]4c66d2a2010-05-11 04:36:05140 start));
[email protected]d82443b2009-01-15 19:54:56141 } else if (parameter == kGoogleRLZParameter) {
[email protected]4c66d2a2010-05-11 04:36:05142 replacements->push_back(Replacement(GOOGLE_RLZ, start));
[email protected]d82443b2009-01-15 19:54:56143 } else if (parameter == kGoogleUnescapedSearchTermsParameter) {
[email protected]4c66d2a2010-05-11 04:36:05144 replacements->push_back(Replacement(GOOGLE_UNESCAPED_SEARCH_TERMS, start));
[email protected]81c6ef62010-01-21 09:58:47145 } else {
146 // It can be some garbage but can also be a javascript block. Put it back.
147 url->insert(start, full_parameter);
[email protected]d82443b2009-01-15 19:54:56148 return false;
149 }
150 return true;
151}
152
[email protected]ddd231e2010-06-29 20:35:19153std::string TemplateURLRef::ParseURL(const std::string& url,
154 Replacements* replacements,
155 bool* valid) const {
[email protected]d82443b2009-01-15 19:54:56156 *valid = false;
[email protected]ddd231e2010-06-29 20:35:19157 std::string parsed_url = url;
[email protected]d82443b2009-01-15 19:54:56158 for (size_t last = 0; last != std::string::npos; ) {
159 last = parsed_url.find(kStartParameter, last);
160 if (last != std::string::npos) {
[email protected]81c6ef62010-01-21 09:58:47161 size_t template_end = parsed_url.find(kEndParameter, last);
162 if (template_end != std::string::npos) {
163 // Since we allow Javascript in the URL, {} pairs could be nested. Match
164 // only leaf pairs with supported parameters.
165 size_t next_template_start = parsed_url.find(kStartParameter, last + 1);
166 if (next_template_start == std::string::npos ||
167 next_template_start > template_end) {
168 // If successful, ParseParameter erases from the string as such no
169 // need to update |last|. If failed, move |last| to the end of pair.
170 if (!ParseParameter(last, template_end, &parsed_url, replacements)) {
171 // |template_end| + 1 may be beyond the end of the string.
172 last = template_end;
173 }
174 } else {
175 last = next_template_start;
[email protected]d82443b2009-01-15 19:54:56176 }
[email protected]d82443b2009-01-15 19:54:56177 } else {
178 // Open brace without a closing brace, return.
[email protected]ddd231e2010-06-29 20:35:19179 return std::string();
[email protected]d82443b2009-01-15 19:54:56180 }
181 }
182 }
183 *valid = true;
184 return parsed_url;
185}
186
187void TemplateURLRef::ParseIfNecessary() const {
[email protected]923d733d12010-09-03 00:53:31188 UIThreadSearchTermsData search_terms_data;
189 ParseIfNecessaryUsingTermsData(search_terms_data);
190}
191
192void TemplateURLRef::ParseIfNecessaryUsingTermsData(
193 const SearchTermsData& search_terms_data) const {
[email protected]d82443b2009-01-15 19:54:56194 if (!parsed_) {
195 parsed_ = true;
196 parsed_url_ = ParseURL(url_, &replacements_, &valid_);
197 supports_replacements_ = false;
198 if (valid_) {
199 bool has_only_one_search_term = false;
200 for (Replacements::const_iterator i = replacements_.begin();
201 i != replacements_.end(); ++i) {
202 if ((i->type == SEARCH_TERMS) ||
203 (i->type == GOOGLE_UNESCAPED_SEARCH_TERMS)) {
204 if (has_only_one_search_term) {
205 has_only_one_search_term = false;
206 break;
207 }
208 has_only_one_search_term = true;
209 supports_replacements_ = true;
210 }
211 }
212 // Only parse the host/key if there is one search term. Technically there
213 // could be more than one term, but it's uncommon; so we punt.
214 if (has_only_one_search_term)
[email protected]923d733d12010-09-03 00:53:31215 ParseHostAndSearchTermKey(search_terms_data);
[email protected]d82443b2009-01-15 19:54:56216 }
217 }
218}
219
[email protected]923d733d12010-09-03 00:53:31220void TemplateURLRef::ParseHostAndSearchTermKey(
221 const SearchTermsData& search_terms_data) const {
[email protected]ddd231e2010-06-29 20:35:19222 std::string url_string = url_;
[email protected]d82443b2009-01-15 19:54:56223 ReplaceSubstringsAfterOffset(&url_string, 0,
[email protected]ddd231e2010-06-29 20:35:19224 kGoogleBaseURLParameterFull,
[email protected]375bd7312010-08-30 22:18:13225 search_terms_data.GoogleBaseURLValue());
[email protected]dbf476d2009-03-03 01:21:09226 ReplaceSubstringsAfterOffset(&url_string, 0,
[email protected]ddd231e2010-06-29 20:35:19227 kGoogleBaseSuggestURLParameterFull,
[email protected]375bd7312010-08-30 22:18:13228 search_terms_data.GoogleBaseSuggestURLValue());
[email protected]d82443b2009-01-15 19:54:56229
[email protected]ddd231e2010-06-29 20:35:19230 GURL url(url_string);
[email protected]d82443b2009-01-15 19:54:56231 if (!url.is_valid())
232 return;
233
234 std::string query_string = url.query();
235 if (query_string.empty())
236 return;
237
238 url_parse::Component query, key, value;
239 query.len = static_cast<int>(query_string.size());
240 while (url_parse::ExtractQueryKeyValue(query_string.c_str(), &query, &key,
241 &value)) {
242 if (key.is_nonempty() && value.is_nonempty()) {
243 std::string value_string = query_string.substr(value.begin, value.len);
[email protected]dbf476d2009-03-03 01:21:09244 if (value_string.find(kSearchTermsParameterFull, 0) !=
[email protected]d82443b2009-01-15 19:54:56245 std::string::npos ||
[email protected]dbf476d2009-03-03 01:21:09246 value_string.find(kGoogleUnescapedSearchTermsParameterFull, 0) !=
[email protected]d82443b2009-01-15 19:54:56247 std::string::npos) {
248 search_term_key_ = query_string.substr(key.begin, key.len);
249 host_ = url.host();
250 path_ = url.path();
251 break;
252 }
253 }
254 }
255}
256
[email protected]375bd7312010-08-30 22:18:13257// static
258void TemplateURLRef::SetGoogleBaseURL(std::string* google_base_url) {
259 UIThreadSearchTermsData::SetGoogleBaseURL(google_base_url);
260}
261
[email protected]ddd231e2010-06-29 20:35:19262std::string TemplateURLRef::ReplaceSearchTerms(
[email protected]d82443b2009-01-15 19:54:56263 const TemplateURL& host,
264 const std::wstring& terms,
265 int accepted_suggestion,
266 const std::wstring& original_query_for_suggestion) const {
[email protected]375bd7312010-08-30 22:18:13267 UIThreadSearchTermsData search_terms_data;
268 return ReplaceSearchTermsUsingTermsData(host,
269 terms,
270 accepted_suggestion,
271 original_query_for_suggestion,
272 search_terms_data);
273}
[email protected]6506f2ae2010-08-27 06:23:57274
[email protected]375bd7312010-08-30 22:18:13275std::string TemplateURLRef::ReplaceSearchTermsUsingTermsData(
276 const TemplateURL& host,
277 const std::wstring& terms,
278 int accepted_suggestion,
279 const std::wstring& original_query_for_suggestion,
280 const SearchTermsData& search_terms_data) const {
[email protected]923d733d12010-09-03 00:53:31281 ParseIfNecessaryUsingTermsData(search_terms_data);
[email protected]d82443b2009-01-15 19:54:56282 if (!valid_)
[email protected]ddd231e2010-06-29 20:35:19283 return std::string();
[email protected]d82443b2009-01-15 19:54:56284
285 if (replacements_.empty())
[email protected]7b9f3672009-06-15 18:31:22286 return parsed_url_;
[email protected]d82443b2009-01-15 19:54:56287
[email protected]0d2e6a62010-01-15 20:09:19288 // Determine if the search terms are in the query or before. We're escaping
289 // space as '+' in the former case and as '%20' in the latter case.
[email protected]3c75f0d2010-03-02 05:51:17290 bool is_in_query = true;
[email protected]0d2e6a62010-01-15 20:09:19291 for (Replacements::iterator i = replacements_.begin();
292 i != replacements_.end(); ++i) {
293 if (i->type == SEARCH_TERMS) {
[email protected]ddd231e2010-06-29 20:35:19294 std::wstring::size_type query_start = parsed_url_.find('?');
[email protected]3c75f0d2010-03-02 05:51:17295 is_in_query = query_start != std::wstring::npos &&
[email protected]0d2e6a62010-01-15 20:09:19296 (static_cast<std::wstring::size_type>(i->index) > query_start);
297 break;
298 }
299 }
300
[email protected]48f619d2009-11-30 20:39:41301 string16 encoded_terms;
302 string16 encoded_original_query;
[email protected]ddd231e2010-06-29 20:35:19303 std::string input_encoding;
[email protected]3c75f0d2010-03-02 05:51:17304 // If the search terms are in query - escape them respecting the encoding.
305 if (is_in_query) {
306 // Encode the search terms so that we know the encoding.
307 const std::vector<std::string>& encodings = host.input_encodings();
308 for (size_t i = 0; i < encodings.size(); ++i) {
309 if (EscapeQueryParamValue(WideToUTF16Hack(terms),
310 encodings[i].c_str(), true,
311 &encoded_terms)) {
312 if (!original_query_for_suggestion.empty()) {
313 EscapeQueryParamValue(WideToUTF16Hack(original_query_for_suggestion),
314 encodings[i].c_str(),
315 true,
316 &encoded_original_query);
317 }
[email protected]ddd231e2010-06-29 20:35:19318 input_encoding = encodings[i];
[email protected]3c75f0d2010-03-02 05:51:17319 break;
[email protected]d82443b2009-01-15 19:54:56320 }
[email protected]d82443b2009-01-15 19:54:56321 }
[email protected]3c75f0d2010-03-02 05:51:17322 if (input_encoding.empty()) {
323 encoded_terms = WideToUTF16Hack(
324 EscapeQueryParamValueUTF8(terms, true));
325 if (!original_query_for_suggestion.empty()) {
326 encoded_original_query =
327 WideToUTF16Hack(EscapeQueryParamValueUTF8(
328 original_query_for_suggestion, true));
329 }
[email protected]ddd231e2010-06-29 20:35:19330 input_encoding = "UTF-8";
[email protected]d82443b2009-01-15 19:54:56331 }
[email protected]3c75f0d2010-03-02 05:51:17332 } else {
333 encoded_terms = WideToUTF16Hack(UTF8ToWide(EscapePath(WideToUTF8(terms))));
[email protected]ddd231e2010-06-29 20:35:19334 input_encoding = "UTF-8";
[email protected]d82443b2009-01-15 19:54:56335 }
336
[email protected]ddd231e2010-06-29 20:35:19337 std::string url = parsed_url_;
[email protected]d82443b2009-01-15 19:54:56338
339 // replacements_ is ordered in ascending order, as such we need to iterate
340 // from the back.
341 for (Replacements::reverse_iterator i = replacements_.rbegin();
342 i != replacements_.rend(); ++i) {
343 switch (i->type) {
344 case ENCODING:
345 url.insert(i->index, input_encoding);
346 break;
347
348 case GOOGLE_ACCEPTED_SUGGESTION:
349 if (accepted_suggestion == NO_SUGGESTION_CHOSEN)
[email protected]ddd231e2010-06-29 20:35:19350 url.insert(i->index, "aq=f&");
[email protected]d82443b2009-01-15 19:54:56351 else if (accepted_suggestion != NO_SUGGESTIONS_AVAILABLE)
[email protected]ddd231e2010-06-29 20:35:19352 url.insert(i->index, StringPrintf("aq=%d&", accepted_suggestion));
[email protected]d82443b2009-01-15 19:54:56353 break;
354
355 case GOOGLE_BASE_URL:
[email protected]375bd7312010-08-30 22:18:13356 url.insert(i->index, search_terms_data.GoogleBaseURLValue());
[email protected]d82443b2009-01-15 19:54:56357 break;
358
359 case GOOGLE_BASE_SUGGEST_URL:
[email protected]375bd7312010-08-30 22:18:13360 url.insert(i->index, search_terms_data.GoogleBaseSuggestURLValue());
[email protected]d82443b2009-01-15 19:54:56361 break;
362
363 case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION:
364 if (accepted_suggestion >= 0)
[email protected]ddd231e2010-06-29 20:35:19365 url.insert(i->index, "oq=" + UTF16ToUTF8(encoded_original_query) +
366 "&");
[email protected]d82443b2009-01-15 19:54:56367 break;
368
369 case GOOGLE_RLZ: {
[email protected]04999e62009-09-25 01:27:31370 // On platforms that don't have RLZ, we still want this branch
371 // to happen so that we replace the RLZ template with the
372 // empty string. (If we don't handle this case, we hit a
373 // NOTREACHED below.)
[email protected]58b64332010-08-13 16:09:39374#if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
[email protected]375bd7312010-08-30 22:18:13375 std::wstring rlz_string = search_terms_data.GetRlzParameterValue();
[email protected]d82443b2009-01-15 19:54:56376 if (!rlz_string.empty()) {
377 rlz_string = L"rlz=" + rlz_string + L"&";
[email protected]ddd231e2010-06-29 20:35:19378 url.insert(i->index, WideToUTF8(rlz_string));
[email protected]d82443b2009-01-15 19:54:56379 }
[email protected]04999e62009-09-25 01:27:31380#endif
[email protected]c5b911d2009-09-25 20:07:04381 break;
[email protected]d82443b2009-01-15 19:54:56382 }
383
384 case GOOGLE_UNESCAPED_SEARCH_TERMS: {
385 std::string unescaped_terms;
[email protected]ddd231e2010-06-29 20:35:19386 base::WideToCodepage(terms, input_encoding.c_str(),
[email protected]d6e58c6e2009-10-10 20:40:50387 base::OnStringConversionError::SKIP,
388 &unescaped_terms);
[email protected]ddd231e2010-06-29 20:35:19389 url.insert(i->index, std::string(unescaped_terms.begin(),
390 unescaped_terms.end()));
[email protected]d82443b2009-01-15 19:54:56391 break;
392 }
393
394 case LANGUAGE:
[email protected]375bd7312010-08-30 22:18:13395 url.insert(i->index, search_terms_data.GetApplicationLocale());
[email protected]d82443b2009-01-15 19:54:56396 break;
397
398 case SEARCH_TERMS:
[email protected]ddd231e2010-06-29 20:35:19399 url.insert(i->index, UTF16ToUTF8(encoded_terms));
[email protected]d82443b2009-01-15 19:54:56400 break;
401
402 default:
403 NOTREACHED();
404 break;
405 }
406 }
407
[email protected]7b9f3672009-06-15 18:31:22408 return url;
[email protected]d82443b2009-01-15 19:54:56409}
410
411bool TemplateURLRef::SupportsReplacement() const {
[email protected]923d733d12010-09-03 00:53:31412 UIThreadSearchTermsData search_terms_data;
413 return SupportsReplacementUsingTermsData(search_terms_data);
414}
415
416bool TemplateURLRef::SupportsReplacementUsingTermsData(
417 const SearchTermsData& search_terms_data) const {
418 ParseIfNecessaryUsingTermsData(search_terms_data);
[email protected]d82443b2009-01-15 19:54:56419 return valid_ && supports_replacements_;
420}
421
422bool TemplateURLRef::IsValid() const {
[email protected]923d733d12010-09-03 00:53:31423 UIThreadSearchTermsData search_terms_data;
424 return IsValidUsingTermsData(search_terms_data);
425}
426
427bool TemplateURLRef::IsValidUsingTermsData(
428 const SearchTermsData& search_terms_data) const {
429 ParseIfNecessaryUsingTermsData(search_terms_data);
[email protected]d82443b2009-01-15 19:54:56430 return valid_;
431}
432
433std::wstring TemplateURLRef::DisplayURL() const {
434 ParseIfNecessary();
[email protected]ddd231e2010-06-29 20:35:19435 if (!valid_ || replacements_.empty())
436 return UTF8ToWide(url_);
[email protected]d82443b2009-01-15 19:54:56437
[email protected]ddd231e2010-06-29 20:35:19438 string16 result = UTF8ToUTF16(url_);
[email protected]d82443b2009-01-15 19:54:56439 ReplaceSubstringsAfterOffset(&result, 0,
[email protected]dbf476d2009-03-03 01:21:09440 ASCIIToUTF16(kSearchTermsParameterFull),
441 ASCIIToUTF16(kDisplaySearchTerms));
[email protected]d82443b2009-01-15 19:54:56442
[email protected]dbf476d2009-03-03 01:21:09443 ReplaceSubstringsAfterOffset(
444 &result, 0,
445 ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull),
446 ASCIIToUTF16(kDisplayUnescapedSearchTerms));
447
448 return UTF16ToWideHack(result);
[email protected]d82443b2009-01-15 19:54:56449}
450
451// static
[email protected]ddd231e2010-06-29 20:35:19452std::string TemplateURLRef::DisplayURLToURLRef(
[email protected]d82443b2009-01-15 19:54:56453 const std::wstring& display_url) {
[email protected]dbf476d2009-03-03 01:21:09454 string16 result = WideToUTF16Hack(display_url);
455 ReplaceSubstringsAfterOffset(&result, 0, ASCIIToUTF16(kDisplaySearchTerms),
456 ASCIIToUTF16(kSearchTermsParameterFull));
457 ReplaceSubstringsAfterOffset(
458 &result, 0,
459 ASCIIToUTF16(kDisplayUnescapedSearchTerms),
460 ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull));
[email protected]ddd231e2010-06-29 20:35:19461 return UTF16ToUTF8(result);
[email protected]d82443b2009-01-15 19:54:56462}
463
464const std::string& TemplateURLRef::GetHost() const {
465 ParseIfNecessary();
466 return host_;
467}
468
469const std::string& TemplateURLRef::GetPath() const {
470 ParseIfNecessary();
471 return path_;
472}
473
474const std::string& TemplateURLRef::GetSearchTermKey() const {
475 ParseIfNecessary();
476 return search_term_key_;
477}
478
479std::wstring TemplateURLRef::SearchTermToWide(const TemplateURL& host,
480 const std::string& term) const {
481 const std::vector<std::string>& encodings = host.input_encodings();
482 std::wstring result;
483
484 std::string unescaped =
[email protected]7df43482009-07-31 19:37:44485 UnescapeURLComponent(term, UnescapeRule::REPLACE_PLUS_WITH_SPACE |
486 UnescapeRule::URL_SPECIAL_CHARS);
[email protected]d82443b2009-01-15 19:54:56487 for (size_t i = 0; i < encodings.size(); ++i) {
[email protected]d6e58c6e2009-10-10 20:40:50488 if (base::CodepageToWide(unescaped, encodings[i].c_str(),
489 base::OnStringConversionError::FAIL, &result))
[email protected]d82443b2009-01-15 19:54:56490 return result;
491 }
492
493 // Always fall back on UTF-8 if it works.
[email protected]d6e58c6e2009-10-10 20:40:50494 if (base::CodepageToWide(unescaped, base::kCodepageUTF8,
495 base::OnStringConversionError::FAIL, &result))
[email protected]d82443b2009-01-15 19:54:56496 return result;
497
498 // When nothing worked, just use the escaped text. We have no idea what the
499 // encoding is. We need to substitute spaces for pluses ourselves since we're
500 // not sending it through an unescaper.
501 result = UTF8ToWide(term);
502 std::replace(result.begin(), result.end(), '+', ' ');
503 return result;
504}
505
506bool TemplateURLRef::HasGoogleBaseURLs() const {
507 ParseIfNecessary();
508 for (size_t i = 0; i < replacements_.size(); ++i) {
509 if ((replacements_[i].type == GOOGLE_BASE_URL) ||
510 (replacements_[i].type == GOOGLE_BASE_SUGGEST_URL))
511 return true;
512 }
513 return false;
514}
515
516void TemplateURLRef::InvalidateCachedValues() const {
517 supports_replacements_ = valid_ = parsed_ = false;
518 host_.clear();
519 path_.clear();
520 search_term_key_.clear();
521 replacements_.clear();
522}
523
[email protected]d82443b2009-01-15 19:54:56524// TemplateURL ----------------------------------------------------------------
525
526// static
527GURL TemplateURL::GenerateFaviconURL(const GURL& url) {
528 DCHECK(url.is_valid());
529 GURL::Replacements rep;
530
531 const char favicon_path[] = "/favicon.ico";
532 int favicon_path_len = arraysize(favicon_path) - 1;
533
534 rep.SetPath(favicon_path, url_parse::Component(0, favicon_path_len));
535 rep.ClearUsername();
536 rep.ClearPassword();
537 rep.ClearQuery();
538 rep.ClearRef();
539 return url.ReplaceComponents(rep);
540}
541
[email protected]257ab712009-04-14 17:16:24542// static
543bool TemplateURL::SupportsReplacement(const TemplateURL* turl) {
[email protected]923d733d12010-09-03 00:53:31544 UIThreadSearchTermsData search_terms_data;
545 return SupportsReplacementUsingTermsData(turl, search_terms_data);
546}
547
548// static
549bool TemplateURL::SupportsReplacementUsingTermsData(
550 const TemplateURL* turl,
551 const SearchTermsData& search_terms_data) {
552 return turl && turl->url() &&
553 turl->url()->SupportsReplacementUsingTermsData(search_terms_data);
[email protected]257ab712009-04-14 17:16:24554}
555
[email protected]dcd869c2010-08-30 20:15:25556TemplateURL::TemplateURL()
557 : autogenerate_keyword_(false),
558 keyword_generated_(false),
559 show_in_default_list_(false),
560 safe_for_autoreplace_(false),
561 id_(0),
562 date_created_(base::Time::Now()),
[email protected]80808802010-09-03 03:54:06563 created_by_policy_(false),
[email protected]dcd869c2010-08-30 20:15:25564 usage_count_(0),
565 search_engine_type_(TemplateURLPrepopulateData::SEARCH_ENGINE_OTHER),
566 logo_id_(0),
567 prepopulate_id_(0) {
568}
569
570TemplateURL::~TemplateURL() {
571}
572
[email protected]bed9bd6c2009-04-21 17:27:47573std::wstring TemplateURL::AdjustedShortNameForLocaleDirection() const {
574 std::wstring bidi_safe_short_name;
[email protected]7cf1b6ce2010-03-20 06:37:01575 if (base::i18n::AdjustStringForLocaleDirection(short_name_,
576 &bidi_safe_short_name))
[email protected]bed9bd6c2009-04-21 17:27:47577 return bidi_safe_short_name;
578 return short_name_;
579}
580
[email protected]ddd231e2010-06-29 20:35:19581void TemplateURL::SetSuggestionsURL(const std::string& suggestions_url,
[email protected]d82443b2009-01-15 19:54:56582 int index_offset,
583 int page_offset) {
584 suggestions_url_.Set(suggestions_url, index_offset, page_offset);
585}
586
[email protected]ddd231e2010-06-29 20:35:19587void TemplateURL::SetURL(const std::string& url,
[email protected]d82443b2009-01-15 19:54:56588 int index_offset,
589 int page_offset) {
590 url_.Set(url, index_offset, page_offset);
591}
592
593void TemplateURL::set_keyword(const std::wstring& keyword) {
594 // Case sensitive keyword matching is confusing. As such, we force all
595 // keywords to be lower case.
[email protected]e5a8c472010-08-04 19:47:20596 keyword_ = UTF16ToWide(l10n_util::ToLower(WideToUTF16(keyword)));
[email protected]d82443b2009-01-15 19:54:56597 autogenerate_keyword_ = false;
598}
599
600const std::wstring& TemplateURL::keyword() const {
[email protected]774ef482010-08-27 22:55:56601 EnsureKeyword();
602 return keyword_;
603}
604
605void TemplateURL::EnsureKeyword() const {
[email protected]66789d62010-05-14 07:25:03606 if (autogenerate_keyword_ && !keyword_generated_) {
[email protected]d82443b2009-01-15 19:54:56607 // Generate a keyword and cache it.
608 keyword_ = TemplateURLModel::GenerateKeyword(
609 TemplateURLModel::GenerateSearchURL(this).GetWithEmptyPath(), true);
[email protected]66789d62010-05-14 07:25:03610 keyword_generated_ = true;
[email protected]d82443b2009-01-15 19:54:56611 }
[email protected]d82443b2009-01-15 19:54:56612}
613
614bool TemplateURL::ShowInDefaultList() const {
615 return show_in_default_list() && url() && url()->SupportsReplacement();
616}
617
618void TemplateURL::SetFavIconURL(const GURL& url) {
619 for (std::vector<ImageRef>::iterator i = image_refs_.begin();
620 i != image_refs_.end(); ++i) {
621 if (i->type == L"image/x-icon" &&
622 i->width == kFavIconSize && i->height == kFavIconSize) {
623 if (!url.is_valid())
624 image_refs_.erase(i);
625 else
626 i->url = url;
627 return;
628 }
629 }
630 // Don't have one yet, add it.
631 if (url.is_valid()) {
632 add_image_ref(
633 TemplateURL::ImageRef(L"image/x-icon", kFavIconSize, kFavIconSize,
634 url));
635 }
636}
637
638GURL TemplateURL::GetFavIconURL() const {
639 for (std::vector<ImageRef>::const_iterator i = image_refs_.begin();
640 i != image_refs_.end(); ++i) {
641 if ((i->type == L"image/x-icon" || i->type == L"image/vnd.microsoft.icon")
642 && i->width == kFavIconSize && i->height == kFavIconSize) {
643 return i->url;
644 }
645 }
646 return GURL();
647}
648
649void TemplateURL::InvalidateCachedValues() const {
650 url_.InvalidateCachedValues();
651 suggestions_url_.InvalidateCachedValues();
[email protected]66789d62010-05-14 07:25:03652 if (autogenerate_keyword_) {
[email protected]d82443b2009-01-15 19:54:56653 keyword_.clear();
[email protected]66789d62010-05-14 07:25:03654 keyword_generated_ = false;
655 }
[email protected]d82443b2009-01-15 19:54:56656}
[email protected]56ad3792010-05-28 17:45:33657
658std::string TemplateURL::GetExtensionId() const {
659 DCHECK(IsExtensionKeyword());
[email protected]ddd231e2010-06-29 20:35:19660 return GURL(url_.url()).host();
[email protected]56ad3792010-05-28 17:45:33661}
662
663bool TemplateURL::IsExtensionKeyword() const {
[email protected]ddd231e2010-06-29 20:35:19664 return GURL(url_.url()).SchemeIs(chrome::kExtensionScheme);
[email protected]56ad3792010-05-28 17:45:33665}