• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2012 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/search.h"
6 
7 #include "base/command_line.h"
8 #include "base/metrics/field_trial.h"
9 #include "base/metrics/histogram.h"
10 #include "base/prefs/pref_service.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/utf_string_conversions.h"
14 #include "chrome/browser/browser_process.h"
15 #include "chrome/browser/profiles/profile.h"
16 #include "chrome/browser/profiles/profile_manager.h"
17 #include "chrome/browser/search/instant_service.h"
18 #include "chrome/browser/search/instant_service_factory.h"
19 #include "chrome/browser/search_engines/template_url_service.h"
20 #include "chrome/browser/search_engines/template_url_service_factory.h"
21 #include "chrome/browser/search_engines/ui_thread_search_terms_data.h"
22 #include "chrome/browser/ui/browser.h"
23 #include "chrome/browser/ui/browser_instant_controller.h"
24 #include "chrome/browser/ui/browser_iterator.h"
25 #include "chrome/browser/ui/search/instant_search_prerenderer.h"
26 #include "chrome/common/chrome_switches.h"
27 #include "chrome/common/pref_names.h"
28 #include "chrome/common/search_urls.h"
29 #include "chrome/common/url_constants.h"
30 #include "components/google/core/browser/google_util.h"
31 #include "components/pref_registry/pref_registry_syncable.h"
32 #include "components/sessions/serialized_navigation_entry.h"
33 #include "content/public/browser/navigation_entry.h"
34 #include "content/public/browser/render_process_host.h"
35 #include "content/public/browser/web_contents.h"
36 #include "grit/generated_resources.h"
37 #include "ui/base/l10n/l10n_util.h"
38 
39 #if defined(ENABLE_MANAGED_USERS)
40 #include "chrome/browser/supervised_user/supervised_user_service.h"
41 #include "chrome/browser/supervised_user/supervised_user_service_factory.h"
42 #include "chrome/browser/supervised_user/supervised_user_url_filter.h"
43 #endif
44 
45 namespace chrome {
46 
47 namespace {
48 
49 // Configuration options for Embedded Search.
50 // EmbeddedSearch field trials are named in such a way that we can parse out
51 // the experiment configuration from the trial's group name in order to give
52 // us maximum flexability in running experiments.
53 // Field trial groups should be named things like "Group7 espv:2 instant:1".
54 // The first token is always GroupN for some integer N, followed by a
55 // space-delimited list of key:value pairs which correspond to these flags:
56 const char kEmbeddedPageVersionFlagName[] = "espv";
57 #if defined(OS_IOS)
58 const uint64 kEmbeddedPageVersionDefault = 1;
59 #elif defined(OS_ANDROID)
60 const uint64 kEmbeddedPageVersionDefault = 1;
61 // Use this variant to enable EmbeddedSearch SearchBox API in the results page.
62 const uint64 kEmbeddedSearchEnabledVersion = 2;
63 #else
64 const uint64 kEmbeddedPageVersionDefault = 2;
65 #endif
66 
67 const char kHideVerbatimFlagName[] = "hide_verbatim";
68 const char kPrefetchSearchResultsFlagName[] = "prefetch_results";
69 const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp";
70 const char kAllowPrefetchNonDefaultMatch[] = "allow_prefetch_non_default_match";
71 const char kPrerenderInstantUrlOnOmniboxFocus[] =
72     "prerender_instant_url_on_omnibox_focus";
73 
74 // Controls whether to reuse prerendered Instant Search base page to commit any
75 // search query.
76 const char kReuseInstantSearchBasePage[] = "reuse_instant_search_base_page";
77 
78 // Controls whether to use the alternate Instant search base URL. This allows
79 // experimentation of Instant search.
80 const char kUseAltInstantURL[] = "use_alternate_instant_url";
81 const char kAltInstantURLPath[] = "search";
82 const char kAltInstantURLQueryParams[] = "&qbp=1";
83 
84 const char kDisplaySearchButtonFlagName[] = "display_search_button";
85 const char kOriginChipFlagName[] = "origin_chip";
86 #if !defined(OS_IOS) && !defined(OS_ANDROID)
87 const char kEnableQueryExtractionFlagName[] = "query_extraction";
88 #endif
89 const char kShouldShowGoogleLocalNTPFlagName[] = "google_local_ntp";
90 
91 // Constants for the field trial name and group prefix.
92 // Note in M30 and below this field trial was named "InstantExtended" and in
93 // M31 was renamed to EmbeddedSearch for clarity and cleanliness.  Since we
94 // can't easilly sync up Finch configs with the pushing of this change to
95 // Dev & Canary, for now the code accepts both names.
96 // TODO(dcblack): Remove the InstantExtended name once M31 hits the Beta
97 // channel.
98 const char kInstantExtendedFieldTrialName[] = "InstantExtended";
99 const char kEmbeddedSearchFieldTrialName[] = "EmbeddedSearch";
100 
101 // If the field trial's group name ends with this string its configuration will
102 // be ignored and Instant Extended will not be enabled by default.
103 const char kDisablingSuffix[] = "DISABLED";
104 
105 // Status of the New Tab URL for the default Search provider. NOTE: Used in a
106 // UMA histogram so values should only be added at the end and not reordered.
107 enum NewTabURLState {
108   // Valid URL that should be used.
109   NEW_TAB_URL_VALID = 0,
110 
111   // Corrupt state (e.g. no profile or template url).
112   NEW_TAB_URL_BAD = 1,
113 
114   // URL should not be used because in incognito window.
115   NEW_TAB_URL_INCOGNITO = 2,
116 
117   // No New Tab URL set for provider.
118   NEW_TAB_URL_NOT_SET = 3,
119 
120   // URL is not secure.
121   NEW_TAB_URL_INSECURE = 4,
122 
123   // URL should not be used because Suggest is disabled.
124   // Not used anymore, see crbug.com/340424.
125   // NEW_TAB_URL_SUGGEST_OFF = 5,
126 
127   // URL should not be used because it is blocked for a supervised user.
128   NEW_TAB_URL_BLOCKED = 6,
129 
130   NEW_TAB_URL_MAX
131 };
132 
133 // Used to set the Instant support state of the Navigation entry.
134 const char kInstantSupportStateKey[] = "instant_support_state";
135 
136 const char kInstantSupportEnabled[] = "Instant support enabled";
137 const char kInstantSupportDisabled[] = "Instant support disabled";
138 const char kInstantSupportUnknown[] = "Instant support unknown";
139 
StringToInstantSupportState(const base::string16 & value)140 InstantSupportState StringToInstantSupportState(const base::string16& value) {
141   if (value == base::ASCIIToUTF16(kInstantSupportEnabled))
142     return INSTANT_SUPPORT_YES;
143   else if (value == base::ASCIIToUTF16(kInstantSupportDisabled))
144     return INSTANT_SUPPORT_NO;
145   else
146     return INSTANT_SUPPORT_UNKNOWN;
147 }
148 
InstantSupportStateToString(InstantSupportState state)149 base::string16 InstantSupportStateToString(InstantSupportState state) {
150   switch (state) {
151     case INSTANT_SUPPORT_NO:
152       return base::ASCIIToUTF16(kInstantSupportDisabled);
153     case INSTANT_SUPPORT_YES:
154       return base::ASCIIToUTF16(kInstantSupportEnabled);
155     case INSTANT_SUPPORT_UNKNOWN:
156       return base::ASCIIToUTF16(kInstantSupportUnknown);
157   }
158   return base::ASCIIToUTF16(kInstantSupportUnknown);
159 }
160 
GetDefaultSearchProviderTemplateURL(Profile * profile)161 TemplateURL* GetDefaultSearchProviderTemplateURL(Profile* profile) {
162   if (profile) {
163     TemplateURLService* template_url_service =
164         TemplateURLServiceFactory::GetForProfile(profile);
165     if (template_url_service)
166       return template_url_service->GetDefaultSearchProvider();
167   }
168   return NULL;
169 }
170 
TemplateURLRefToGURL(const TemplateURLRef & ref,const SearchTermsData & search_terms_data,int start_margin,bool append_extra_query_params,bool force_instant_results)171 GURL TemplateURLRefToGURL(const TemplateURLRef& ref,
172                           const SearchTermsData& search_terms_data,
173                           int start_margin,
174                           bool append_extra_query_params,
175                           bool force_instant_results) {
176   TemplateURLRef::SearchTermsArgs search_terms_args =
177       TemplateURLRef::SearchTermsArgs(base::string16());
178   search_terms_args.omnibox_start_margin = start_margin;
179   search_terms_args.append_extra_query_params = append_extra_query_params;
180   search_terms_args.force_instant_results = force_instant_results;
181   return GURL(ref.ReplaceSearchTerms(search_terms_args, search_terms_data));
182 }
183 
MatchesAnySearchURL(const GURL & url,TemplateURL * template_url,const SearchTermsData & search_terms_data)184 bool MatchesAnySearchURL(const GURL& url,
185                          TemplateURL* template_url,
186                          const SearchTermsData& search_terms_data) {
187   GURL search_url =
188       TemplateURLRefToGURL(template_url->url_ref(), search_terms_data,
189                            kDisableStartMargin, false, false);
190   if (search_url.is_valid() &&
191       search::MatchesOriginAndPath(url, search_url))
192     return true;
193 
194   // "URLCount() - 1" because we already tested url_ref above.
195   for (size_t i = 0; i < template_url->URLCount() - 1; ++i) {
196     TemplateURLRef ref(template_url, i);
197     search_url = TemplateURLRefToGURL(ref, search_terms_data,
198                                       kDisableStartMargin, false, false);
199     if (search_url.is_valid() &&
200         search::MatchesOriginAndPath(url, search_url))
201       return true;
202   }
203 
204   return false;
205 }
206 
207 
208 
209 // |url| should either have a secure scheme or have a non-HTTPS base URL that
210 // the user specified using --google-base-url. (This allows testers to use
211 // --google-base-url to point at non-HTTPS servers, which eases testing.)
IsSuitableURLForInstant(const GURL & url,const TemplateURL * template_url)212 bool IsSuitableURLForInstant(const GURL& url, const TemplateURL* template_url) {
213   return template_url->HasSearchTermsReplacementKey(url) &&
214       (url.SchemeIsSecure() ||
215        google_util::StartsWithCommandLineGoogleBaseURL(url));
216 }
217 
218 // Returns true if |url| can be used as an Instant URL for |profile|.
IsInstantURL(const GURL & url,Profile * profile)219 bool IsInstantURL(const GURL& url, Profile* profile) {
220   if (!IsInstantExtendedAPIEnabled())
221     return false;
222 
223   if (!url.is_valid())
224     return false;
225 
226   const GURL new_tab_url(GetNewTabPageURL(profile));
227   if (new_tab_url.is_valid() &&
228       search::MatchesOriginAndPath(url, new_tab_url))
229     return true;
230 
231   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
232   if (!template_url)
233     return false;
234 
235   if (!IsSuitableURLForInstant(url, template_url))
236     return false;
237 
238   const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
239   UIThreadSearchTermsData search_terms_data(profile);
240   const GURL instant_url = TemplateURLRefToGURL(
241       instant_url_ref, search_terms_data, kDisableStartMargin, false, false);
242   if (!instant_url.is_valid())
243     return false;
244 
245   if (search::MatchesOriginAndPath(url, instant_url))
246     return true;
247 
248   return IsQueryExtractionEnabled() &&
249       MatchesAnySearchURL(url, template_url, search_terms_data);
250 }
251 
GetSearchTermsImpl(const content::WebContents * contents,const content::NavigationEntry * entry)252 base::string16 GetSearchTermsImpl(const content::WebContents* contents,
253                                   const content::NavigationEntry* entry) {
254   if (!contents || !IsQueryExtractionEnabled())
255     return base::string16();
256 
257   // For security reasons, don't extract search terms if the page is not being
258   // rendered in the privileged Instant renderer process. This is to protect
259   // against a malicious page somehow scripting the search results page and
260   // faking search terms in the URL. Random pages can't get into the Instant
261   // renderer and scripting doesn't work cross-process, so if the page is in
262   // the Instant process, we know it isn't being exploited.
263   Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
264   if (IsInstantExtendedAPIEnabled() &&
265       !IsRenderedInInstantProcess(contents, profile) &&
266       ((entry == contents->GetController().GetLastCommittedEntry()) ||
267        !ShouldAssignURLToInstantRenderer(entry->GetURL(), profile)))
268     return base::string16();
269 
270   // Check to see if search terms have already been extracted.
271   base::string16 search_terms = GetSearchTermsFromNavigationEntry(entry);
272   if (!search_terms.empty())
273     return search_terms;
274 
275   if (!IsQueryExtractionAllowedForURL(profile, entry->GetVirtualURL()))
276     return base::string16();
277 
278   // Otherwise, extract from the URL.
279   return ExtractSearchTermsFromURL(profile, entry->GetVirtualURL());
280 }
281 
IsURLAllowedForSupervisedUser(const GURL & url,Profile * profile)282 bool IsURLAllowedForSupervisedUser(const GURL& url, Profile* profile) {
283 #if defined(ENABLE_MANAGED_USERS)
284   SupervisedUserService* supervised_user_service =
285       SupervisedUserServiceFactory::GetForProfile(profile);
286   SupervisedUserURLFilter* url_filter =
287       supervised_user_service->GetURLFilterForUIThread();
288   if (url_filter->GetFilteringBehaviorForURL(url) ==
289           SupervisedUserURLFilter::BLOCK) {
290     return false;
291   }
292 #endif
293   return true;
294 }
295 
296 // Returns whether |new_tab_url| can be used as a URL for the New Tab page.
297 // NEW_TAB_URL_VALID means a valid URL; other enum values imply an invalid URL.
IsValidNewTabURL(Profile * profile,const GURL & new_tab_url)298 NewTabURLState IsValidNewTabURL(Profile* profile, const GURL& new_tab_url) {
299   if (profile->IsOffTheRecord())
300     return NEW_TAB_URL_INCOGNITO;
301   if (!new_tab_url.is_valid())
302     return NEW_TAB_URL_NOT_SET;
303   if (!new_tab_url.SchemeIsSecure())
304     return NEW_TAB_URL_INSECURE;
305   if (!IsURLAllowedForSupervisedUser(new_tab_url, profile))
306     return NEW_TAB_URL_BLOCKED;
307   return NEW_TAB_URL_VALID;
308 }
309 
310 // Used to look up the URL to use for the New Tab page. Also tracks how we
311 // arrived at that URL so it can be logged with UMA.
312 struct NewTabURLDetails {
NewTabURLDetailschrome::__anonb6446fde0111::NewTabURLDetails313   NewTabURLDetails(const GURL& url, NewTabURLState state)
314       : url(url), state(state) {}
315 
ForProfilechrome::__anonb6446fde0111::NewTabURLDetails316   static NewTabURLDetails ForProfile(Profile* profile) {
317     const GURL local_url(chrome::kChromeSearchLocalNtpUrl);
318     TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
319     if (!profile || !template_url)
320       return NewTabURLDetails(local_url, NEW_TAB_URL_BAD);
321 
322     GURL search_provider_url = TemplateURLRefToGURL(
323         template_url->new_tab_url_ref(), UIThreadSearchTermsData(profile),
324         kDisableStartMargin, false, false);
325     NewTabURLState state = IsValidNewTabURL(profile, search_provider_url);
326     switch (state) {
327       case NEW_TAB_URL_VALID:
328         // We can use the search provider's page.
329         return NewTabURLDetails(search_provider_url, state);
330       case NEW_TAB_URL_INCOGNITO:
331         // Incognito has its own New Tab.
332         return NewTabURLDetails(GURL(), state);
333       default:
334         // Use the local New Tab otherwise.
335         return NewTabURLDetails(local_url, state);
336     }
337   }
338 
339   GURL url;
340   NewTabURLState state;
341 };
342 
343 }  // namespace
344 
345 // Negative start-margin values prevent the "es_sm" parameter from being used.
346 const int kDisableStartMargin = -1;
347 
IsInstantExtendedAPIEnabled()348 bool IsInstantExtendedAPIEnabled() {
349 #if defined(OS_IOS)
350   return false;
351 #elif defined(OS_ANDROID)
352   return EmbeddedSearchPageVersion() == kEmbeddedSearchEnabledVersion;
353 #else
354   return true;
355 #endif  // defined(OS_IOS)
356 }
357 
358 // Determine what embedded search page version to request from the user's
359 // default search provider. If 0, the embedded search UI should not be enabled.
EmbeddedSearchPageVersion()360 uint64 EmbeddedSearchPageVersion() {
361 #if defined(OS_ANDROID)
362   if (CommandLine::ForCurrentProcess()->HasSwitch(
363       switches::kEnableEmbeddedSearchAPI)) {
364     return kEmbeddedSearchEnabledVersion;
365   }
366 #endif
367 
368   FieldTrialFlags flags;
369   if (GetFieldTrialInfo(&flags)) {
370     return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
371                                             kEmbeddedPageVersionDefault,
372                                             flags);
373   }
374   return kEmbeddedPageVersionDefault;
375 }
376 
InstantExtendedEnabledParam(bool for_search)377 std::string InstantExtendedEnabledParam(bool for_search) {
378   if (for_search && !chrome::IsQueryExtractionEnabled())
379     return std::string();
380   return std::string(google_util::kInstantExtendedAPIParam) + "=" +
381       base::Uint64ToString(EmbeddedSearchPageVersion()) + "&";
382 }
383 
ForceInstantResultsParam(bool for_prerender)384 std::string ForceInstantResultsParam(bool for_prerender) {
385   return (for_prerender || !IsInstantExtendedAPIEnabled()) ?
386       "ion=1&" : std::string();
387 }
388 
IsQueryExtractionEnabled()389 bool IsQueryExtractionEnabled() {
390 #if defined(OS_IOS) || defined(OS_ANDROID)
391   return true;
392 #else
393   if (!IsInstantExtendedAPIEnabled())
394     return false;
395 
396   const CommandLine* command_line = CommandLine::ForCurrentProcess();
397   if (command_line->HasSwitch(switches::kEnableQueryExtraction))
398     return true;
399 
400   FieldTrialFlags flags;
401   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
402       kEnableQueryExtractionFlagName, false, flags);
403 #endif  // defined(OS_IOS) || defined(OS_ANDROID)
404 }
405 
ExtractSearchTermsFromURL(Profile * profile,const GURL & url)406 base::string16 ExtractSearchTermsFromURL(Profile* profile, const GURL& url) {
407   if (url.is_valid() && url == GetSearchResultPrefetchBaseURL(profile)) {
408     // InstantSearchPrerenderer has the search query for the Instant search base
409     // page.
410     InstantSearchPrerenderer* prerenderer =
411         InstantSearchPrerenderer::GetForProfile(profile);
412     // TODO(kmadhusu): Remove this CHECK after the investigation of
413     // crbug.com/367204.
414     CHECK(prerenderer);
415     return prerenderer->get_last_query();
416   }
417 
418   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
419   base::string16 search_terms;
420   if (template_url)
421     template_url->ExtractSearchTermsFromURL(
422         url, UIThreadSearchTermsData(profile), &search_terms);
423   return search_terms;
424 }
425 
IsQueryExtractionAllowedForURL(Profile * profile,const GURL & url)426 bool IsQueryExtractionAllowedForURL(Profile* profile, const GURL& url) {
427   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
428   return template_url && IsSuitableURLForInstant(url, template_url);
429 }
430 
GetSearchTermsFromNavigationEntry(const content::NavigationEntry * entry)431 base::string16 GetSearchTermsFromNavigationEntry(
432     const content::NavigationEntry* entry) {
433   base::string16 search_terms;
434   if (entry)
435     entry->GetExtraData(sessions::kSearchTermsKey, &search_terms);
436   return search_terms;
437 }
438 
GetSearchTerms(const content::WebContents * contents)439 base::string16 GetSearchTerms(const content::WebContents* contents) {
440   if (!contents)
441     return base::string16();
442 
443   const content::NavigationEntry* entry =
444       contents->GetController().GetVisibleEntry();
445   if (!entry)
446     return base::string16();
447 
448   if (IsInstantExtendedAPIEnabled()) {
449     InstantSupportState state =
450         GetInstantSupportStateFromNavigationEntry(*entry);
451     if (state == INSTANT_SUPPORT_NO)
452       return base::string16();
453   }
454 
455   return GetSearchTermsImpl(contents, entry);
456 }
457 
ShouldAssignURLToInstantRenderer(const GURL & url,Profile * profile)458 bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) {
459   return url.is_valid() &&
460          profile &&
461          IsInstantExtendedAPIEnabled() &&
462          (url.SchemeIs(chrome::kChromeSearchScheme) ||
463           IsInstantURL(url, profile));
464 }
465 
IsRenderedInInstantProcess(const content::WebContents * contents,Profile * profile)466 bool IsRenderedInInstantProcess(const content::WebContents* contents,
467                                 Profile* profile) {
468   const content::RenderProcessHost* process_host =
469       contents->GetRenderProcessHost();
470   if (!process_host)
471     return false;
472 
473   const InstantService* instant_service =
474       InstantServiceFactory::GetForProfile(profile);
475   if (!instant_service)
476     return false;
477 
478   return instant_service->IsInstantProcess(process_host->GetID());
479 }
480 
ShouldUseProcessPerSiteForInstantURL(const GURL & url,Profile * profile)481 bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile) {
482   return ShouldAssignURLToInstantRenderer(url, profile) &&
483       (url.host() == chrome::kChromeSearchLocalNtpHost ||
484        url.host() == chrome::kChromeSearchRemoteNtpHost);
485 }
486 
IsNTPURL(const GURL & url,Profile * profile)487 bool IsNTPURL(const GURL& url, Profile* profile) {
488   if (!url.is_valid())
489     return false;
490 
491   if (!IsInstantExtendedAPIEnabled())
492     return url == GURL(chrome::kChromeUINewTabURL);
493 
494   const base::string16 search_terms = ExtractSearchTermsFromURL(profile, url);
495   return profile &&
496       ((IsInstantURL(url, profile) && search_terms.empty()) ||
497        url == GURL(chrome::kChromeSearchLocalNtpUrl));
498 }
499 
IsInstantNTP(const content::WebContents * contents)500 bool IsInstantNTP(const content::WebContents* contents) {
501   if (!contents)
502     return false;
503 
504   return NavEntryIsInstantNTP(contents,
505                               contents->GetController().GetVisibleEntry());
506 }
507 
NavEntryIsInstantNTP(const content::WebContents * contents,const content::NavigationEntry * entry)508 bool NavEntryIsInstantNTP(const content::WebContents* contents,
509                           const content::NavigationEntry* entry) {
510   if (!contents || !entry || !IsInstantExtendedAPIEnabled())
511     return false;
512 
513   Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
514   if (!IsRenderedInInstantProcess(contents, profile))
515     return false;
516 
517   if (entry->GetURL() == GetLocalInstantURL(profile))
518     return true;
519 
520   GURL new_tab_url(GetNewTabPageURL(profile));
521   return new_tab_url.is_valid() &&
522       search::MatchesOriginAndPath(entry->GetURL(), new_tab_url);
523 }
524 
IsSuggestPrefEnabled(Profile * profile)525 bool IsSuggestPrefEnabled(Profile* profile) {
526   return profile && !profile->IsOffTheRecord() && profile->GetPrefs() &&
527          profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled);
528 }
529 
GetInstantURL(Profile * profile,int start_margin,bool force_instant_results)530 GURL GetInstantURL(Profile* profile, int start_margin,
531                    bool force_instant_results) {
532   if (!IsInstantExtendedAPIEnabled() || !IsSuggestPrefEnabled(profile))
533     return GURL();
534 
535   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
536   if (!template_url)
537     return GURL();
538 
539   GURL instant_url = TemplateURLRefToGURL(
540       template_url->instant_url_ref(), UIThreadSearchTermsData(profile),
541       start_margin, true, force_instant_results);
542   if (!instant_url.is_valid() ||
543       !template_url->HasSearchTermsReplacementKey(instant_url))
544     return GURL();
545 
546   // Extended mode requires HTTPS.  Force it unless the base URL was overridden
547   // on the command line, in which case we allow HTTP (see comments on
548   // IsSuitableURLForInstant()).
549   if (!instant_url.SchemeIsSecure() &&
550       !google_util::StartsWithCommandLineGoogleBaseURL(instant_url)) {
551     GURL::Replacements replacements;
552     const std::string secure_scheme(url::kHttpsScheme);
553     replacements.SetSchemeStr(secure_scheme);
554     instant_url = instant_url.ReplaceComponents(replacements);
555   }
556 
557   if (!IsURLAllowedForSupervisedUser(instant_url, profile))
558     return GURL();
559 
560   if (ShouldUseAltInstantURL()) {
561     GURL::Replacements replacements;
562     const std::string path(kAltInstantURLPath);
563     replacements.SetPathStr(path);
564     const std::string query(
565         instant_url.query() + std::string(kAltInstantURLQueryParams));
566     replacements.SetQueryStr(query);
567     instant_url = instant_url.ReplaceComponents(replacements);
568   }
569   return instant_url;
570 }
571 
572 // Returns URLs associated with the default search engine for |profile|.
GetSearchURLs(Profile * profile)573 std::vector<GURL> GetSearchURLs(Profile* profile) {
574   std::vector<GURL> result;
575   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
576   if (!template_url)
577     return result;
578   for (size_t i = 0; i < template_url->URLCount(); ++i) {
579     TemplateURLRef ref(template_url, i);
580     result.push_back(TemplateURLRefToGURL(ref, UIThreadSearchTermsData(profile),
581                                           kDisableStartMargin, false, false));
582   }
583   return result;
584 }
585 
GetNewTabPageURL(Profile * profile)586 GURL GetNewTabPageURL(Profile* profile) {
587   return NewTabURLDetails::ForProfile(profile).url;
588 }
589 
GetSearchResultPrefetchBaseURL(Profile * profile)590 GURL GetSearchResultPrefetchBaseURL(Profile* profile) {
591   return ShouldPrefetchSearchResults() ?
592       GetInstantURL(profile, kDisableStartMargin, true) : GURL();
593 }
594 
ShouldPrefetchSearchResults()595 bool ShouldPrefetchSearchResults() {
596   if (!IsInstantExtendedAPIEnabled())
597     return false;
598 
599   if (CommandLine::ForCurrentProcess()->HasSwitch(
600           switches::kPrefetchSearchResults)) {
601     return true;
602   }
603 
604   FieldTrialFlags flags;
605   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
606       kPrefetchSearchResultsFlagName, false, flags);
607 }
608 
ShouldAllowPrefetchNonDefaultMatch()609 bool ShouldAllowPrefetchNonDefaultMatch() {
610   if (!ShouldPrefetchSearchResults())
611     return false;
612 
613   FieldTrialFlags flags;
614   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
615       kAllowPrefetchNonDefaultMatch, false, flags);
616 }
617 
ShouldPrerenderInstantUrlOnOmniboxFocus()618 bool ShouldPrerenderInstantUrlOnOmniboxFocus() {
619   if (!ShouldPrefetchSearchResults())
620     return false;
621 
622   FieldTrialFlags flags;
623   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
624       kPrerenderInstantUrlOnOmniboxFocus, false, flags);
625 }
626 
ShouldReuseInstantSearchBasePage()627 bool ShouldReuseInstantSearchBasePage() {
628   if (!ShouldPrefetchSearchResults())
629     return false;
630 
631   FieldTrialFlags flags;
632   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
633       kReuseInstantSearchBasePage, false, flags);
634 }
635 
GetLocalInstantURL(Profile * profile)636 GURL GetLocalInstantURL(Profile* profile) {
637   return GURL(chrome::kChromeSearchLocalNtpUrl);
638 }
639 
ShouldHideTopVerbatimMatch()640 bool ShouldHideTopVerbatimMatch() {
641   FieldTrialFlags flags;
642   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
643       kHideVerbatimFlagName, false, flags);
644 }
645 
GetDisplaySearchButtonConditions()646 DisplaySearchButtonConditions GetDisplaySearchButtonConditions() {
647   const CommandLine* cl = CommandLine::ForCurrentProcess();
648   if (cl->HasSwitch(switches::kDisableSearchButtonInOmnibox))
649     return DISPLAY_SEARCH_BUTTON_NEVER;
650   if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStr))
651     return DISPLAY_SEARCH_BUTTON_FOR_STR;
652   if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStrOrIip))
653     return DISPLAY_SEARCH_BUTTON_FOR_STR_OR_IIP;
654   if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxAlways))
655     return DISPLAY_SEARCH_BUTTON_ALWAYS;
656 
657   FieldTrialFlags flags;
658   if (!GetFieldTrialInfo(&flags))
659     return DISPLAY_SEARCH_BUTTON_NEVER;
660   uint64 value =
661       GetUInt64ValueForFlagWithDefault(kDisplaySearchButtonFlagName, 0, flags);
662   return (value < DISPLAY_SEARCH_BUTTON_NUM_VALUES) ?
663       static_cast<DisplaySearchButtonConditions>(value) :
664       DISPLAY_SEARCH_BUTTON_NEVER;
665 }
666 
ShouldDisplayOriginChip()667 bool ShouldDisplayOriginChip() {
668   return GetOriginChipCondition() != ORIGIN_CHIP_DISABLED;
669 }
670 
GetOriginChipCondition()671 OriginChipCondition GetOriginChipCondition() {
672   const CommandLine* cl = CommandLine::ForCurrentProcess();
673   if (cl->HasSwitch(switches::kDisableOriginChip))
674     return ORIGIN_CHIP_DISABLED;
675   if (cl->HasSwitch(switches::kEnableOriginChipAlways))
676     return ORIGIN_CHIP_ALWAYS;
677   if (cl->HasSwitch(switches::kEnableOriginChipOnSrp))
678     return ORIGIN_CHIP_ON_SRP;
679 
680   FieldTrialFlags flags;
681   if (!GetFieldTrialInfo(&flags))
682     return ORIGIN_CHIP_DISABLED;
683   uint64 value =
684       GetUInt64ValueForFlagWithDefault(kOriginChipFlagName, 0, flags);
685   return (value < ORIGIN_CHIP_NUM_VALUES) ?
686       static_cast<OriginChipCondition>(value) : ORIGIN_CHIP_DISABLED;
687 }
688 
ShouldShowGoogleLocalNTP()689 bool ShouldShowGoogleLocalNTP() {
690   FieldTrialFlags flags;
691   return !GetFieldTrialInfo(&flags) || GetBoolValueForFlagWithDefault(
692       kShouldShowGoogleLocalNTPFlagName, true, flags);
693 }
694 
GetEffectiveURLForInstant(const GURL & url,Profile * profile)695 GURL GetEffectiveURLForInstant(const GURL& url, Profile* profile) {
696   CHECK(ShouldAssignURLToInstantRenderer(url, profile))
697       << "Error granting Instant access.";
698 
699   if (url.SchemeIs(chrome::kChromeSearchScheme))
700     return url;
701 
702   GURL effective_url(url);
703 
704   // Replace the scheme with "chrome-search:".
705   url::Replacements<char> replacements;
706   std::string search_scheme(chrome::kChromeSearchScheme);
707   replacements.SetScheme(search_scheme.data(),
708                          url::Component(0, search_scheme.length()));
709 
710   // If this is the URL for a server-provided NTP, replace the host with
711   // "remote-ntp".
712   std::string remote_ntp_host(chrome::kChromeSearchRemoteNtpHost);
713   NewTabURLDetails details = NewTabURLDetails::ForProfile(profile);
714   if (details.state == NEW_TAB_URL_VALID &&
715       search::MatchesOriginAndPath(url, details.url)) {
716     replacements.SetHost(remote_ntp_host.c_str(),
717                          url::Component(0, remote_ntp_host.length()));
718   }
719 
720   effective_url = effective_url.ReplaceComponents(replacements);
721   return effective_url;
722 }
723 
HandleNewTabURLRewrite(GURL * url,content::BrowserContext * browser_context)724 bool HandleNewTabURLRewrite(GURL* url,
725                             content::BrowserContext* browser_context) {
726   if (!IsInstantExtendedAPIEnabled())
727     return false;
728 
729   if (!url->SchemeIs(content::kChromeUIScheme) ||
730       url->host() != chrome::kChromeUINewTabHost)
731     return false;
732 
733   Profile* profile = Profile::FromBrowserContext(browser_context);
734   NewTabURLDetails details(NewTabURLDetails::ForProfile(profile));
735   UMA_HISTOGRAM_ENUMERATION("NewTabPage.URLState",
736                             details.state, NEW_TAB_URL_MAX);
737   if (details.url.is_valid()) {
738     *url = details.url;
739     return true;
740   }
741   return false;
742 }
743 
HandleNewTabURLReverseRewrite(GURL * url,content::BrowserContext * browser_context)744 bool HandleNewTabURLReverseRewrite(GURL* url,
745                                    content::BrowserContext* browser_context) {
746   if (!IsInstantExtendedAPIEnabled())
747     return false;
748 
749   // Do nothing in incognito.
750   Profile* profile = Profile::FromBrowserContext(browser_context);
751   if (profile && profile->IsOffTheRecord())
752     return false;
753 
754   if (search::MatchesOriginAndPath(
755       GURL(chrome::kChromeSearchLocalNtpUrl), *url)) {
756     *url = GURL(chrome::kChromeUINewTabURL);
757     return true;
758   }
759 
760   GURL new_tab_url(GetNewTabPageURL(profile));
761   if (new_tab_url.is_valid() &&
762       search::MatchesOriginAndPath(new_tab_url, *url)) {
763     *url = GURL(chrome::kChromeUINewTabURL);
764     return true;
765   }
766 
767   return false;
768 }
769 
SetInstantSupportStateInNavigationEntry(InstantSupportState state,content::NavigationEntry * entry)770 void SetInstantSupportStateInNavigationEntry(InstantSupportState state,
771                                              content::NavigationEntry* entry) {
772   if (!entry)
773     return;
774 
775   entry->SetExtraData(kInstantSupportStateKey,
776                       InstantSupportStateToString(state));
777 }
778 
GetInstantSupportStateFromNavigationEntry(const content::NavigationEntry & entry)779 InstantSupportState GetInstantSupportStateFromNavigationEntry(
780     const content::NavigationEntry& entry) {
781   base::string16 value;
782   if (!entry.GetExtraData(kInstantSupportStateKey, &value))
783     return INSTANT_SUPPORT_UNKNOWN;
784 
785   return StringToInstantSupportState(value);
786 }
787 
ShouldPrefetchSearchResultsOnSRP()788 bool ShouldPrefetchSearchResultsOnSRP() {
789   FieldTrialFlags flags;
790   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
791       kPrefetchSearchResultsOnSRP, false, flags);
792 }
793 
EnableQueryExtractionForTesting()794 void EnableQueryExtractionForTesting() {
795   CommandLine* cl = CommandLine::ForCurrentProcess();
796   cl->AppendSwitch(switches::kEnableQueryExtraction);
797 }
798 
GetFieldTrialInfo(FieldTrialFlags * flags)799 bool GetFieldTrialInfo(FieldTrialFlags* flags) {
800   // Get the group name.  If the EmbeddedSearch trial doesn't exist, look for
801   // the older InstantExtended name.
802   std::string group_name = base::FieldTrialList::FindFullName(
803       kEmbeddedSearchFieldTrialName);
804   if (group_name.empty()) {
805     group_name = base::FieldTrialList::FindFullName(
806         kInstantExtendedFieldTrialName);
807   }
808 
809   if (EndsWith(group_name, kDisablingSuffix, true))
810     return false;
811 
812   // We have a valid trial that isn't disabled. Extract the flags.
813   std::string group_prefix(group_name);
814   size_t first_space = group_name.find(" ");
815   if (first_space != std::string::npos) {
816     // There is a flags section of the group name. Split that out and parse it.
817     group_prefix = group_name.substr(0, first_space);
818     if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space),
819                                             ':', ' ', flags)) {
820       // Failed to parse the flags section. Assume the whole group name is
821       // invalid.
822       return false;
823     }
824   }
825   return true;
826 }
827 
828 // Given a FieldTrialFlags object, returns the string value of the provided
829 // flag.
GetStringValueForFlagWithDefault(const std::string & flag,const std::string & default_value,const FieldTrialFlags & flags)830 std::string GetStringValueForFlagWithDefault(const std::string& flag,
831                                              const std::string& default_value,
832                                              const FieldTrialFlags& flags) {
833   FieldTrialFlags::const_iterator i;
834   for (i = flags.begin(); i != flags.end(); i++) {
835     if (i->first == flag)
836       return i->second;
837   }
838   return default_value;
839 }
840 
841 // Given a FieldTrialFlags object, returns the uint64 value of the provided
842 // flag.
GetUInt64ValueForFlagWithDefault(const std::string & flag,uint64 default_value,const FieldTrialFlags & flags)843 uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag,
844                                         uint64 default_value,
845                                         const FieldTrialFlags& flags) {
846   uint64 value;
847   std::string str_value =
848       GetStringValueForFlagWithDefault(flag, std::string(), flags);
849   if (base::StringToUint64(str_value, &value))
850     return value;
851   return default_value;
852 }
853 
854 // Given a FieldTrialFlags object, returns the boolean value of the provided
855 // flag.
GetBoolValueForFlagWithDefault(const std::string & flag,bool default_value,const FieldTrialFlags & flags)856 bool GetBoolValueForFlagWithDefault(const std::string& flag,
857                                     bool default_value,
858                                     const FieldTrialFlags& flags) {
859   return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
860 }
861 
ShouldUseAltInstantURL()862 bool ShouldUseAltInstantURL() {
863   FieldTrialFlags flags;
864   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
865       kUseAltInstantURL, false, flags);
866 }
867 
868 }  // namespace chrome
869