template_url.cc 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818
  1. // Copyright 2014 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. #include "components/search_engines/template_url.h"
  5. #include <string>
  6. #include <tuple>
  7. #include <vector>
  8. #include "base/base64.h"
  9. #include "base/base64url.h"
  10. #include "base/check_op.h"
  11. #include "base/command_line.h"
  12. #include "base/containers/adapters.h"
  13. #include "base/containers/contains.h"
  14. #include "base/feature_list.h"
  15. #include "base/format_macros.h"
  16. #include "base/i18n/case_conversion.h"
  17. #include "base/i18n/icu_string_conversions.h"
  18. #include "base/i18n/rtl.h"
  19. #include "base/metrics/field_trial.h"
  20. #include "base/metrics/histogram_functions.h"
  21. #include "base/notreached.h"
  22. #include "base/strings/escape.h"
  23. #include "base/strings/string_number_conversions.h"
  24. #include "base/strings/string_piece.h"
  25. #include "base/strings/string_split.h"
  26. #include "base/strings/string_util.h"
  27. #include "base/strings/stringprintf.h"
  28. #include "base/strings/utf_string_conversions.h"
  29. #include "base/trace_event/memory_usage_estimator.h"
  30. #include "build/build_config.h"
  31. #include "components/google/core/common/google_util.h"
  32. #include "components/omnibox/common/omnibox_features.h"
  33. #include "components/search_engines/search_engine_utils.h"
  34. #include "components/search_engines/search_engines_switches.h"
  35. #include "components/search_engines/search_terms_data.h"
  36. #include "components/url_formatter/url_formatter.h"
  37. #include "google_apis/google_api_keys.h"
  38. #include "net/base/mime_util.h"
  39. #include "net/base/url_util.h"
  40. #include "template_url_starter_pack_data.h"
  41. #include "third_party/metrics_proto/omnibox_input_type.pb.h"
  42. #include "ui/base/device_form_factor.h"
  43. #include "url/gurl.h"
  44. namespace {
  45. // The TemplateURLRef has any number of terms that need to be replaced. Each of
  46. // the terms is enclosed in braces. If the character preceeding the final
  47. // brace is a ?, it indicates the term is optional and can be replaced with
  48. // an empty string.
  49. const char kStartParameter = '{';
  50. const char kEndParameter = '}';
  51. const char kOptional = '?';
  52. // Known parameters found in the URL.
  53. const char kSearchTermsParameter[] = "searchTerms";
  54. const char kSearchTermsParameterFull[] = "{searchTerms}";
  55. const char kSearchTermsParameterFullEscaped[] = "%7BsearchTerms%7D";
  56. // Same as kSearchTermsParameter, with no escaping.
  57. const char kGoogleUnescapedSearchTermsParameter[] =
  58. "google:unescapedSearchTerms";
  59. const char kGoogleUnescapedSearchTermsParameterFull[] =
  60. "{google:unescapedSearchTerms}";
  61. // Display value for kSearchTermsParameter.
  62. const char kDisplaySearchTerms[] = "%s";
  63. // Display value for kGoogleUnescapedSearchTermsParameter.
  64. const char kDisplayUnescapedSearchTerms[] = "%S";
  65. // Used if the count parameter is not optional. Indicates we want 10 search
  66. // results.
  67. const char kDefaultCount[] = "10";
  68. // Used if the output encoding parameter is required.
  69. const char kOutputEncodingType[] = "UTF-8";
  70. const size_t kMaxStringEncodeStringLength = 1'000'000;
  71. // Attempts to encode |terms| and |original_query| in |encoding| and escape
  72. // them. |terms| may be escaped as path or query depending on |is_in_query|;
  73. // |original_query| is always escaped as query. If |force_encode| is true
  74. // encoding ignores errors and function always returns true. Otherwise function
  75. // returns whether the encoding process succeeded.
  76. bool TryEncoding(const std::u16string& terms,
  77. const std::u16string& original_query,
  78. const char* encoding,
  79. bool is_in_query,
  80. bool force_encode,
  81. std::u16string* escaped_terms,
  82. std::u16string* escaped_original_query) {
  83. DCHECK(escaped_terms);
  84. DCHECK(escaped_original_query);
  85. // Both |base::UTF16ToCodepage()| and |net::Escape*()| invocations below
  86. // create strings longer than their inputs. To ensure doing so does not crash,
  87. // this truncates |terms| to |kMaxStringEncodeStringLength|.
  88. const std::u16string& truncated_terms =
  89. terms.size() > kMaxStringEncodeStringLength
  90. ? terms.substr(0, kMaxStringEncodeStringLength)
  91. : terms;
  92. base::OnStringConversionError::Type error_handling =
  93. force_encode ? base::OnStringConversionError::SKIP
  94. : base::OnStringConversionError::FAIL;
  95. std::string encoded_terms;
  96. if (!base::UTF16ToCodepage(truncated_terms, encoding, error_handling,
  97. &encoded_terms)) {
  98. return false;
  99. }
  100. *escaped_terms = base::UTF8ToUTF16(
  101. is_in_query ? base::EscapeQueryParamValue(encoded_terms, true)
  102. : base::EscapePath(encoded_terms));
  103. if (original_query.empty())
  104. return true;
  105. std::string encoded_original_query;
  106. if (!base::UTF16ToCodepage(original_query, encoding, error_handling,
  107. &encoded_original_query))
  108. return false;
  109. *escaped_original_query = base::UTF8ToUTF16(
  110. base::EscapeQueryParamValue(encoded_original_query, true));
  111. return true;
  112. }
  113. // Finds the position of the search terms' parameter in the URL component.
  114. class SearchTermLocation {
  115. public:
  116. SearchTermLocation(const base::StringPiece& url_component,
  117. url::Parsed::ComponentType url_component_type)
  118. : found_(false) {
  119. if (url_component_type == url::Parsed::PATH) {
  120. // GURL's constructor escapes "{" and "}" in the path of a passed string.
  121. found_ =
  122. TryMatchSearchParam(url_component, kSearchTermsParameterFullEscaped);
  123. } else {
  124. DCHECK((url_component_type == url::Parsed::QUERY) ||
  125. (url_component_type == url::Parsed::REF));
  126. url::Component query, key, value;
  127. query.len = static_cast<int>(url_component.size());
  128. while (url::ExtractQueryKeyValue(url_component.data(), &query, &key,
  129. &value)) {
  130. if (key.is_nonempty() && value.is_nonempty()) {
  131. const base::StringPiece value_string =
  132. url_component.substr(value.begin, value.len);
  133. if (TryMatchSearchParam(value_string, kSearchTermsParameterFull) ||
  134. TryMatchSearchParam(value_string,
  135. kGoogleUnescapedSearchTermsParameterFull)) {
  136. found_ = true;
  137. key_ = std::string(url_component.substr(key.begin, key.len));
  138. break;
  139. }
  140. }
  141. }
  142. }
  143. }
  144. SearchTermLocation(const SearchTermLocation&) = delete;
  145. SearchTermLocation& operator=(const SearchTermLocation&) = delete;
  146. bool found() const { return found_; }
  147. const std::string& key() const { return key_; }
  148. const std::string& value_prefix() const { return value_prefix_; }
  149. const std::string& value_suffix() const { return value_suffix_; }
  150. private:
  151. // Returns true if the search term placeholder is present, and also assigns
  152. // the constant prefix/suffix found.
  153. bool TryMatchSearchParam(const base::StringPiece& value,
  154. const base::StringPiece& pattern) {
  155. size_t pos = value.find(pattern);
  156. if (pos == base::StringPiece::npos)
  157. return false;
  158. value_prefix_ = std::string(value.substr(0, pos));
  159. value_suffix_ = std::string(value.substr(pos + pattern.size()));
  160. return true;
  161. }
  162. bool found_;
  163. std::string key_;
  164. std::string value_prefix_;
  165. std::string value_suffix_;
  166. };
  167. bool IsTemplateParameterString(const std::string& param) {
  168. return (param.length() > 2) && (*(param.begin()) == kStartParameter) &&
  169. (*(param.rbegin()) == kEndParameter);
  170. }
  171. std::string YandexSearchPathFromDeviceFormFactor() {
  172. switch (ui::GetDeviceFormFactor()) {
  173. case ui::DEVICE_FORM_FACTOR_DESKTOP:
  174. return "search/";
  175. case ui::DEVICE_FORM_FACTOR_PHONE:
  176. return "search/touch/";
  177. case ui::DEVICE_FORM_FACTOR_TABLET:
  178. return "search/pad/";
  179. default:
  180. NOTREACHED();
  181. return std::string();
  182. }
  183. }
  184. } // namespace
  185. // TemplateURLRef::SearchTermsArgs --------------------------------------------
  186. TemplateURLRef::SearchTermsArgs::SearchTermsArgs() = default;
  187. TemplateURLRef::SearchTermsArgs::SearchTermsArgs(
  188. const std::u16string& search_terms)
  189. : search_terms(search_terms) {}
  190. TemplateURLRef::SearchTermsArgs::SearchTermsArgs(const SearchTermsArgs& other) =
  191. default;
  192. TemplateURLRef::SearchTermsArgs::~SearchTermsArgs() {
  193. }
  194. size_t TemplateURLRef::SearchTermsArgs::EstimateMemoryUsage() const {
  195. size_t res = 0;
  196. res += base::trace_event::EstimateMemoryUsage(search_terms);
  197. res += base::trace_event::EstimateMemoryUsage(original_query);
  198. res += base::trace_event::EstimateMemoryUsage(assisted_query_stats);
  199. res += base::trace_event::EstimateMemoryUsage(current_page_url);
  200. res += base::trace_event::EstimateMemoryUsage(session_token);
  201. res += base::trace_event::EstimateMemoryUsage(prefetch_query);
  202. res += base::trace_event::EstimateMemoryUsage(prefetch_query_type);
  203. res += base::trace_event::EstimateMemoryUsage(additional_query_params);
  204. res += base::trace_event::EstimateMemoryUsage(image_thumbnail_content);
  205. res += base::trace_event::EstimateMemoryUsage(image_url);
  206. res += base::trace_event::EstimateMemoryUsage(contextual_search_params);
  207. return res;
  208. }
  209. TemplateURLRef::SearchTermsArgs::ContextualSearchParams::
  210. ContextualSearchParams() = default;
  211. TemplateURLRef::SearchTermsArgs::ContextualSearchParams::ContextualSearchParams(
  212. int version,
  213. int contextual_cards_version,
  214. std::string home_country,
  215. int64_t previous_event_id,
  216. int previous_event_results,
  217. bool is_exact_search,
  218. std::string source_lang,
  219. std::string target_lang,
  220. std::string fluent_languages,
  221. std::string related_searches_stamp)
  222. : version(version),
  223. contextual_cards_version(contextual_cards_version),
  224. home_country(home_country),
  225. previous_event_id(previous_event_id),
  226. previous_event_results(previous_event_results),
  227. is_exact_search(is_exact_search),
  228. source_lang(source_lang),
  229. target_lang(target_lang),
  230. fluent_languages(fluent_languages),
  231. related_searches_stamp(related_searches_stamp) {}
  232. TemplateURLRef::SearchTermsArgs::ContextualSearchParams::ContextualSearchParams(
  233. const ContextualSearchParams& other) = default;
  234. TemplateURLRef::SearchTermsArgs::ContextualSearchParams::
  235. ~ContextualSearchParams() {
  236. }
  237. size_t
  238. TemplateURLRef::SearchTermsArgs::ContextualSearchParams::EstimateMemoryUsage()
  239. const {
  240. return base::trace_event::EstimateMemoryUsage(home_country);
  241. }
  242. // TemplateURLRef -------------------------------------------------------------
  243. TemplateURLRef::TemplateURLRef(const TemplateURL* owner, Type type)
  244. : owner_(owner), type_(type) {
  245. DCHECK(owner_);
  246. DCHECK_NE(INDEXED, type_);
  247. }
  248. TemplateURLRef::TemplateURLRef(const TemplateURL* owner, size_t index_in_owner)
  249. : owner_(owner), type_(INDEXED), index_in_owner_(index_in_owner) {
  250. DCHECK(owner_);
  251. DCHECK_LT(index_in_owner_, owner_->alternate_urls().size());
  252. }
  253. TemplateURLRef::~TemplateURLRef() {
  254. }
  255. TemplateURLRef::TemplateURLRef(const TemplateURLRef& source) = default;
  256. TemplateURLRef& TemplateURLRef::operator=(const TemplateURLRef& source) =
  257. default;
  258. std::string TemplateURLRef::GetURL() const {
  259. switch (type_) {
  260. case SEARCH: return owner_->url();
  261. case SUGGEST: return owner_->suggestions_url();
  262. case IMAGE: return owner_->image_url();
  263. case NEW_TAB: return owner_->new_tab_url();
  264. case CONTEXTUAL_SEARCH: return owner_->contextual_search_url();
  265. case INDEXED: return owner_->alternate_urls()[index_in_owner_];
  266. default: NOTREACHED(); return std::string(); // NOLINT
  267. }
  268. }
  269. std::string TemplateURLRef::GetPostParamsString() const {
  270. switch (type_) {
  271. case INDEXED:
  272. case SEARCH: return owner_->search_url_post_params();
  273. case SUGGEST: return owner_->suggestions_url_post_params();
  274. case NEW_TAB: return std::string();
  275. case CONTEXTUAL_SEARCH: return std::string();
  276. case IMAGE: return owner_->image_url_post_params();
  277. default: NOTREACHED(); return std::string(); // NOLINT
  278. }
  279. }
  280. bool TemplateURLRef::UsesPOSTMethod(
  281. const SearchTermsData& search_terms_data) const {
  282. ParseIfNecessary(search_terms_data);
  283. return !post_params_.empty();
  284. }
  285. size_t TemplateURLRef::EstimateMemoryUsage() const {
  286. size_t res = 0;
  287. res += base::trace_event::EstimateMemoryUsage(parsed_url_);
  288. res += base::trace_event::EstimateMemoryUsage(replacements_);
  289. res += base::trace_event::EstimateMemoryUsage(host_);
  290. res += base::trace_event::EstimateMemoryUsage(port_);
  291. res += base::trace_event::EstimateMemoryUsage(path_prefix_);
  292. res += base::trace_event::EstimateMemoryUsage(path_suffix_);
  293. res += base::trace_event::EstimateMemoryUsage(search_term_key_);
  294. res += base::trace_event::EstimateMemoryUsage(search_term_value_prefix_);
  295. res += base::trace_event::EstimateMemoryUsage(search_term_value_suffix_);
  296. res += base::trace_event::EstimateMemoryUsage(post_params_);
  297. res += sizeof(path_wildcard_present_);
  298. return res;
  299. }
  300. size_t TemplateURLRef::PostParam::EstimateMemoryUsage() const {
  301. size_t res = 0;
  302. res += base::trace_event::EstimateMemoryUsage(name);
  303. res += base::trace_event::EstimateMemoryUsage(value);
  304. res += base::trace_event::EstimateMemoryUsage(content_type);
  305. return res;
  306. }
  307. bool TemplateURLRef::EncodeFormData(const PostParams& post_params,
  308. PostContent* post_content) const {
  309. if (post_params.empty())
  310. return true;
  311. if (!post_content)
  312. return false;
  313. const char kUploadDataMIMEType[] = "multipart/form-data; boundary=";
  314. // Each name/value pair is stored in a body part which is preceded by a
  315. // boundary delimiter line.
  316. std::string boundary = net::GenerateMimeMultipartBoundary();
  317. // Sets the content MIME type.
  318. post_content->first = kUploadDataMIMEType;
  319. post_content->first += boundary;
  320. // Encodes the post parameters.
  321. std::string* post_data = &post_content->second;
  322. post_data->clear();
  323. for (const auto& param : post_params) {
  324. DCHECK(!param.name.empty());
  325. net::AddMultipartValueForUpload(param.name, param.value, boundary,
  326. param.content_type, post_data);
  327. }
  328. net::AddMultipartFinalDelimiterForUpload(boundary, post_data);
  329. return true;
  330. }
  331. bool TemplateURLRef::SupportsReplacement(
  332. const SearchTermsData& search_terms_data) const {
  333. ParseIfNecessary(search_terms_data);
  334. return valid_ && supports_replacements_;
  335. }
  336. std::string TemplateURLRef::ReplaceSearchTerms(
  337. const SearchTermsArgs& search_terms_args,
  338. const SearchTermsData& search_terms_data,
  339. PostContent* post_content) const {
  340. ParseIfNecessary(search_terms_data);
  341. if (!valid_)
  342. return std::string();
  343. std::string url(HandleReplacements(search_terms_args, search_terms_data,
  344. post_content));
  345. GURL gurl(url);
  346. if (!gurl.is_valid())
  347. return url;
  348. std::vector<std::string> query_params;
  349. if (search_terms_args.append_extra_query_params_from_command_line) {
  350. std::string extra_params(
  351. base::CommandLine::ForCurrentProcess()->GetSwitchValueASCII(
  352. switches::kExtraSearchQueryParams));
  353. if (!extra_params.empty())
  354. query_params.push_back(extra_params);
  355. }
  356. if (!search_terms_args.additional_query_params.empty())
  357. query_params.push_back(search_terms_args.additional_query_params);
  358. if (!gurl.query().empty())
  359. query_params.push_back(gurl.query());
  360. if (owner_->created_from_play_api()) {
  361. // Append attribution parameter to query originating from Play API search
  362. // engine.
  363. query_params.push_back("chrome_dse_attribution=1");
  364. }
  365. if (query_params.empty())
  366. return url;
  367. GURL::Replacements replacements;
  368. std::string query_str = base::JoinString(query_params, "&");
  369. replacements.SetQueryStr(query_str);
  370. return gurl.ReplaceComponents(replacements).possibly_invalid_spec();
  371. }
  372. bool TemplateURLRef::IsValid(const SearchTermsData& search_terms_data) const {
  373. ParseIfNecessary(search_terms_data);
  374. return valid_;
  375. }
  376. std::u16string TemplateURLRef::DisplayURL(
  377. const SearchTermsData& search_terms_data) const {
  378. ParseIfNecessary(search_terms_data);
  379. std::string result(GetURL());
  380. if (valid_ && !replacements_.empty()) {
  381. base::ReplaceSubstringsAfterOffset(&result, 0,
  382. kSearchTermsParameterFull,
  383. kDisplaySearchTerms);
  384. base::ReplaceSubstringsAfterOffset(&result, 0,
  385. kGoogleUnescapedSearchTermsParameterFull,
  386. kDisplayUnescapedSearchTerms);
  387. }
  388. return base::UTF8ToUTF16(result);
  389. }
  390. // static
  391. std::string TemplateURLRef::DisplayURLToURLRef(
  392. const std::u16string& display_url) {
  393. std::string result = base::UTF16ToUTF8(display_url);
  394. base::ReplaceSubstringsAfterOffset(&result, 0,
  395. kDisplaySearchTerms,
  396. kSearchTermsParameterFull);
  397. base::ReplaceSubstringsAfterOffset(&result, 0,
  398. kDisplayUnescapedSearchTerms,
  399. kGoogleUnescapedSearchTermsParameterFull);
  400. return result;
  401. }
  402. const std::string& TemplateURLRef::GetHost(
  403. const SearchTermsData& search_terms_data) const {
  404. ParseIfNecessary(search_terms_data);
  405. return host_;
  406. }
  407. std::string TemplateURLRef::GetPath(
  408. const SearchTermsData& search_terms_data) const {
  409. ParseIfNecessary(search_terms_data);
  410. return path_prefix_ + path_suffix_;
  411. }
  412. const std::string& TemplateURLRef::GetSearchTermKey(
  413. const SearchTermsData& search_terms_data) const {
  414. ParseIfNecessary(search_terms_data);
  415. return search_term_key_;
  416. }
  417. url::Parsed::ComponentType TemplateURLRef::GetSearchTermKeyLocation(
  418. const SearchTermsData& search_terms_data) const {
  419. ParseIfNecessary(search_terms_data);
  420. return search_term_key_location_;
  421. }
  422. const std::string& TemplateURLRef::GetSearchTermValuePrefix(
  423. const SearchTermsData& search_terms_data) const {
  424. ParseIfNecessary(search_terms_data);
  425. return search_term_value_prefix_;
  426. }
  427. const std::string& TemplateURLRef::GetSearchTermValueSuffix(
  428. const SearchTermsData& search_terms_data) const {
  429. ParseIfNecessary(search_terms_data);
  430. return search_term_value_suffix_;
  431. }
  432. std::u16string TemplateURLRef::SearchTermToString16(
  433. const base::StringPiece& term) const {
  434. const std::vector<std::string>& encodings = owner_->input_encodings();
  435. std::u16string result;
  436. base::UnescapeRule::Type unescape_rules =
  437. base::UnescapeRule::SPACES | base::UnescapeRule::PATH_SEPARATORS |
  438. base::UnescapeRule::URL_SPECIAL_CHARS_EXCEPT_PATH_SEPARATORS;
  439. if (search_term_key_location_ != url::Parsed::PATH)
  440. unescape_rules |= base::UnescapeRule::REPLACE_PLUS_WITH_SPACE;
  441. std::string unescaped = base::UnescapeURLComponent(term, unescape_rules);
  442. for (size_t i = 0; i < encodings.size(); ++i) {
  443. if (base::CodepageToUTF16(unescaped, encodings[i].c_str(),
  444. base::OnStringConversionError::FAIL, &result))
  445. return result;
  446. }
  447. // Always fall back on UTF-8 if it works.
  448. if (base::CodepageToUTF16(unescaped, base::kCodepageUTF8,
  449. base::OnStringConversionError::FAIL, &result))
  450. return result;
  451. // When nothing worked, just use the escaped text. We have no idea what the
  452. // encoding is. We need to substitute spaces for pluses ourselves since we're
  453. // not sending it through an unescaper.
  454. result = base::UTF8ToUTF16(term);
  455. if (unescape_rules & base::UnescapeRule::REPLACE_PLUS_WITH_SPACE)
  456. std::replace(result.begin(), result.end(), '+', ' ');
  457. return result;
  458. }
  459. bool TemplateURLRef::HasGoogleBaseURLs(
  460. const SearchTermsData& search_terms_data) const {
  461. ParseIfNecessary(search_terms_data);
  462. return std::any_of(replacements_.begin(), replacements_.end(),
  463. [](const Replacement& replacement) {
  464. return replacement.type == GOOGLE_BASE_URL ||
  465. replacement.type == GOOGLE_BASE_SUGGEST_URL;
  466. });
  467. }
  468. bool TemplateURLRef::ExtractSearchTermsFromURL(
  469. const GURL& url,
  470. std::u16string* search_terms,
  471. const SearchTermsData& search_terms_data,
  472. url::Parsed::ComponentType* search_terms_component,
  473. url::Component* search_terms_position) const {
  474. DCHECK(search_terms);
  475. search_terms->clear();
  476. ParseIfNecessary(search_terms_data);
  477. // We need a search term in the template URL to extract something.
  478. if (search_term_key_.empty() &&
  479. (search_term_key_location_ != url::Parsed::PATH))
  480. return false;
  481. // Host, port, and path must match.
  482. if ((url.host() != host_) || (url.port() != port_) ||
  483. (!PathIsEqual(url) && (search_term_key_location_ != url::Parsed::PATH))) {
  484. return false;
  485. }
  486. base::StringPiece source;
  487. url::Component position;
  488. if (search_term_key_location_ == url::Parsed::PATH) {
  489. source = url.path_piece();
  490. // If the path does not contain the expected prefix and suffix, then this is
  491. // not a match.
  492. if (source.size() < (search_term_value_prefix_.size() +
  493. search_term_value_suffix_.size()) ||
  494. !base::StartsWith(source, search_term_value_prefix_) ||
  495. !base::EndsWith(source, search_term_value_suffix_))
  496. return false;
  497. position =
  498. url::MakeRange(search_term_value_prefix_.size(),
  499. source.length() - search_term_value_suffix_.size());
  500. } else {
  501. DCHECK(search_term_key_location_ == url::Parsed::QUERY ||
  502. search_term_key_location_ == url::Parsed::REF);
  503. source = (search_term_key_location_ == url::Parsed::QUERY)
  504. ? url.query_piece()
  505. : url.ref_piece();
  506. url::Component query, key, value;
  507. query.len = static_cast<int>(source.size());
  508. bool key_found = false;
  509. while (url::ExtractQueryKeyValue(source.data(), &query, &key, &value)) {
  510. if (key.is_nonempty()) {
  511. if (source.substr(key.begin, key.len) == search_term_key_) {
  512. // Fail if search term key is found twice.
  513. if (key_found)
  514. return false;
  515. // If the query parameter does not contain the expected prefix and
  516. // suffix, then this is not a match.
  517. base::StringPiece search_term =
  518. base::StringPiece(source).substr(value.begin, value.len);
  519. if (search_term.size() < (search_term_value_prefix_.size() +
  520. search_term_value_suffix_.size()) ||
  521. !base::StartsWith(search_term, search_term_value_prefix_) ||
  522. !base::EndsWith(search_term, search_term_value_suffix_))
  523. continue;
  524. key_found = true;
  525. position =
  526. url::MakeRange(value.begin + search_term_value_prefix_.size(),
  527. value.end() - search_term_value_suffix_.size());
  528. }
  529. }
  530. }
  531. if (!key_found)
  532. return false;
  533. }
  534. // Extract the search term.
  535. *search_terms =
  536. SearchTermToString16(source.substr(position.begin, position.len));
  537. if (search_terms_component)
  538. *search_terms_component = search_term_key_location_;
  539. if (search_terms_position)
  540. *search_terms_position = position;
  541. return true;
  542. }
  543. void TemplateURLRef::InvalidateCachedValues() const {
  544. supports_replacements_ = valid_ = parsed_ = path_wildcard_present_ = false;
  545. host_.clear();
  546. port_.clear();
  547. path_prefix_.clear();
  548. path_suffix_.clear();
  549. search_term_key_.clear();
  550. search_term_key_location_ = url::Parsed::QUERY;
  551. search_term_value_prefix_.clear();
  552. search_term_value_suffix_.clear();
  553. replacements_.clear();
  554. post_params_.clear();
  555. }
  556. bool TemplateURLRef::ParseParameter(size_t start,
  557. size_t end,
  558. std::string* url,
  559. Replacements* replacements) const {
  560. DCHECK(start != std::string::npos &&
  561. end != std::string::npos && end > start);
  562. size_t length = end - start - 1;
  563. bool optional = false;
  564. // Make a copy of |url| that can be referenced in StringPieces below. |url| is
  565. // modified, so that can't be used in StringPiece.
  566. const std::string original_url(*url);
  567. if (original_url[end - 1] == kOptional) {
  568. optional = true;
  569. length--;
  570. }
  571. const auto parameter =
  572. base::MakeStringPiece(original_url.begin() + start + 1,
  573. original_url.begin() + start + 1 + length);
  574. const auto full_parameter = base::MakeStringPiece(
  575. original_url.begin() + start, original_url.begin() + end + 1);
  576. // Remove the parameter from the string. For parameters who replacement is
  577. // constant and already known, just replace them directly. For other cases,
  578. // like parameters whose values may change over time, use |replacements|.
  579. url->erase(start, end - start + 1);
  580. if (parameter == kSearchTermsParameter) {
  581. replacements->push_back(Replacement(SEARCH_TERMS, start));
  582. } else if (parameter == "count") {
  583. if (!optional)
  584. url->insert(start, kDefaultCount);
  585. } else if (parameter == "google:assistedQueryStats") {
  586. replacements->push_back(Replacement(GOOGLE_ASSISTED_QUERY_STATS, start));
  587. } else if (parameter == "google:baseURL") {
  588. replacements->push_back(Replacement(GOOGLE_BASE_URL, start));
  589. } else if (parameter == "google:baseSearchByImageURL") {
  590. replacements->push_back(
  591. Replacement(GOOGLE_BASE_SEARCH_BY_IMAGE_URL, start));
  592. } else if (parameter == "google:baseSuggestURL") {
  593. replacements->push_back(Replacement(GOOGLE_BASE_SUGGEST_URL, start));
  594. } else if (parameter == "google:currentPageUrl") {
  595. replacements->push_back(Replacement(GOOGLE_CURRENT_PAGE_URL, start));
  596. } else if (parameter == "google:cursorPosition") {
  597. replacements->push_back(Replacement(GOOGLE_CURSOR_POSITION, start));
  598. } else if (parameter == "google:imageOriginalHeight") {
  599. replacements->push_back(
  600. Replacement(TemplateURLRef::GOOGLE_IMAGE_ORIGINAL_HEIGHT, start));
  601. } else if (parameter == "google:imageOriginalWidth") {
  602. replacements->push_back(
  603. Replacement(TemplateURLRef::GOOGLE_IMAGE_ORIGINAL_WIDTH, start));
  604. } else if (parameter == "google:imageSearchSource") {
  605. replacements->push_back(
  606. Replacement(TemplateURLRef::GOOGLE_IMAGE_SEARCH_SOURCE, start));
  607. } else if (parameter == "google:imageThumbnail") {
  608. replacements->push_back(
  609. Replacement(TemplateURLRef::GOOGLE_IMAGE_THUMBNAIL, start));
  610. } else if (parameter == "google:imageThumbnailBase64") {
  611. replacements->push_back(
  612. Replacement(TemplateURLRef::GOOGLE_IMAGE_THUMBNAIL_BASE64, start));
  613. } else if (parameter == "google:imageURL") {
  614. replacements->push_back(Replacement(TemplateURLRef::GOOGLE_IMAGE_URL,
  615. start));
  616. } else if (parameter == "google:inputType") {
  617. replacements->push_back(Replacement(TemplateURLRef::GOOGLE_INPUT_TYPE,
  618. start));
  619. } else if (parameter == "google:omniboxFocusType") {
  620. replacements->push_back(
  621. Replacement(TemplateURLRef::GOOGLE_OMNIBOX_FOCUS_TYPE, start));
  622. } else if (parameter == "google:iOSSearchLanguage") {
  623. replacements->push_back(Replacement(GOOGLE_IOS_SEARCH_LANGUAGE, start));
  624. } else if (parameter == "google:contextualSearchVersion") {
  625. replacements->push_back(
  626. Replacement(GOOGLE_CONTEXTUAL_SEARCH_VERSION, start));
  627. } else if (parameter == "google:contextualSearchContextData") {
  628. replacements->push_back(
  629. Replacement(GOOGLE_CONTEXTUAL_SEARCH_CONTEXT_DATA, start));
  630. } else if (parameter == "google:originalQueryForSuggestion") {
  631. replacements->push_back(Replacement(GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION,
  632. start));
  633. } else if (parameter == "google:pageClassification") {
  634. replacements->push_back(Replacement(GOOGLE_PAGE_CLASSIFICATION, start));
  635. } else if (parameter == "google:clientCacheTimeToLive") {
  636. replacements->push_back(
  637. Replacement(GOOGLE_CLIENT_CACHE_TIME_TO_LIVE, start));
  638. } else if (parameter == "google:pathWildcard") {
  639. // Do nothing, we just want the path wildcard removed from the URL.
  640. } else if (parameter == "google:prefetchQuery") {
  641. replacements->push_back(Replacement(GOOGLE_PREFETCH_QUERY, start));
  642. } else if (parameter == "google:prefetchSource") {
  643. replacements->push_back(Replacement(GOOGLE_PREFETCH_SOURCE, start));
  644. } else if (parameter == "google:RLZ") {
  645. replacements->push_back(Replacement(GOOGLE_RLZ, start));
  646. } else if (parameter == "google:searchboxStats") {
  647. replacements->push_back(Replacement(GOOGLE_SEARCHBOX_STATS, start));
  648. } else if (parameter == "google:searchClient") {
  649. replacements->push_back(Replacement(GOOGLE_SEARCH_CLIENT, start));
  650. } else if (parameter == "google:searchFieldtrialParameter") {
  651. replacements->push_back(Replacement(GOOGLE_SEARCH_FIELDTRIAL_GROUP, start));
  652. } else if (parameter == "google:searchVersion") {
  653. replacements->push_back(Replacement(GOOGLE_SEARCH_VERSION, start));
  654. } else if (parameter == "google:sessionToken") {
  655. replacements->push_back(Replacement(GOOGLE_SESSION_TOKEN, start));
  656. } else if (parameter == "google:sourceId") {
  657. #if BUILDFLAG(IS_ANDROID) || BUILDFLAG(IS_IOS)
  658. url->insert(start, "sourceid=chrome-mobile&");
  659. #else
  660. url->insert(start, "sourceid=chrome&");
  661. #endif
  662. } else if (parameter == "google:suggestAPIKeyParameter") {
  663. url->insert(start,
  664. base::EscapeQueryParamValue(google_apis::GetAPIKey(), false));
  665. } else if (parameter == "google:suggestClient") {
  666. replacements->push_back(Replacement(GOOGLE_SUGGEST_CLIENT, start));
  667. } else if (parameter == "google:suggestRid") {
  668. replacements->push_back(Replacement(GOOGLE_SUGGEST_REQUEST_ID, start));
  669. } else if (parameter == kGoogleUnescapedSearchTermsParameter) {
  670. replacements->push_back(Replacement(GOOGLE_UNESCAPED_SEARCH_TERMS, start));
  671. } else if (parameter == "yandex:referralID") {
  672. replacements->push_back(Replacement(YANDEX_REFERRAL_ID, start));
  673. } else if (parameter == "mailru:referralID") {
  674. replacements->push_back(Replacement(MAIL_RU_REFERRAL_ID, start));
  675. } else if (parameter == "yandex:searchPath") {
  676. url->insert(start, YandexSearchPathFromDeviceFormFactor());
  677. } else if (parameter == "inputEncoding") {
  678. replacements->push_back(Replacement(ENCODING, start));
  679. } else if (parameter == "language") {
  680. replacements->push_back(Replacement(LANGUAGE, start));
  681. } else if (parameter == "outputEncoding") {
  682. if (!optional)
  683. url->insert(start, kOutputEncodingType);
  684. } else if ((parameter == "startIndex") || (parameter == "startPage")) {
  685. // We don't support these.
  686. if (!optional)
  687. url->insert(start, "1");
  688. } else if (!prepopulated_) {
  689. // If it's a prepopulated URL, we know that it's safe to remove unknown
  690. // parameters, so just ignore this and return true below. Otherwise it could
  691. // be some garbage but can also be a javascript block. Put it back.
  692. url->insert(start, full_parameter.data(), full_parameter.size());
  693. return false;
  694. }
  695. return true;
  696. }
  697. std::string TemplateURLRef::ParseURL(const std::string& url,
  698. Replacements* replacements,
  699. PostParams* post_params,
  700. bool* valid) const {
  701. *valid = false;
  702. std::string parsed_url = url;
  703. for (size_t last = 0; last != std::string::npos; ) {
  704. last = parsed_url.find(kStartParameter, last);
  705. if (last != std::string::npos) {
  706. size_t template_end = parsed_url.find(kEndParameter, last);
  707. if (template_end != std::string::npos) {
  708. // Since we allow Javascript in the URL, {} pairs could be nested. Match
  709. // only leaf pairs with supported parameters.
  710. size_t next_template_start = parsed_url.find(kStartParameter, last + 1);
  711. if (next_template_start == std::string::npos ||
  712. next_template_start > template_end) {
  713. // If successful, ParseParameter erases from the string as such no
  714. // need to update |last|. If failed, move |last| to the end of pair.
  715. if (!ParseParameter(last, template_end, &parsed_url, replacements)) {
  716. // |template_end| + 1 may be beyond the end of the string.
  717. last = template_end;
  718. }
  719. } else {
  720. last = next_template_start;
  721. }
  722. } else {
  723. // Open brace without a closing brace, return.
  724. return std::string();
  725. }
  726. }
  727. }
  728. // Handles the post parameters.
  729. const std::string& post_params_string = GetPostParamsString();
  730. if (!post_params_string.empty()) {
  731. for (const base::StringPiece& cur : base::SplitStringPiece(
  732. post_params_string, ",",
  733. base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL)) {
  734. // The '=' delimiter is required and the name must be not empty.
  735. std::vector<std::string> parts = base::SplitString(
  736. cur, "=", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
  737. if ((parts.size() != 2U) || parts[0].empty())
  738. return std::string();
  739. std::string& value = parts[1];
  740. size_t replacements_size = replacements->size();
  741. if (IsTemplateParameterString(value))
  742. ParseParameter(0, value.length() - 1, &value, replacements);
  743. PostParam param = { parts[0], value };
  744. post_params->push_back(param);
  745. // If there was a replacement added, points its index to last added
  746. // PostParam.
  747. if (replacements->size() > replacements_size) {
  748. DCHECK_EQ(replacements_size + 1, replacements->size());
  749. Replacement* r = &replacements->back();
  750. r->is_post_param = true;
  751. r->index = post_params->size() - 1;
  752. }
  753. }
  754. DCHECK(!post_params->empty());
  755. }
  756. *valid = true;
  757. return parsed_url;
  758. }
  759. void TemplateURLRef::ParseIfNecessary(
  760. const SearchTermsData& search_terms_data) const {
  761. if (!parsed_) {
  762. InvalidateCachedValues();
  763. parsed_ = true;
  764. parsed_url_ = ParseURL(GetURL(), &replacements_, &post_params_, &valid_);
  765. supports_replacements_ = false;
  766. if (valid_) {
  767. bool has_only_one_search_term = false;
  768. for (Replacements::const_iterator i = replacements_.begin();
  769. i != replacements_.end(); ++i) {
  770. if ((i->type == SEARCH_TERMS) ||
  771. (i->type == GOOGLE_UNESCAPED_SEARCH_TERMS)) {
  772. if (has_only_one_search_term) {
  773. has_only_one_search_term = false;
  774. break;
  775. }
  776. has_only_one_search_term = true;
  777. supports_replacements_ = true;
  778. }
  779. }
  780. // Only parse the host/key if there is one search term. Technically there
  781. // could be more than one term, but it's uncommon; so we punt.
  782. if (has_only_one_search_term)
  783. ParseHostAndSearchTermKey(search_terms_data);
  784. }
  785. }
  786. }
  787. void TemplateURLRef::ParsePath(const std::string& path) const {
  788. // Wildcard string used when matching URLs.
  789. const std::string wildcard_escaped = "%7Bgoogle:pathWildcard%7D";
  790. // We only search for the escaped wildcard because we're only replacing it in
  791. // the path, and GURL's constructor escapes { and }.
  792. size_t wildcard_start = path.find(wildcard_escaped);
  793. path_wildcard_present_ = wildcard_start != std::string::npos;
  794. path_prefix_ = path.substr(0, wildcard_start);
  795. path_suffix_ = path_wildcard_present_
  796. ? path.substr(wildcard_start + wildcard_escaped.length())
  797. : std::string();
  798. }
  799. bool TemplateURLRef::PathIsEqual(const GURL& url) const {
  800. base::StringPiece path = url.path_piece();
  801. if (!path_wildcard_present_)
  802. return path == path_prefix_;
  803. return ((path.length() >= path_prefix_.length() + path_suffix_.length()) &&
  804. base::StartsWith(path, path_prefix_) &&
  805. base::EndsWith(path, path_suffix_));
  806. }
  807. void TemplateURLRef::ParseHostAndSearchTermKey(
  808. const SearchTermsData& search_terms_data) const {
  809. std::string url_string(GetURL());
  810. base::ReplaceSubstringsAfterOffset(
  811. &url_string, 0, "{google:baseURL}",
  812. search_terms_data.GoogleBaseURLValue());
  813. base::ReplaceSubstringsAfterOffset(
  814. &url_string, 0, "{google:baseSuggestURL}",
  815. search_terms_data.GoogleBaseSuggestURLValue());
  816. base::ReplaceSubstringsAfterOffset(&url_string, 0, "{yandex:searchPath}",
  817. YandexSearchPathFromDeviceFormFactor());
  818. GURL url(url_string);
  819. if (!url.is_valid())
  820. return;
  821. SearchTermLocation query_result(url.query_piece(), url::Parsed::QUERY);
  822. SearchTermLocation ref_result(url.ref_piece(), url::Parsed::REF);
  823. SearchTermLocation path_result(url.path_piece(), url::Parsed::PATH);
  824. const bool in_query = query_result.found();
  825. const bool in_ref = ref_result.found();
  826. const bool in_path = path_result.found();
  827. if (in_query ? (in_ref || in_path) : (in_ref == in_path))
  828. return; // No key or multiple keys found. We only handle having one key.
  829. host_ = url.host();
  830. port_ = url.port();
  831. if (in_query) {
  832. search_term_key_location_ = url::Parsed::QUERY;
  833. search_term_key_ = query_result.key();
  834. search_term_value_prefix_ = query_result.value_prefix();
  835. search_term_value_suffix_ = query_result.value_suffix();
  836. ParsePath(url.path());
  837. } else if (in_ref) {
  838. search_term_key_location_ = url::Parsed::REF;
  839. search_term_key_ = ref_result.key();
  840. search_term_value_prefix_ = ref_result.value_prefix();
  841. search_term_value_suffix_ = ref_result.value_suffix();
  842. ParsePath(url.path());
  843. } else {
  844. DCHECK(in_path);
  845. search_term_key_location_ = url::Parsed::PATH;
  846. search_term_value_prefix_ = path_result.value_prefix();
  847. search_term_value_suffix_ = path_result.value_suffix();
  848. }
  849. }
  850. void TemplateURLRef::HandleReplacement(const std::string& name,
  851. const std::string& value,
  852. const Replacement& replacement,
  853. std::string* url) const {
  854. size_t pos = replacement.index;
  855. if (replacement.is_post_param) {
  856. DCHECK_LT(pos, post_params_.size());
  857. DCHECK(!post_params_[pos].name.empty());
  858. post_params_[pos].value = value;
  859. } else {
  860. url->insert(pos, name.empty() ? value : (name + "=" + value + "&"));
  861. }
  862. }
  863. std::string TemplateURLRef::HandleReplacements(
  864. const SearchTermsArgs& search_terms_args,
  865. const SearchTermsData& search_terms_data,
  866. PostContent* post_content) const {
  867. if (replacements_.empty()) {
  868. if (!post_params_.empty())
  869. EncodeFormData(post_params_, post_content);
  870. return parsed_url_;
  871. }
  872. // Determine if the search terms are in the query or before. We're escaping
  873. // space as '+' in the former case and as '%20' in the latter case.
  874. bool is_in_query = true;
  875. auto search_terms = std::find_if(replacements_.begin(), replacements_.end(),
  876. [](const Replacement& replacement) {
  877. return replacement.type == SEARCH_TERMS;
  878. });
  879. if (search_terms != replacements_.end()) {
  880. std::u16string::size_type query_start = parsed_url_.find('?');
  881. is_in_query = query_start != std::u16string::npos &&
  882. (static_cast<std::u16string::size_type>(search_terms->index) >
  883. query_start);
  884. }
  885. std::string input_encoding;
  886. std::u16string encoded_terms;
  887. std::u16string encoded_original_query;
  888. owner_->EncodeSearchTerms(search_terms_args, is_in_query, &input_encoding,
  889. &encoded_terms, &encoded_original_query);
  890. std::string url = parsed_url_;
  891. // replacements_ is ordered in ascending order, as such we need to iterate
  892. // from the back.
  893. for (const Replacement& replacement : base::Reversed(replacements_)) {
  894. switch (replacement.type) {
  895. case ENCODING:
  896. HandleReplacement(std::string(), input_encoding, replacement, &url);
  897. break;
  898. case GOOGLE_CONTEXTUAL_SEARCH_VERSION:
  899. if (search_terms_args.contextual_search_params.version >= 0) {
  900. HandleReplacement(
  901. "ctxs",
  902. base::NumberToString(
  903. search_terms_args.contextual_search_params.version),
  904. replacement, &url);
  905. }
  906. break;
  907. case GOOGLE_CONTEXTUAL_SEARCH_CONTEXT_DATA: {
  908. DCHECK(!replacement.is_post_param);
  909. const SearchTermsArgs::ContextualSearchParams& params =
  910. search_terms_args.contextual_search_params;
  911. std::vector<std::string> args;
  912. if (params.contextual_cards_version > 0) {
  913. args.push_back("ctxsl_coca=" +
  914. base::NumberToString(params.contextual_cards_version));
  915. }
  916. if (!params.home_country.empty())
  917. args.push_back("ctxs_hc=" + params.home_country);
  918. if (params.previous_event_id != 0) {
  919. args.push_back("ctxsl_pid=" +
  920. base::NumberToString(params.previous_event_id));
  921. }
  922. if (params.previous_event_results != 0) {
  923. args.push_back("ctxsl_per=" +
  924. base::NumberToString(params.previous_event_results));
  925. }
  926. if (params.is_exact_search)
  927. args.push_back("ctxsl_exact=1");
  928. if (!params.source_lang.empty())
  929. args.push_back("tlitesl=" + params.source_lang);
  930. if (!params.target_lang.empty())
  931. args.push_back("tlitetl=" + params.target_lang);
  932. if (!params.fluent_languages.empty())
  933. args.push_back("ctxs_fls=" + params.fluent_languages);
  934. if (!params.related_searches_stamp.empty())
  935. args.push_back("ctxsl_rs=" + params.related_searches_stamp);
  936. HandleReplacement(std::string(), base::JoinString(args, "&"),
  937. replacement, &url);
  938. break;
  939. }
  940. case GOOGLE_ASSISTED_QUERY_STATS:
  941. DCHECK(!replacement.is_post_param);
  942. if (!search_terms_args.assisted_query_stats.empty()) {
  943. // Get the base URL without substituting AQS and gs_lcrp to avoid
  944. // infinite recursion and unwanted replacement respectively. We need
  945. // the URL to find out if it meets all AQS requirements (e.g. HTTPS
  946. // protocol check). See TemplateURLRef::SearchTermsArgs for more
  947. // details.
  948. SearchTermsArgs sanitized_search_terms_args(search_terms_args);
  949. sanitized_search_terms_args.assisted_query_stats.clear();
  950. // Clear the proto. Its empty state has a serialized size of zero.
  951. sanitized_search_terms_args.searchbox_stats.Clear();
  952. GURL base_url(ReplaceSearchTerms(sanitized_search_terms_args,
  953. search_terms_data, nullptr));
  954. if (base_url.SchemeIsCryptographic() &&
  955. base::FeatureList::IsEnabled(
  956. omnibox::kReportAssistedQueryStats)) {
  957. HandleReplacement("aqs", search_terms_args.assisted_query_stats,
  958. replacement, &url);
  959. base::UmaHistogramCounts1000(
  960. "Omnibox.AssistedQueryStats.Length",
  961. static_cast<int>(
  962. search_terms_args.assisted_query_stats.length()));
  963. }
  964. }
  965. break;
  966. case GOOGLE_SEARCHBOX_STATS: {
  967. DCHECK(!replacement.is_post_param);
  968. if (search_terms_args.searchbox_stats.ByteSizeLong() > 0) {
  969. // Get the base URL without substituting gs_lcrp and AQS to avoid
  970. // infinite recursion and unwanted replacement respectively. We need
  971. // the URL to find out if it meets all gs_lcrp requirements (e.g.
  972. // HTTPS protocol check). See TemplateURLRef::SearchTermsArgs for more
  973. // details.
  974. SearchTermsArgs sanitized_search_terms_args(search_terms_args);
  975. sanitized_search_terms_args.assisted_query_stats.clear();
  976. // Clear the proto. Its empty state has a serialized size of zero.
  977. sanitized_search_terms_args.searchbox_stats.Clear();
  978. GURL base_url(ReplaceSearchTerms(sanitized_search_terms_args,
  979. search_terms_data, nullptr));
  980. if (base_url.SchemeIsCryptographic() &&
  981. base::FeatureList::IsEnabled(omnibox::kReportSearchboxStats)) {
  982. std::string serialized_searchbox_stats;
  983. search_terms_args.searchbox_stats.SerializeToString(
  984. &serialized_searchbox_stats);
  985. if (!serialized_searchbox_stats.empty()) {
  986. std::string encoded_searchbox_stats;
  987. base::Base64UrlEncode(serialized_searchbox_stats,
  988. base::Base64UrlEncodePolicy::OMIT_PADDING,
  989. &encoded_searchbox_stats);
  990. HandleReplacement("gs_lcrp", encoded_searchbox_stats, replacement,
  991. &url);
  992. base::UmaHistogramCounts1000(
  993. "Omnibox.SearchboxStats.Length",
  994. static_cast<int>(encoded_searchbox_stats.length()));
  995. }
  996. }
  997. }
  998. break;
  999. }
  1000. case GOOGLE_BASE_URL:
  1001. DCHECK(!replacement.is_post_param);
  1002. HandleReplacement(std::string(), search_terms_data.GoogleBaseURLValue(),
  1003. replacement, &url);
  1004. break;
  1005. case GOOGLE_BASE_SEARCH_BY_IMAGE_URL:
  1006. DCHECK(!replacement.is_post_param);
  1007. HandleReplacement(std::string(),
  1008. search_terms_data.GoogleBaseSearchByImageURLValue(),
  1009. replacement, &url);
  1010. break;
  1011. case GOOGLE_BASE_SUGGEST_URL:
  1012. DCHECK(!replacement.is_post_param);
  1013. HandleReplacement(std::string(),
  1014. search_terms_data.GoogleBaseSuggestURLValue(),
  1015. replacement, &url);
  1016. break;
  1017. case GOOGLE_CURRENT_PAGE_URL:
  1018. DCHECK(!replacement.is_post_param);
  1019. if (!search_terms_args.current_page_url.empty()) {
  1020. const std::string& escaped_current_page_url =
  1021. base::EscapeQueryParamValue(search_terms_args.current_page_url,
  1022. true);
  1023. HandleReplacement("url", escaped_current_page_url, replacement, &url);
  1024. }
  1025. break;
  1026. case GOOGLE_CURSOR_POSITION:
  1027. DCHECK(!replacement.is_post_param);
  1028. if (search_terms_args.cursor_position != std::u16string::npos)
  1029. HandleReplacement(
  1030. "cp",
  1031. base::StringPrintf("%" PRIuS, search_terms_args.cursor_position),
  1032. replacement, &url);
  1033. break;
  1034. case GOOGLE_INPUT_TYPE:
  1035. DCHECK(!replacement.is_post_param);
  1036. HandleReplacement("oit",
  1037. base::NumberToString(search_terms_args.input_type),
  1038. replacement, &url);
  1039. break;
  1040. case GOOGLE_OMNIBOX_FOCUS_TYPE:
  1041. DCHECK(!replacement.is_post_param);
  1042. if (search_terms_args.focus_type != OmniboxFocusType::DEFAULT) {
  1043. HandleReplacement("oft",
  1044. base::NumberToString(
  1045. static_cast<int>(search_terms_args.focus_type)),
  1046. replacement, &url);
  1047. }
  1048. break;
  1049. case GOOGLE_ORIGINAL_QUERY_FOR_SUGGESTION:
  1050. DCHECK(!replacement.is_post_param);
  1051. if (search_terms_args.accepted_suggestion >= 0 ||
  1052. !search_terms_args.assisted_query_stats.empty()) {
  1053. HandleReplacement("oq", base::UTF16ToUTF8(encoded_original_query),
  1054. replacement, &url);
  1055. }
  1056. break;
  1057. case GOOGLE_PAGE_CLASSIFICATION:
  1058. if (search_terms_args.page_classification !=
  1059. metrics::OmniboxEventProto::INVALID_SPEC) {
  1060. HandleReplacement(
  1061. "pgcl",
  1062. base::NumberToString(search_terms_args.page_classification),
  1063. replacement, &url);
  1064. }
  1065. break;
  1066. case GOOGLE_CLIENT_CACHE_TIME_TO_LIVE:
  1067. if (search_terms_args.search_terms.size() == 0 &&
  1068. search_terms_args.zero_suggest_cache_duration_sec > 0) {
  1069. HandleReplacement(
  1070. "ccttl",
  1071. base::NumberToString(
  1072. search_terms_args.zero_suggest_cache_duration_sec),
  1073. replacement, &url);
  1074. }
  1075. break;
  1076. case GOOGLE_PREFETCH_QUERY: {
  1077. const std::string& query = search_terms_args.prefetch_query;
  1078. const std::string& type = search_terms_args.prefetch_query_type;
  1079. if (!query.empty() && !type.empty()) {
  1080. HandleReplacement(std::string(),
  1081. "pfq=" + query + "&qha=" + type + "&", replacement,
  1082. &url);
  1083. }
  1084. break;
  1085. }
  1086. case GOOGLE_PREFETCH_SOURCE: {
  1087. if (search_terms_args.is_prefetch) {
  1088. // Currently, Chrome only support "cs" for prefetches, but if new
  1089. // prefetch sources (outside of suggestions) are added, a new prefetch
  1090. // source value is needed. These should denote the source of the
  1091. // prefetch to allow the search server to treat the requests based on
  1092. // source. "cs" represents Chrome Suggestions as the source. Adding a
  1093. // new source should be supported by the Search engine.
  1094. HandleReplacement("pf", "cs", replacement, &url);
  1095. }
  1096. break;
  1097. }
  1098. case GOOGLE_RLZ: {
  1099. DCHECK(!replacement.is_post_param);
  1100. // On platforms that don't have RLZ, we still want this branch
  1101. // to happen so that we replace the RLZ template with the
  1102. // empty string. (If we don't handle this case, we hit a
  1103. // NOTREACHED below.)
  1104. std::u16string rlz_string = search_terms_data.GetRlzParameterValue(
  1105. search_terms_args.request_source == CROS_APP_LIST);
  1106. if (!rlz_string.empty()) {
  1107. HandleReplacement("rlz", base::UTF16ToUTF8(rlz_string), replacement,
  1108. &url);
  1109. }
  1110. break;
  1111. }
  1112. case GOOGLE_SEARCH_CLIENT: {
  1113. DCHECK(!replacement.is_post_param);
  1114. std::string client = search_terms_data.GetSearchClient();
  1115. if (!client.empty())
  1116. HandleReplacement("client", client, replacement, &url);
  1117. break;
  1118. }
  1119. case GOOGLE_SEARCH_FIELDTRIAL_GROUP:
  1120. // We are not currently running any fieldtrials that modulate the search
  1121. // url. If we do, then we'd have some conditional insert such as:
  1122. // url.insert(replacement.index, used_www ? "gcx=w&" : "gcx=c&");
  1123. break;
  1124. case GOOGLE_SEARCH_VERSION:
  1125. HandleReplacement("gs_rn", "42", replacement, &url);
  1126. break;
  1127. case GOOGLE_SESSION_TOKEN: {
  1128. std::string token = search_terms_args.session_token;
  1129. if (!token.empty())
  1130. HandleReplacement("psi", token, replacement, &url);
  1131. break;
  1132. }
  1133. case GOOGLE_SUGGEST_CLIENT:
  1134. HandleReplacement(
  1135. std::string(),
  1136. search_terms_data.GetSuggestClient(
  1137. search_terms_args.request_source == NON_SEARCHBOX_NTP),
  1138. replacement, &url);
  1139. break;
  1140. case GOOGLE_SUGGEST_REQUEST_ID:
  1141. HandleReplacement(
  1142. std::string(),
  1143. search_terms_data.GetSuggestRequestIdentifier(
  1144. search_terms_args.request_source == NON_SEARCHBOX_NTP),
  1145. replacement, &url);
  1146. break;
  1147. case GOOGLE_UNESCAPED_SEARCH_TERMS: {
  1148. std::string unescaped_terms;
  1149. base::UTF16ToCodepage(search_terms_args.search_terms,
  1150. input_encoding.c_str(),
  1151. base::OnStringConversionError::SKIP,
  1152. &unescaped_terms);
  1153. HandleReplacement(std::string(), unescaped_terms, replacement, &url);
  1154. break;
  1155. }
  1156. case LANGUAGE:
  1157. HandleReplacement(std::string(),
  1158. search_terms_data.GetApplicationLocale(), replacement,
  1159. &url);
  1160. break;
  1161. case SEARCH_TERMS:
  1162. HandleReplacement(std::string(), base::UTF16ToUTF8(encoded_terms),
  1163. replacement, &url);
  1164. break;
  1165. case GOOGLE_IMAGE_THUMBNAIL:
  1166. HandleReplacement(std::string(),
  1167. search_terms_args.image_thumbnail_content,
  1168. replacement, &url);
  1169. if (replacement.is_post_param)
  1170. post_params_[replacement.index].content_type = "image/jpeg";
  1171. break;
  1172. case GOOGLE_IMAGE_THUMBNAIL_BASE64: {
  1173. std::string base64_thumbnail_content;
  1174. base::Base64Encode(search_terms_args.image_thumbnail_content,
  1175. &base64_thumbnail_content);
  1176. HandleReplacement(std::string(), base64_thumbnail_content, replacement,
  1177. &url);
  1178. if (replacement.is_post_param)
  1179. post_params_[replacement.index].content_type = "image/jpeg";
  1180. break;
  1181. }
  1182. case GOOGLE_IMAGE_URL:
  1183. if (search_terms_args.image_url.is_valid()) {
  1184. HandleReplacement(std::string(), search_terms_args.image_url.spec(),
  1185. replacement, &url);
  1186. }
  1187. break;
  1188. case GOOGLE_IMAGE_ORIGINAL_WIDTH:
  1189. if (!search_terms_args.image_original_size.IsEmpty()) {
  1190. HandleReplacement(std::string(),
  1191. base::NumberToString(
  1192. search_terms_args.image_original_size.width()),
  1193. replacement, &url);
  1194. }
  1195. break;
  1196. case GOOGLE_IMAGE_ORIGINAL_HEIGHT:
  1197. if (!search_terms_args.image_original_size.IsEmpty()) {
  1198. HandleReplacement(std::string(),
  1199. base::NumberToString(
  1200. search_terms_args.image_original_size.height()),
  1201. replacement, &url);
  1202. }
  1203. break;
  1204. case GOOGLE_IMAGE_SEARCH_SOURCE:
  1205. HandleReplacement(std::string(),
  1206. search_terms_data.GoogleImageSearchSource(),
  1207. replacement, &url);
  1208. break;
  1209. case GOOGLE_IOS_SEARCH_LANGUAGE:
  1210. #if BUILDFLAG(IS_IOS)
  1211. HandleReplacement("hl", search_terms_data.GetApplicationLocale(),
  1212. replacement, &url);
  1213. #endif
  1214. break;
  1215. case YANDEX_REFERRAL_ID: {
  1216. std::string referral_id = search_terms_data.GetYandexReferralID();
  1217. if (!referral_id.empty())
  1218. HandleReplacement("clid", referral_id, replacement, &url);
  1219. break;
  1220. }
  1221. case MAIL_RU_REFERRAL_ID: {
  1222. std::string referral_id = search_terms_data.GetMailRUReferralID();
  1223. if (!referral_id.empty())
  1224. HandleReplacement("gp", referral_id, replacement, &url);
  1225. break;
  1226. }
  1227. default:
  1228. NOTREACHED();
  1229. break;
  1230. }
  1231. }
  1232. if (!post_params_.empty())
  1233. EncodeFormData(post_params_, post_content);
  1234. return url;
  1235. }
  1236. // TemplateURL ----------------------------------------------------------------
  1237. TemplateURL::AssociatedExtensionInfo::AssociatedExtensionInfo(
  1238. const std::string& extension_id,
  1239. base::Time install_time,
  1240. bool wants_to_be_default_engine)
  1241. : extension_id(extension_id),
  1242. install_time(install_time),
  1243. wants_to_be_default_engine(wants_to_be_default_engine) {}
  1244. TemplateURL::AssociatedExtensionInfo::~AssociatedExtensionInfo() {
  1245. }
  1246. size_t TemplateURL::AssociatedExtensionInfo::EstimateMemoryUsage() const {
  1247. return base::trace_event::EstimateMemoryUsage(extension_id);
  1248. }
  1249. TemplateURL::TemplateURL(const TemplateURLData& data, Type type)
  1250. : data_(data),
  1251. suggestions_url_ref_(this, TemplateURLRef::SUGGEST),
  1252. image_url_ref_(this, TemplateURLRef::IMAGE),
  1253. new_tab_url_ref_(this, TemplateURLRef::NEW_TAB),
  1254. contextual_search_url_ref_(this, TemplateURLRef::CONTEXTUAL_SEARCH),
  1255. type_(type),
  1256. engine_type_(SEARCH_ENGINE_UNKNOWN) {
  1257. ResizeURLRefVector();
  1258. SetPrepopulateId(data_.prepopulate_id);
  1259. }
  1260. TemplateURL::TemplateURL(const TemplateURLData& data,
  1261. Type type,
  1262. std::string extension_id,
  1263. base::Time install_time,
  1264. bool wants_to_be_default_engine)
  1265. : TemplateURL(data, type) {
  1266. DCHECK(type == NORMAL_CONTROLLED_BY_EXTENSION ||
  1267. type == OMNIBOX_API_EXTENSION);
  1268. // Omnibox keywords may not be set as default.
  1269. DCHECK(!wants_to_be_default_engine || type != OMNIBOX_API_EXTENSION) << type;
  1270. DCHECK_EQ(kInvalidTemplateURLID, data.id);
  1271. extension_info_ = std::make_unique<AssociatedExtensionInfo>(
  1272. extension_id, install_time, wants_to_be_default_engine);
  1273. }
  1274. TemplateURL::~TemplateURL() {
  1275. }
  1276. bool TemplateURL::IsBetterThanEngineWithConflictingKeyword(
  1277. const TemplateURL* other) const {
  1278. DCHECK(other);
  1279. auto get_sort_key = [](const TemplateURL* engine) {
  1280. return std::make_tuple(
  1281. // Policy-created engines always win over non-policy created engines.
  1282. engine->created_by_policy(),
  1283. // The integral value of the type enum is used to sort next.
  1284. // This makes extension-controlled engines win.
  1285. engine->type(),
  1286. // For engines with associated extensions; more recently installed
  1287. // extensions win.
  1288. engine->extension_info_ ? engine->extension_info_->install_time
  1289. : base::Time(),
  1290. // Prefer engines that CANNOT be auto-replaced.
  1291. !engine->safe_for_autoreplace(),
  1292. // Prefer engines created by Play API.
  1293. engine->created_from_play_api(),
  1294. // Favor prepopulated engines over other auto-generated engines.
  1295. engine->prepopulate_id() > 0,
  1296. // Favor starter pack engines over other auto-generated engines.
  1297. engine->starter_pack_id() > 0,
  1298. // Favor engines derived from OpenSearch descriptions over
  1299. // autogenerated engines heuristically generated from searchable forms.
  1300. engine->originating_url().is_valid(),
  1301. // More recently modified engines or created engines win.
  1302. engine->last_modified(), engine->date_created(),
  1303. // TODO(tommycli): This should be a tie-breaker than provides a total
  1304. // ordering of all TemplateURLs so that distributed clients resolve
  1305. // conflicts identically. This sync_guid is not globally unique today,
  1306. // so we need to fix that before we can resolve conflicts with this.
  1307. engine->sync_guid());
  1308. };
  1309. // Although normally sort is done by operator<, in this case, we want the
  1310. // BETTER engine to be preceding the worse engine.
  1311. return get_sort_key(this) > get_sort_key(other);
  1312. }
  1313. // static
  1314. std::u16string TemplateURL::GenerateKeyword(const GURL& url) {
  1315. DCHECK(url.is_valid());
  1316. // Strip "www." off the front of the keyword; otherwise the keyword won't work
  1317. // properly. See http://code.google.com/p/chromium/issues/detail?id=6984 .
  1318. // |url|'s hostname may be IDN-encoded. Before generating |keyword| from it,
  1319. // convert to Unicode, so it won't look like a confusing punycode string.
  1320. std::u16string keyword =
  1321. url_formatter::IDNToUnicode(url_formatter::StripWWW(url.host()));
  1322. return base::i18n::ToLower(keyword);
  1323. }
  1324. // static
  1325. GURL TemplateURL::GenerateFaviconURL(const GURL& url) {
  1326. DCHECK(url.is_valid());
  1327. GURL::Replacements rep;
  1328. static const char kFaviconPath[] = "/favicon.ico";
  1329. rep.SetPathStr(kFaviconPath);
  1330. rep.ClearUsername();
  1331. rep.ClearPassword();
  1332. rep.ClearQuery();
  1333. rep.ClearRef();
  1334. return url.ReplaceComponents(rep);
  1335. }
  1336. // static
  1337. bool TemplateURL::MatchesData(const TemplateURL* t_url,
  1338. const TemplateURLData* data,
  1339. const SearchTermsData& search_terms_data) {
  1340. if (!t_url || !data)
  1341. return !t_url && !data;
  1342. return (t_url->short_name() == data->short_name()) &&
  1343. t_url->HasSameKeywordAs(*data, search_terms_data) &&
  1344. (t_url->url() == data->url()) &&
  1345. (t_url->suggestions_url() == data->suggestions_url) &&
  1346. (t_url->image_url() == data->image_url) &&
  1347. (t_url->new_tab_url() == data->new_tab_url) &&
  1348. (t_url->search_url_post_params() == data->search_url_post_params) &&
  1349. (t_url->suggestions_url_post_params() ==
  1350. data->suggestions_url_post_params) &&
  1351. (t_url->image_url_post_params() == data->image_url_post_params) &&
  1352. (t_url->safe_for_autoreplace() == data->safe_for_autoreplace) &&
  1353. (t_url->input_encodings() == data->input_encodings) &&
  1354. (t_url->alternate_urls() == data->alternate_urls);
  1355. }
  1356. std::u16string TemplateURL::AdjustedShortNameForLocaleDirection() const {
  1357. std::u16string bidi_safe_short_name = data_.short_name();
  1358. base::i18n::AdjustStringForLocaleDirection(&bidi_safe_short_name);
  1359. return bidi_safe_short_name;
  1360. }
  1361. bool TemplateURL::SupportsReplacement(
  1362. const SearchTermsData& search_terms_data) const {
  1363. return url_ref().SupportsReplacement(search_terms_data);
  1364. }
  1365. bool TemplateURL::HasGoogleBaseURLs(
  1366. const SearchTermsData& search_terms_data) const {
  1367. if (std::any_of(url_refs_.begin(), url_refs_.end(),
  1368. [&](const TemplateURLRef& ref) {
  1369. return ref.HasGoogleBaseURLs(search_terms_data);
  1370. }))
  1371. return true;
  1372. return suggestions_url_ref_.HasGoogleBaseURLs(search_terms_data) ||
  1373. image_url_ref_.HasGoogleBaseURLs(search_terms_data) ||
  1374. new_tab_url_ref_.HasGoogleBaseURLs(search_terms_data) ||
  1375. contextual_search_url_ref_.HasGoogleBaseURLs(search_terms_data);
  1376. }
  1377. bool TemplateURL::IsGoogleSearchURLWithReplaceableKeyword(
  1378. const SearchTermsData& search_terms_data) const {
  1379. return (type_ == NORMAL) && url_ref().HasGoogleBaseURLs(search_terms_data) &&
  1380. google_util::IsGoogleHostname(base::UTF16ToUTF8(data_.keyword()),
  1381. google_util::DISALLOW_SUBDOMAIN);
  1382. }
  1383. bool TemplateURL::HasSameKeywordAs(
  1384. const TemplateURLData& other,
  1385. const SearchTermsData& search_terms_data) const {
  1386. return (data_.keyword() == other.keyword()) ||
  1387. (IsGoogleSearchURLWithReplaceableKeyword(search_terms_data) &&
  1388. TemplateURL(other).IsGoogleSearchURLWithReplaceableKeyword(
  1389. search_terms_data));
  1390. }
  1391. std::string TemplateURL::GetExtensionId() const {
  1392. DCHECK(extension_info_);
  1393. return extension_info_->extension_id;
  1394. }
  1395. SearchEngineType TemplateURL::GetEngineType(
  1396. const SearchTermsData& search_terms_data) const {
  1397. if (engine_type_ == SEARCH_ENGINE_UNKNOWN) {
  1398. const GURL url = GenerateSearchURL(search_terms_data);
  1399. engine_type_ = url.is_valid() ? SearchEngineUtils::GetEngineType(url)
  1400. : SEARCH_ENGINE_OTHER;
  1401. DCHECK_NE(SEARCH_ENGINE_UNKNOWN, engine_type_);
  1402. }
  1403. return engine_type_;
  1404. }
  1405. BuiltinEngineType TemplateURL::GetBuiltinEngineType() const {
  1406. if (data_.prepopulate_id != 0) {
  1407. return KEYWORD_MODE_PREPOPULATED_ENGINE;
  1408. } else if (data_.starter_pack_id != 0) {
  1409. switch (data_.starter_pack_id) {
  1410. case TemplateURLStarterPackData::kBookmarks:
  1411. return KEYWORD_MODE_STARTER_PACK_BOOKMARKS;
  1412. case TemplateURLStarterPackData::kHistory:
  1413. return KEYWORD_MODE_STARTER_PACK_HISTORY;
  1414. case TemplateURLStarterPackData::kTabs:
  1415. return KEYWORD_MODE_STARTER_PACK_TABS;
  1416. default:
  1417. NOTREACHED();
  1418. return KEYWORD_MODE_NON_BUILT_IN;
  1419. }
  1420. } else {
  1421. return KEYWORD_MODE_NON_BUILT_IN;
  1422. }
  1423. }
  1424. bool TemplateURL::ExtractSearchTermsFromURL(
  1425. const GURL& url,
  1426. const SearchTermsData& search_terms_data,
  1427. std::u16string* search_terms) const {
  1428. return FindSearchTermsInURL(url, search_terms_data, search_terms, nullptr,
  1429. nullptr);
  1430. }
  1431. bool TemplateURL::IsSearchURL(const GURL& url,
  1432. const SearchTermsData& search_terms_data) const {
  1433. std::u16string search_terms;
  1434. return ExtractSearchTermsFromURL(url, search_terms_data, &search_terms) &&
  1435. !search_terms.empty();
  1436. }
  1437. bool TemplateURL::ReplaceSearchTermsInURL(
  1438. const GURL& url,
  1439. const TemplateURLRef::SearchTermsArgs& search_terms_args,
  1440. const SearchTermsData& search_terms_data,
  1441. GURL* result) const {
  1442. // TODO(beaudoin): Use AQS from |search_terms_args| too.
  1443. url::Parsed::ComponentType search_term_component;
  1444. url::Component search_terms_position;
  1445. std::u16string search_terms;
  1446. if (!FindSearchTermsInURL(url, search_terms_data, &search_terms,
  1447. &search_term_component, &search_terms_position)) {
  1448. return false;
  1449. }
  1450. DCHECK(search_terms_position.is_nonempty());
  1451. // Query and ref are encoded in the same way.
  1452. const bool is_in_query = (search_term_component != url::Parsed::PATH);
  1453. std::string input_encoding;
  1454. std::u16string encoded_terms;
  1455. std::u16string encoded_original_query;
  1456. EncodeSearchTerms(search_terms_args, is_in_query, &input_encoding,
  1457. &encoded_terms, &encoded_original_query);
  1458. std::string old_params;
  1459. if (search_term_component == url::Parsed::QUERY) {
  1460. old_params = url.query();
  1461. } else if (search_term_component == url::Parsed::REF) {
  1462. old_params = url.ref();
  1463. } else {
  1464. DCHECK_EQ(search_term_component, url::Parsed::PATH);
  1465. old_params = url.path();
  1466. }
  1467. std::string new_params(old_params, 0, search_terms_position.begin);
  1468. new_params += base::UTF16ToUTF8(encoded_terms);
  1469. new_params += old_params.substr(search_terms_position.end());
  1470. GURL::Replacements replacements;
  1471. if (search_term_component == url::Parsed::QUERY) {
  1472. replacements.SetQueryStr(new_params);
  1473. } else if (search_term_component == url::Parsed::REF) {
  1474. replacements.SetRefStr(new_params);
  1475. } else {
  1476. DCHECK_EQ(search_term_component, url::Parsed::PATH);
  1477. replacements.SetPathStr(new_params);
  1478. }
  1479. *result = url.ReplaceComponents(replacements);
  1480. return true;
  1481. }
  1482. void TemplateURL::EncodeSearchTerms(
  1483. const TemplateURLRef::SearchTermsArgs& search_terms_args,
  1484. bool is_in_query,
  1485. std::string* input_encoding,
  1486. std::u16string* encoded_terms,
  1487. std::u16string* encoded_original_query) const {
  1488. std::vector<std::string> encodings(input_encodings());
  1489. if (!base::Contains(encodings, "UTF-8"))
  1490. encodings.push_back("UTF-8");
  1491. for (auto i = encodings.begin(); i != encodings.end(); ++i) {
  1492. if (TryEncoding(search_terms_args.search_terms,
  1493. search_terms_args.original_query, i->c_str(), is_in_query,
  1494. std::next(i) == encodings.end(), encoded_terms,
  1495. encoded_original_query)) {
  1496. *input_encoding = *i;
  1497. return;
  1498. }
  1499. }
  1500. NOTREACHED();
  1501. }
  1502. GURL TemplateURL::GenerateSearchURL(
  1503. const SearchTermsData& search_terms_data) const {
  1504. if (!url_ref().IsValid(search_terms_data))
  1505. return GURL();
  1506. if (!url_ref().SupportsReplacement(search_terms_data))
  1507. return GURL(url());
  1508. // Use something obscure for the search terms argument so that in the rare
  1509. // case the term replaces the URL it's unlikely another keyword would have the
  1510. // same url.
  1511. // TODO(jnd): Add additional parameters to get post data when the search URL
  1512. // has post parameters.
  1513. return GURL(url_ref().ReplaceSearchTerms(
  1514. TemplateURLRef::SearchTermsArgs(u"blah.blah.blah.blah.blah"),
  1515. search_terms_data, nullptr));
  1516. }
  1517. bool TemplateURL::IsSideSearchSupported() const {
  1518. return !side_search_param().empty();
  1519. }
  1520. GURL TemplateURL::GenerateSideSearchURL(
  1521. const GURL& search_url,
  1522. const std::string& version,
  1523. const SearchTermsData& search_terms_data) const {
  1524. DCHECK(IsSideSearchSupported());
  1525. DCHECK(IsSearchURL(search_url, search_terms_data));
  1526. return net::AppendQueryParameter(search_url, side_search_param(), version);
  1527. }
  1528. void TemplateURL::CopyFrom(const TemplateURL& other) {
  1529. if (this == &other)
  1530. return;
  1531. data_ = other.data_;
  1532. ResizeURLRefVector();
  1533. InvalidateCachedValues();
  1534. SetPrepopulateId(other.data_.prepopulate_id);
  1535. }
  1536. void TemplateURL::SetURL(const std::string& url) {
  1537. data_.SetURL(url);
  1538. engine_type_ = SEARCH_ENGINE_UNKNOWN;
  1539. url_ref().InvalidateCachedValues();
  1540. }
  1541. void TemplateURL::SetPrepopulateId(int id) {
  1542. data_.prepopulate_id = id;
  1543. const bool prepopulated = id > 0;
  1544. for (TemplateURLRef& ref : url_refs_)
  1545. ref.prepopulated_ = prepopulated;
  1546. suggestions_url_ref_.prepopulated_ = prepopulated;
  1547. image_url_ref_.prepopulated_ = prepopulated;
  1548. new_tab_url_ref_.prepopulated_ = prepopulated;
  1549. contextual_search_url_ref_.prepopulated_ = prepopulated;
  1550. }
  1551. void TemplateURL::ResetKeywordIfNecessary(
  1552. const SearchTermsData& search_terms_data,
  1553. bool force) {
  1554. if (IsGoogleSearchURLWithReplaceableKeyword(search_terms_data) || force) {
  1555. DCHECK_NE(OMNIBOX_API_EXTENSION, type_);
  1556. GURL url(GenerateSearchURL(search_terms_data));
  1557. if (url.is_valid())
  1558. data_.SetKeyword(GenerateKeyword(url));
  1559. }
  1560. }
  1561. void TemplateURL::InvalidateCachedValues() const {
  1562. for (const TemplateURLRef& ref : url_refs_)
  1563. ref.InvalidateCachedValues();
  1564. suggestions_url_ref_.InvalidateCachedValues();
  1565. image_url_ref_.InvalidateCachedValues();
  1566. new_tab_url_ref_.InvalidateCachedValues();
  1567. contextual_search_url_ref_.InvalidateCachedValues();
  1568. }
  1569. size_t TemplateURL::EstimateMemoryUsage() const {
  1570. size_t res = 0;
  1571. res += base::trace_event::EstimateMemoryUsage(data_);
  1572. res += base::trace_event::EstimateMemoryUsage(url_refs_);
  1573. res += base::trace_event::EstimateMemoryUsage(suggestions_url_ref_);
  1574. res += base::trace_event::EstimateMemoryUsage(image_url_ref_);
  1575. res += base::trace_event::EstimateMemoryUsage(new_tab_url_ref_);
  1576. res += base::trace_event::EstimateMemoryUsage(contextual_search_url_ref_);
  1577. res += base::trace_event::EstimateMemoryUsage(extension_info_);
  1578. return res;
  1579. }
  1580. void TemplateURL::ResizeURLRefVector() {
  1581. const size_t new_size = data_.alternate_urls.size() + 1;
  1582. if (url_refs_.size() == new_size)
  1583. return;
  1584. url_refs_.clear();
  1585. url_refs_.reserve(new_size);
  1586. for (size_t i = 0; i != data_.alternate_urls.size(); ++i)
  1587. url_refs_.emplace_back(this, i);
  1588. url_refs_.emplace_back(this, TemplateURLRef::SEARCH);
  1589. }
  1590. bool TemplateURL::FindSearchTermsInURL(
  1591. const GURL& url,
  1592. const SearchTermsData& search_terms_data,
  1593. std::u16string* search_terms,
  1594. url::Parsed::ComponentType* search_term_component,
  1595. url::Component* search_terms_position) const {
  1596. DCHECK(search_terms);
  1597. search_terms->clear();
  1598. // Try to match with every pattern.
  1599. for (const TemplateURLRef& ref : url_refs_) {
  1600. if (ref.ExtractSearchTermsFromURL(url, search_terms, search_terms_data,
  1601. search_term_component, search_terms_position)) {
  1602. // If ExtractSearchTermsFromURL() returns true and |search_terms| is empty
  1603. // it means the pattern matched but no search terms were present. In this
  1604. // case we fail immediately without looking for matches in subsequent
  1605. // patterns. This means that given patterns
  1606. // [ "http://foo/#q={searchTerms}", "http://foo/?q={searchTerms}" ],
  1607. // calling ExtractSearchTermsFromURL() on "http://foo/?q=bar#q=' would
  1608. // return false. This is important for at least Google, where such URLs
  1609. // are invalid.
  1610. return !search_terms->empty();
  1611. }
  1612. }
  1613. return false;
  1614. }