• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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