1 // Copyright (c) 2011 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 #include "chrome/browser/search_engines/template_url.h"
6
7 #include "base/i18n/icu_string_conversions.h"
8 #include "base/i18n/rtl.h"
9 #include "base/logging.h"
10 #include "base/string_number_conversions.h"
11 #include "base/utf_string_conversions.h"
12 #include "chrome/browser/metrics/user_metrics.h"
13 #include "chrome/browser/search_engines/search_engine_type.h"
14 #include "chrome/browser/search_engines/search_terms_data.h"
15 #include "chrome/browser/search_engines/template_url_model.h"
16 #include "chrome/common/url_constants.h"
17 #include "chrome/installer/util/google_update_settings.h"
18 #include "net/base/escape.h"
19 #include "ui/base/l10n/l10n_util.h"
20 #include "ui/gfx/favicon_size.h"
21 // TODO(pastarmovj): Remove google_update_settings and user_metrics when the
22 // CollectRLZMetrics function is not needed anymore.
23
24 // The TemplateURLRef has any number of terms that need to be replaced. Each of
25 // the terms is enclosed in braces. If the character preceeding the final
26 // brace is a ?, it indicates the term is optional and can be replaced with
27 // an empty string.
28 static const char kStartParameter = '{';
29 static const char kEndParameter = '}';
30 static const char kOptional = '?';
31
32 // Known parameters found in the URL.
33 static const char kSearchTermsParameter[] = "searchTerms";
34 static const char kSearchTermsParameterFull[] = "{searchTerms}";
35 static const char kCountParameter[] = "count";
36 static const char kStartIndexParameter[] = "startIndex";
37 static const char kStartPageParameter[] = "startPage";
38 static const char kLanguageParameter[] = "language";
39 static const char kInputEncodingParameter[] = "inputEncoding";
40 static const char kOutputEncodingParameter[] = "outputEncoding";
41
42 static const char kGoogleAcceptedSuggestionParameter[] =
43 "google:acceptedSuggestion";
44 // Host/Domain Google searches are relative to.
45 static const char kGoogleBaseURLParameter[] = "google:baseURL";
46 static const char kGoogleBaseURLParameterFull[] = "{google:baseURL}";
47 // Like google:baseURL, but for the Search Suggest capability.
48 static const char kGoogleBaseSuggestURLParameter[] =
49 "google:baseSuggestURL";
50 static const char kGoogleBaseSuggestURLParameterFull[] =
51 "{google:baseSuggestURL}";
52 static const char kGoogleOriginalQueryForSuggestionParameter[] =
53 "google:originalQueryForSuggestion";
54 static const char kGoogleRLZParameter[] = "google:RLZ";
55 // Same as kSearchTermsParameter, with no escaping.
56 static const char kGoogleUnescapedSearchTermsParameter[] =
57 "google:unescapedSearchTerms";
58 static const char kGoogleUnescapedSearchTermsParameterFull[] =
59 "{google:unescapedSearchTerms}";
60
61 // Display value for kSearchTermsParameter.
62 static const char kDisplaySearchTerms[] = "%s";
63
64 // Display value for kGoogleUnescapedSearchTermsParameter.
65 static const char kDisplayUnescapedSearchTerms[] = "%S";
66
67 // Used if the count parameter is not optional. Indicates we want 10 search
68 // results.
69 static const char kDefaultCount[] = "10";
70
71 // Used if the parameter kOutputEncodingParameter is required.
72 static const char kOutputEncodingType[] = "UTF-8";
73
TemplateURLRef()74 TemplateURLRef::TemplateURLRef() {
75 Set(std::string(), 0, 0);
76 }
77
TemplateURLRef(const std::string & url,int index_offset,int page_offset)78 TemplateURLRef::TemplateURLRef(const std::string& url,
79 int index_offset,
80 int page_offset)
81 : url_(url),
82 index_offset_(index_offset),
83 page_offset_(page_offset),
84 parsed_(false),
85 valid_(false),
86 supports_replacements_(false) {
87 }
88
Set(const std::string & url,int index_offset,int page_offset)89 void TemplateURLRef::Set(const std::string& url,
90 int index_offset,
91 int page_offset) {
92 url_ = url;
93 index_offset_ = index_offset;
94 page_offset_ = page_offset;
95 InvalidateCachedValues();
96 }
97
~TemplateURLRef()98 TemplateURLRef::~TemplateURLRef() {
99 }
100
ParseParameter(size_t start,size_t end,std::string * url,Replacements * replacements) const101 bool TemplateURLRef::ParseParameter(size_t start,
102 size_t end,
103 std::string* url,
104 Replacements* replacements) const {
105 DCHECK(start != std::string::npos &&
106 end != std::string::npos && end > start);
107 size_t length = end - start - 1;
108 bool optional = false;
109 if ((*url)[end - 1] == kOptional) {
110 optional = true;
111 length--;
112 }
113 std::string parameter(url->substr(start + 1, length));
114 std::string full_parameter(url->substr(start, end - start + 1));
115 // Remove the parameter from the string.
116 url->erase(start, end - start + 1);
117 if (parameter == kSearchTermsParameter) {
118 replacements->push_back(Replacement(SEARCH_TERMS, start));
119 } else if (parameter == kCountParameter) {
120 if (!optional)
121 url->insert(start, kDefaultCount);
122 } else if (parameter == kStartIndexParameter) {
123 if (!optional) {
124 url->insert(start, base::IntToString(index_offset_));
125 }
126 } else if (parameter == kStartPageParameter) {
127 if (!optional) {
128 url->insert(start, base::IntToString(page_offset_));
129 }
130 } else if (parameter == kLanguageParameter) {
131 replacements->push_back(Replacement(LANGUAGE, start));
132 } else if (parameter == kInputEncodingParameter) {
133 replacements->push_back(Replacement(ENCODING, start));
134 } else if (parameter == kOutputEncodingParameter) {
135 if (!optional)
136 url->insert(start, kOutputEncodingType);
137 } else if (parameter == kGoogleAcceptedSuggestionParameter) {
138 replacements->push_back(Replacement(GOOGLE_ACCEPTED_SUGGESTION, start));
139 } else if (parameter == kGoogleBaseURLParameter) {
140 replacements->push_back(Replacement(GOOGLE_BASE_URL, start));
141 } else if (parameter == kGoogleBaseSuggestURLParameter) {
142 replacements->push_back(Replacement(GOOGLE_BASE_SUGGEST_URL, start));
143 } else if (parameter == kGoogleOriginalQueryForSuggestionParameter) {
144 replacements->push_back(Replacement(GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION,
145 start));
146 } else if (parameter == kGoogleRLZParameter) {
147 replacements->push_back(Replacement(GOOGLE_RLZ, start));
148 } else if (parameter == kGoogleUnescapedSearchTermsParameter) {
149 replacements->push_back(Replacement(GOOGLE_UNESCAPED_SEARCH_TERMS, start));
150 } else {
151 // It can be some garbage but can also be a javascript block. Put it back.
152 url->insert(start, full_parameter);
153 return false;
154 }
155 return true;
156 }
157
ParseURL(const std::string & url,Replacements * replacements,bool * valid) const158 std::string TemplateURLRef::ParseURL(const std::string& url,
159 Replacements* replacements,
160 bool* valid) const {
161 *valid = false;
162 std::string parsed_url = url;
163 for (size_t last = 0; last != std::string::npos; ) {
164 last = parsed_url.find(kStartParameter, last);
165 if (last != std::string::npos) {
166 size_t template_end = parsed_url.find(kEndParameter, last);
167 if (template_end != std::string::npos) {
168 // Since we allow Javascript in the URL, {} pairs could be nested. Match
169 // only leaf pairs with supported parameters.
170 size_t next_template_start = parsed_url.find(kStartParameter, last + 1);
171 if (next_template_start == std::string::npos ||
172 next_template_start > template_end) {
173 // If successful, ParseParameter erases from the string as such no
174 // need to update |last|. If failed, move |last| to the end of pair.
175 if (!ParseParameter(last, template_end, &parsed_url, replacements)) {
176 // |template_end| + 1 may be beyond the end of the string.
177 last = template_end;
178 }
179 } else {
180 last = next_template_start;
181 }
182 } else {
183 // Open brace without a closing brace, return.
184 return std::string();
185 }
186 }
187 }
188 *valid = true;
189 return parsed_url;
190 }
191
ParseIfNecessary() const192 void TemplateURLRef::ParseIfNecessary() const {
193 UIThreadSearchTermsData search_terms_data;
194 ParseIfNecessaryUsingTermsData(search_terms_data);
195 }
196
ParseIfNecessaryUsingTermsData(const SearchTermsData & search_terms_data) const197 void TemplateURLRef::ParseIfNecessaryUsingTermsData(
198 const SearchTermsData& search_terms_data) const {
199 if (!parsed_) {
200 parsed_ = true;
201 parsed_url_ = ParseURL(url_, &replacements_, &valid_);
202 supports_replacements_ = false;
203 if (valid_) {
204 bool has_only_one_search_term = false;
205 for (Replacements::const_iterator i = replacements_.begin();
206 i != replacements_.end(); ++i) {
207 if ((i->type == SEARCH_TERMS) ||
208 (i->type == GOOGLE_UNESCAPED_SEARCH_TERMS)) {
209 if (has_only_one_search_term) {
210 has_only_one_search_term = false;
211 break;
212 }
213 has_only_one_search_term = true;
214 supports_replacements_ = true;
215 }
216 }
217 // Only parse the host/key if there is one search term. Technically there
218 // could be more than one term, but it's uncommon; so we punt.
219 if (has_only_one_search_term)
220 ParseHostAndSearchTermKey(search_terms_data);
221 }
222 }
223 }
224
ParseHostAndSearchTermKey(const SearchTermsData & search_terms_data) const225 void TemplateURLRef::ParseHostAndSearchTermKey(
226 const SearchTermsData& search_terms_data) const {
227 std::string url_string = url_;
228 ReplaceSubstringsAfterOffset(&url_string, 0,
229 kGoogleBaseURLParameterFull,
230 search_terms_data.GoogleBaseURLValue());
231 ReplaceSubstringsAfterOffset(&url_string, 0,
232 kGoogleBaseSuggestURLParameterFull,
233 search_terms_data.GoogleBaseSuggestURLValue());
234
235 GURL url(url_string);
236 if (!url.is_valid())
237 return;
238
239 std::string query_string = url.query();
240 if (query_string.empty())
241 return;
242
243 url_parse::Component query, key, value;
244 query.len = static_cast<int>(query_string.size());
245 while (url_parse::ExtractQueryKeyValue(query_string.c_str(), &query, &key,
246 &value)) {
247 if (key.is_nonempty() && value.is_nonempty()) {
248 std::string value_string = query_string.substr(value.begin, value.len);
249 if (value_string.find(kSearchTermsParameterFull, 0) !=
250 std::string::npos ||
251 value_string.find(kGoogleUnescapedSearchTermsParameterFull, 0) !=
252 std::string::npos) {
253 search_term_key_ = query_string.substr(key.begin, key.len);
254 host_ = url.host();
255 path_ = url.path();
256 break;
257 }
258 }
259 }
260 }
261
262 // static
SetGoogleBaseURL(std::string * google_base_url)263 void TemplateURLRef::SetGoogleBaseURL(std::string* google_base_url) {
264 UIThreadSearchTermsData::SetGoogleBaseURL(google_base_url);
265 }
266
ReplaceSearchTerms(const TemplateURL & host,const string16 & terms,int accepted_suggestion,const string16 & original_query_for_suggestion) const267 std::string TemplateURLRef::ReplaceSearchTerms(
268 const TemplateURL& host,
269 const string16& terms,
270 int accepted_suggestion,
271 const string16& original_query_for_suggestion) const {
272 UIThreadSearchTermsData search_terms_data;
273 return ReplaceSearchTermsUsingTermsData(host,
274 terms,
275 accepted_suggestion,
276 original_query_for_suggestion,
277 search_terms_data);
278 }
279
ReplaceSearchTermsUsingTermsData(const TemplateURL & host,const string16 & terms,int accepted_suggestion,const string16 & original_query_for_suggestion,const SearchTermsData & search_terms_data) const280 std::string TemplateURLRef::ReplaceSearchTermsUsingTermsData(
281 const TemplateURL& host,
282 const string16& terms,
283 int accepted_suggestion,
284 const string16& original_query_for_suggestion,
285 const SearchTermsData& search_terms_data) const {
286 ParseIfNecessaryUsingTermsData(search_terms_data);
287 if (!valid_)
288 return std::string();
289
290 if (replacements_.empty())
291 return parsed_url_;
292
293 // Determine if the search terms are in the query or before. We're escaping
294 // space as '+' in the former case and as '%20' in the latter case.
295 bool is_in_query = true;
296 for (Replacements::iterator i = replacements_.begin();
297 i != replacements_.end(); ++i) {
298 if (i->type == SEARCH_TERMS) {
299 string16::size_type query_start = parsed_url_.find('?');
300 is_in_query = query_start != string16::npos &&
301 (static_cast<string16::size_type>(i->index) > query_start);
302 break;
303 }
304 }
305
306 string16 encoded_terms;
307 string16 encoded_original_query;
308 std::string input_encoding;
309 // If the search terms are in query - escape them respecting the encoding.
310 if (is_in_query) {
311 // Encode the search terms so that we know the encoding.
312 const std::vector<std::string>& encodings = host.input_encodings();
313 for (size_t i = 0; i < encodings.size(); ++i) {
314 if (EscapeQueryParamValue(terms,
315 encodings[i].c_str(), true,
316 &encoded_terms)) {
317 if (!original_query_for_suggestion.empty()) {
318 EscapeQueryParamValue(original_query_for_suggestion,
319 encodings[i].c_str(),
320 true,
321 &encoded_original_query);
322 }
323 input_encoding = encodings[i];
324 break;
325 }
326 }
327 if (input_encoding.empty()) {
328 encoded_terms = EscapeQueryParamValueUTF8(terms, true);
329 if (!original_query_for_suggestion.empty()) {
330 encoded_original_query =
331 EscapeQueryParamValueUTF8(original_query_for_suggestion, true);
332 }
333 input_encoding = "UTF-8";
334 }
335 } else {
336 encoded_terms = UTF8ToUTF16(EscapePath(UTF16ToUTF8(terms)));
337 input_encoding = "UTF-8";
338 }
339
340 std::string url = parsed_url_;
341
342 // replacements_ is ordered in ascending order, as such we need to iterate
343 // from the back.
344 for (Replacements::reverse_iterator i = replacements_.rbegin();
345 i != replacements_.rend(); ++i) {
346 switch (i->type) {
347 case ENCODING:
348 url.insert(i->index, input_encoding);
349 break;
350
351 case GOOGLE_ACCEPTED_SUGGESTION:
352 if (accepted_suggestion == NO_SUGGESTION_CHOSEN)
353 url.insert(i->index, "aq=f&");
354 else if (accepted_suggestion != NO_SUGGESTIONS_AVAILABLE)
355 url.insert(i->index, StringPrintf("aq=%d&", accepted_suggestion));
356 break;
357
358 case GOOGLE_BASE_URL:
359 url.insert(i->index, search_terms_data.GoogleBaseURLValue());
360 break;
361
362 case GOOGLE_BASE_SUGGEST_URL:
363 url.insert(i->index, search_terms_data.GoogleBaseSuggestURLValue());
364 break;
365
366 case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION:
367 if (accepted_suggestion >= 0)
368 url.insert(i->index, "oq=" + UTF16ToUTF8(encoded_original_query) +
369 "&");
370 break;
371
372 case GOOGLE_RLZ: {
373 // On platforms that don't have RLZ, we still want this branch
374 // to happen so that we replace the RLZ template with the
375 // empty string. (If we don't handle this case, we hit a
376 // NOTREACHED below.)
377 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
378 string16 rlz_string = search_terms_data.GetRlzParameterValue();
379 if (!rlz_string.empty()) {
380 rlz_string = L"rlz=" + rlz_string + L"&";
381 url.insert(i->index, UTF16ToUTF8(rlz_string));
382 }
383 #endif
384 break;
385 }
386
387 case GOOGLE_UNESCAPED_SEARCH_TERMS: {
388 std::string unescaped_terms;
389 base::UTF16ToCodepage(terms, input_encoding.c_str(),
390 base::OnStringConversionError::SKIP,
391 &unescaped_terms);
392 url.insert(i->index, std::string(unescaped_terms.begin(),
393 unescaped_terms.end()));
394 break;
395 }
396
397 case LANGUAGE:
398 url.insert(i->index, search_terms_data.GetApplicationLocale());
399 break;
400
401 case SEARCH_TERMS:
402 url.insert(i->index, UTF16ToUTF8(encoded_terms));
403 break;
404
405 default:
406 NOTREACHED();
407 break;
408 }
409 }
410
411 return url;
412 }
413
SupportsReplacement() const414 bool TemplateURLRef::SupportsReplacement() const {
415 UIThreadSearchTermsData search_terms_data;
416 return SupportsReplacementUsingTermsData(search_terms_data);
417 }
418
SupportsReplacementUsingTermsData(const SearchTermsData & search_terms_data) const419 bool TemplateURLRef::SupportsReplacementUsingTermsData(
420 const SearchTermsData& search_terms_data) const {
421 ParseIfNecessaryUsingTermsData(search_terms_data);
422 return valid_ && supports_replacements_;
423 }
424
IsValid() const425 bool TemplateURLRef::IsValid() const {
426 UIThreadSearchTermsData search_terms_data;
427 return IsValidUsingTermsData(search_terms_data);
428 }
429
IsValidUsingTermsData(const SearchTermsData & search_terms_data) const430 bool TemplateURLRef::IsValidUsingTermsData(
431 const SearchTermsData& search_terms_data) const {
432 ParseIfNecessaryUsingTermsData(search_terms_data);
433 return valid_;
434 }
435
DisplayURL() const436 string16 TemplateURLRef::DisplayURL() const {
437 ParseIfNecessary();
438 if (!valid_ || replacements_.empty())
439 return UTF8ToUTF16(url_);
440
441 string16 result = UTF8ToUTF16(url_);
442 ReplaceSubstringsAfterOffset(&result, 0,
443 ASCIIToUTF16(kSearchTermsParameterFull),
444 ASCIIToUTF16(kDisplaySearchTerms));
445
446 ReplaceSubstringsAfterOffset(
447 &result, 0,
448 ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull),
449 ASCIIToUTF16(kDisplayUnescapedSearchTerms));
450
451 return result;
452 }
453
454 // static
DisplayURLToURLRef(const string16 & display_url)455 std::string TemplateURLRef::DisplayURLToURLRef(
456 const string16& display_url) {
457 string16 result = display_url;
458 ReplaceSubstringsAfterOffset(&result, 0, ASCIIToUTF16(kDisplaySearchTerms),
459 ASCIIToUTF16(kSearchTermsParameterFull));
460 ReplaceSubstringsAfterOffset(
461 &result, 0,
462 ASCIIToUTF16(kDisplayUnescapedSearchTerms),
463 ASCIIToUTF16(kGoogleUnescapedSearchTermsParameterFull));
464 return UTF16ToUTF8(result);
465 }
466
GetHost() const467 const std::string& TemplateURLRef::GetHost() const {
468 ParseIfNecessary();
469 return host_;
470 }
471
GetPath() const472 const std::string& TemplateURLRef::GetPath() const {
473 ParseIfNecessary();
474 return path_;
475 }
476
GetSearchTermKey() const477 const std::string& TemplateURLRef::GetSearchTermKey() const {
478 ParseIfNecessary();
479 return search_term_key_;
480 }
481
SearchTermToString16(const TemplateURL & host,const std::string & term) const482 string16 TemplateURLRef::SearchTermToString16(const TemplateURL& host,
483 const std::string& term) const {
484 const std::vector<std::string>& encodings = host.input_encodings();
485 string16 result;
486
487 std::string unescaped =
488 UnescapeURLComponent(term, UnescapeRule::REPLACE_PLUS_WITH_SPACE |
489 UnescapeRule::URL_SPECIAL_CHARS);
490 for (size_t i = 0; i < encodings.size(); ++i) {
491 if (base::CodepageToUTF16(unescaped, encodings[i].c_str(),
492 base::OnStringConversionError::FAIL, &result))
493 return result;
494 }
495
496 // Always fall back on UTF-8 if it works.
497 if (base::CodepageToUTF16(unescaped, base::kCodepageUTF8,
498 base::OnStringConversionError::FAIL, &result))
499 return result;
500
501 // When nothing worked, just use the escaped text. We have no idea what the
502 // encoding is. We need to substitute spaces for pluses ourselves since we're
503 // not sending it through an unescaper.
504 result = UTF8ToUTF16(term);
505 std::replace(result.begin(), result.end(), '+', ' ');
506 return result;
507 }
508
HasGoogleBaseURLs() const509 bool TemplateURLRef::HasGoogleBaseURLs() const {
510 ParseIfNecessary();
511 for (size_t i = 0; i < replacements_.size(); ++i) {
512 if ((replacements_[i].type == GOOGLE_BASE_URL) ||
513 (replacements_[i].type == GOOGLE_BASE_SUGGEST_URL))
514 return true;
515 }
516 return false;
517 }
518
519 // static
SameUrlRefs(const TemplateURLRef * ref1,const TemplateURLRef * ref2)520 bool TemplateURLRef::SameUrlRefs(const TemplateURLRef* ref1,
521 const TemplateURLRef* ref2) {
522 return ref1 == ref2 || (ref1 && ref2 && ref1->url() == ref2->url());
523 }
524
CollectRLZMetrics() const525 void TemplateURLRef::CollectRLZMetrics() const {
526 #if defined(OS_WIN) && defined(GOOGLE_CHROME_BUILD)
527 ParseIfNecessary();
528 for (size_t i = 0; i < replacements_.size(); ++i) {
529 // We are interesed in searches that were supposed to send the RLZ token.
530 if (replacements_[i].type == GOOGLE_RLZ) {
531 string16 brand;
532 // We only have RLZ tocken on a branded browser version.
533 if (GoogleUpdateSettings::GetBrand(&brand) && !brand.empty() &&
534 !GoogleUpdateSettings::IsOrganic(brand)) {
535 // Now we know we should have had RLZ token check if there was one.
536 if (url().find("rlz=") != std::string::npos)
537 UserMetrics::RecordAction(UserMetricsAction("SearchWithRLZ"));
538 else
539 UserMetrics::RecordAction(UserMetricsAction("SearchWithoutRLZ"));
540 }
541 return;
542 }
543 }
544 #endif
545 }
546
InvalidateCachedValues() const547 void TemplateURLRef::InvalidateCachedValues() const {
548 supports_replacements_ = valid_ = parsed_ = false;
549 host_.clear();
550 path_.clear();
551 search_term_key_.clear();
552 replacements_.clear();
553 }
554
555 // TemplateURL ----------------------------------------------------------------
556
557 // static
GenerateFaviconURL(const GURL & url)558 GURL TemplateURL::GenerateFaviconURL(const GURL& url) {
559 DCHECK(url.is_valid());
560 GURL::Replacements rep;
561
562 const char favicon_path[] = "/favicon.ico";
563 int favicon_path_len = arraysize(favicon_path) - 1;
564
565 rep.SetPath(favicon_path, url_parse::Component(0, favicon_path_len));
566 rep.ClearUsername();
567 rep.ClearPassword();
568 rep.ClearQuery();
569 rep.ClearRef();
570 return url.ReplaceComponents(rep);
571 }
572
573 // static
SupportsReplacement(const TemplateURL * turl)574 bool TemplateURL::SupportsReplacement(const TemplateURL* turl) {
575 UIThreadSearchTermsData search_terms_data;
576 return SupportsReplacementUsingTermsData(turl, search_terms_data);
577 }
578
579 // static
SupportsReplacementUsingTermsData(const TemplateURL * turl,const SearchTermsData & search_terms_data)580 bool TemplateURL::SupportsReplacementUsingTermsData(
581 const TemplateURL* turl,
582 const SearchTermsData& search_terms_data) {
583 return turl && turl->url() &&
584 turl->url()->SupportsReplacementUsingTermsData(search_terms_data);
585 }
586
TemplateURL()587 TemplateURL::TemplateURL()
588 : autogenerate_keyword_(false),
589 keyword_generated_(false),
590 show_in_default_list_(false),
591 safe_for_autoreplace_(false),
592 id_(0),
593 date_created_(base::Time::Now()),
594 created_by_policy_(false),
595 usage_count_(0),
596 search_engine_type_(SEARCH_ENGINE_OTHER),
597 logo_id_(kNoSearchEngineLogo),
598 prepopulate_id_(0) {
599 }
600
~TemplateURL()601 TemplateURL::~TemplateURL() {
602 }
603
AdjustedShortNameForLocaleDirection() const604 string16 TemplateURL::AdjustedShortNameForLocaleDirection() const {
605 string16 bidi_safe_short_name = short_name_;
606 base::i18n::AdjustStringForLocaleDirection(&bidi_safe_short_name);
607 return bidi_safe_short_name;
608 }
609
SetSuggestionsURL(const std::string & suggestions_url,int index_offset,int page_offset)610 void TemplateURL::SetSuggestionsURL(const std::string& suggestions_url,
611 int index_offset,
612 int page_offset) {
613 suggestions_url_.Set(suggestions_url, index_offset, page_offset);
614 }
615
SetURL(const std::string & url,int index_offset,int page_offset)616 void TemplateURL::SetURL(const std::string& url,
617 int index_offset,
618 int page_offset) {
619 url_.Set(url, index_offset, page_offset);
620 }
621
SetInstantURL(const std::string & url,int index_offset,int page_offset)622 void TemplateURL::SetInstantURL(const std::string& url,
623 int index_offset,
624 int page_offset) {
625 instant_url_.Set(url, index_offset, page_offset);
626 }
627
set_keyword(const string16 & keyword)628 void TemplateURL::set_keyword(const string16& keyword) {
629 // Case sensitive keyword matching is confusing. As such, we force all
630 // keywords to be lower case.
631 keyword_ = l10n_util::ToLower(keyword);
632 autogenerate_keyword_ = false;
633 }
634
keyword() const635 string16 TemplateURL::keyword() const {
636 EnsureKeyword();
637 return keyword_;
638 }
639
EnsureKeyword() const640 void TemplateURL::EnsureKeyword() const {
641 if (autogenerate_keyword_ && !keyword_generated_) {
642 // Generate a keyword and cache it.
643 keyword_ = TemplateURLModel::GenerateKeyword(
644 TemplateURLModel::GenerateSearchURL(this).GetWithEmptyPath(), true);
645 keyword_generated_ = true;
646 }
647 }
648
ShowInDefaultList() const649 bool TemplateURL::ShowInDefaultList() const {
650 return show_in_default_list() && url() && url()->SupportsReplacement();
651 }
652
SetFaviconURL(const GURL & url)653 void TemplateURL::SetFaviconURL(const GURL& url) {
654 for (std::vector<ImageRef>::iterator i = image_refs_.begin();
655 i != image_refs_.end(); ++i) {
656 if (i->type == "image/x-icon" &&
657 i->width == kFaviconSize && i->height == kFaviconSize) {
658 if (!url.is_valid())
659 image_refs_.erase(i);
660 else
661 i->url = url;
662 return;
663 }
664 }
665 // Don't have one yet, add it.
666 if (url.is_valid()) {
667 add_image_ref(
668 TemplateURL::ImageRef("image/x-icon", kFaviconSize,
669 kFaviconSize, url));
670 }
671 }
672
GetFaviconURL() const673 GURL TemplateURL::GetFaviconURL() const {
674 for (std::vector<ImageRef>::const_iterator i = image_refs_.begin();
675 i != image_refs_.end(); ++i) {
676 if ((i->type == "image/x-icon" || i->type == "image/vnd.microsoft.icon")
677 && i->width == kFaviconSize && i->height == kFaviconSize) {
678 return i->url;
679 }
680 }
681 return GURL();
682 }
683
InvalidateCachedValues() const684 void TemplateURL::InvalidateCachedValues() const {
685 url_.InvalidateCachedValues();
686 suggestions_url_.InvalidateCachedValues();
687 if (autogenerate_keyword_) {
688 keyword_.clear();
689 keyword_generated_ = false;
690 }
691 }
692
GetExtensionId() const693 std::string TemplateURL::GetExtensionId() const {
694 DCHECK(IsExtensionKeyword());
695 return GURL(url_.url()).host();
696 }
697
IsExtensionKeyword() const698 bool TemplateURL::IsExtensionKeyword() const {
699 return GURL(url_.url()).SchemeIs(chrome::kExtensionScheme);
700 }
701