search.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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
45namespace chrome {
46
47namespace {
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:
56const char kEmbeddedPageVersionFlagName[] = "espv";
57#if defined(OS_IOS)
58const uint64 kEmbeddedPageVersionDefault = 1;
59#elif defined(OS_ANDROID)
60const uint64 kEmbeddedPageVersionDefault = 1;
61// Use this variant to enable EmbeddedSearch SearchBox API in the results page.
62const uint64 kEmbeddedSearchEnabledVersion = 2;
63#else
64const uint64 kEmbeddedPageVersionDefault = 2;
65#endif
66
67const char kHideVerbatimFlagName[] = "hide_verbatim";
68const char kPrefetchSearchResultsFlagName[] = "prefetch_results";
69const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp";
70const char kAllowPrefetchNonDefaultMatch[] = "allow_prefetch_non_default_match";
71const 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.
76const 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.
80const char kUseAltInstantURL[] = "use_alternate_instant_url";
81const char kAltInstantURLPath[] = "search";
82const char kAltInstantURLQueryParams[] = "&qbp=1";
83
84const char kDisplaySearchButtonFlagName[] = "display_search_button";
85const char kOriginChipFlagName[] = "origin_chip";
86#if !defined(OS_IOS) && !defined(OS_ANDROID)
87const char kEnableQueryExtractionFlagName[] = "query_extraction";
88#endif
89const 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.
98const char kInstantExtendedFieldTrialName[] = "InstantExtended";
99const 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.
103const 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.
107enum 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.
134const char kInstantSupportStateKey[] = "instant_support_state";
135
136const char kInstantSupportEnabled[] = "Instant support enabled";
137const char kInstantSupportDisabled[] = "Instant support disabled";
138const char kInstantSupportUnknown[] = "Instant support unknown";
139
140InstantSupportState 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
149base::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
161TemplateURL* 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
171GURL 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
184bool 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// Returns true if |contents| is rendered inside the Instant process for
208// |profile|.
209bool IsRenderedInInstantProcess(const content::WebContents* contents,
210                                Profile* profile) {
211  const content::RenderProcessHost* process_host =
212      contents->GetRenderProcessHost();
213  if (!process_host)
214    return false;
215
216  const InstantService* instant_service =
217      InstantServiceFactory::GetForProfile(profile);
218  if (!instant_service)
219    return false;
220
221  return instant_service->IsInstantProcess(process_host->GetID());
222}
223
224// |url| should either have a secure scheme or have a non-HTTPS base URL that
225// the user specified using --google-base-url. (This allows testers to use
226// --google-base-url to point at non-HTTPS servers, which eases testing.)
227bool IsSuitableURLForInstant(const GURL& url, const TemplateURL* template_url) {
228  return template_url->HasSearchTermsReplacementKey(url) &&
229      (url.SchemeIsSecure() ||
230       google_util::StartsWithCommandLineGoogleBaseURL(url));
231}
232
233// Returns true if |url| can be used as an Instant URL for |profile|.
234bool IsInstantURL(const GURL& url, Profile* profile) {
235  if (!IsInstantExtendedAPIEnabled())
236    return false;
237
238  if (!url.is_valid())
239    return false;
240
241  const GURL new_tab_url(GetNewTabPageURL(profile));
242  if (new_tab_url.is_valid() &&
243      search::MatchesOriginAndPath(url, new_tab_url))
244    return true;
245
246  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
247  if (!template_url)
248    return false;
249
250  if (!IsSuitableURLForInstant(url, template_url))
251    return false;
252
253  const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
254  UIThreadSearchTermsData search_terms_data(profile);
255  const GURL instant_url = TemplateURLRefToGURL(
256      instant_url_ref, search_terms_data, kDisableStartMargin, false, false);
257  if (!instant_url.is_valid())
258    return false;
259
260  if (search::MatchesOriginAndPath(url, instant_url))
261    return true;
262
263  return IsQueryExtractionEnabled() &&
264      MatchesAnySearchURL(url, template_url, search_terms_data);
265}
266
267base::string16 GetSearchTermsImpl(const content::WebContents* contents,
268                                  const content::NavigationEntry* entry) {
269  if (!contents || !IsQueryExtractionEnabled())
270    return base::string16();
271
272  // For security reasons, don't extract search terms if the page is not being
273  // rendered in the privileged Instant renderer process. This is to protect
274  // against a malicious page somehow scripting the search results page and
275  // faking search terms in the URL. Random pages can't get into the Instant
276  // renderer and scripting doesn't work cross-process, so if the page is in
277  // the Instant process, we know it isn't being exploited.
278  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
279  if (IsInstantExtendedAPIEnabled() &&
280      !IsRenderedInInstantProcess(contents, profile) &&
281      ((entry == contents->GetController().GetLastCommittedEntry()) ||
282       !ShouldAssignURLToInstantRenderer(entry->GetURL(), profile)))
283    return base::string16();
284
285  // Check to see if search terms have already been extracted.
286  base::string16 search_terms = GetSearchTermsFromNavigationEntry(entry);
287  if (!search_terms.empty())
288    return search_terms;
289
290  if (!IsQueryExtractionAllowedForURL(profile, entry->GetVirtualURL()))
291    return base::string16();
292
293  // Otherwise, extract from the URL.
294  return ExtractSearchTermsFromURL(profile, entry->GetVirtualURL());
295}
296
297bool IsURLAllowedForSupervisedUser(const GURL& url, Profile* profile) {
298#if defined(ENABLE_MANAGED_USERS)
299  SupervisedUserService* supervised_user_service =
300      SupervisedUserServiceFactory::GetForProfile(profile);
301  SupervisedUserURLFilter* url_filter =
302      supervised_user_service->GetURLFilterForUIThread();
303  if (url_filter->GetFilteringBehaviorForURL(url) ==
304          SupervisedUserURLFilter::BLOCK) {
305    return false;
306  }
307#endif
308  return true;
309}
310
311// Returns whether |new_tab_url| can be used as a URL for the New Tab page.
312// NEW_TAB_URL_VALID means a valid URL; other enum values imply an invalid URL.
313NewTabURLState IsValidNewTabURL(Profile* profile, const GURL& new_tab_url) {
314  if (profile->IsOffTheRecord())
315    return NEW_TAB_URL_INCOGNITO;
316  if (!new_tab_url.is_valid())
317    return NEW_TAB_URL_NOT_SET;
318  if (!new_tab_url.SchemeIsSecure())
319    return NEW_TAB_URL_INSECURE;
320  if (!IsURLAllowedForSupervisedUser(new_tab_url, profile))
321    return NEW_TAB_URL_BLOCKED;
322  return NEW_TAB_URL_VALID;
323}
324
325// Used to look up the URL to use for the New Tab page. Also tracks how we
326// arrived at that URL so it can be logged with UMA.
327struct NewTabURLDetails {
328  NewTabURLDetails(const GURL& url, NewTabURLState state)
329      : url(url), state(state) {}
330
331  static NewTabURLDetails ForProfile(Profile* profile) {
332    const GURL local_url(chrome::kChromeSearchLocalNtpUrl);
333    TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
334    if (!profile || !template_url)
335      return NewTabURLDetails(local_url, NEW_TAB_URL_BAD);
336
337    GURL search_provider_url = TemplateURLRefToGURL(
338        template_url->new_tab_url_ref(), UIThreadSearchTermsData(profile),
339        kDisableStartMargin, false, false);
340    NewTabURLState state = IsValidNewTabURL(profile, search_provider_url);
341    switch (state) {
342      case NEW_TAB_URL_VALID:
343        // We can use the search provider's page.
344        return NewTabURLDetails(search_provider_url, state);
345      case NEW_TAB_URL_INCOGNITO:
346        // Incognito has its own New Tab.
347        return NewTabURLDetails(GURL(), state);
348      default:
349        // Use the local New Tab otherwise.
350        return NewTabURLDetails(local_url, state);
351    }
352  }
353
354  GURL url;
355  NewTabURLState state;
356};
357
358}  // namespace
359
360// Negative start-margin values prevent the "es_sm" parameter from being used.
361const int kDisableStartMargin = -1;
362
363bool IsInstantExtendedAPIEnabled() {
364#if defined(OS_IOS)
365  return false;
366#elif defined(OS_ANDROID)
367  return EmbeddedSearchPageVersion() == kEmbeddedSearchEnabledVersion;
368#else
369  return true;
370#endif  // defined(OS_IOS)
371}
372
373// Determine what embedded search page version to request from the user's
374// default search provider. If 0, the embedded search UI should not be enabled.
375uint64 EmbeddedSearchPageVersion() {
376#if defined(OS_ANDROID)
377  if (CommandLine::ForCurrentProcess()->HasSwitch(
378      switches::kEnableEmbeddedSearchAPI)) {
379    return kEmbeddedSearchEnabledVersion;
380  }
381#endif
382
383  FieldTrialFlags flags;
384  if (GetFieldTrialInfo(&flags)) {
385    return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
386                                            kEmbeddedPageVersionDefault,
387                                            flags);
388  }
389  return kEmbeddedPageVersionDefault;
390}
391
392std::string InstantExtendedEnabledParam(bool for_search) {
393  if (for_search && !chrome::IsQueryExtractionEnabled())
394    return std::string();
395  return std::string(google_util::kInstantExtendedAPIParam) + "=" +
396      base::Uint64ToString(EmbeddedSearchPageVersion()) + "&";
397}
398
399std::string ForceInstantResultsParam(bool for_prerender) {
400  return (for_prerender || !IsInstantExtendedAPIEnabled()) ?
401      "ion=1&" : std::string();
402}
403
404bool IsQueryExtractionEnabled() {
405#if defined(OS_IOS) || defined(OS_ANDROID)
406  return true;
407#else
408  if (!IsInstantExtendedAPIEnabled())
409    return false;
410
411  const CommandLine* command_line = CommandLine::ForCurrentProcess();
412  if (command_line->HasSwitch(switches::kEnableQueryExtraction))
413    return true;
414
415  FieldTrialFlags flags;
416  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
417      kEnableQueryExtractionFlagName, false, flags);
418#endif  // defined(OS_IOS) || defined(OS_ANDROID)
419}
420
421base::string16 ExtractSearchTermsFromURL(Profile* profile, const GURL& url) {
422  if (url.is_valid() && url == GetSearchResultPrefetchBaseURL(profile)) {
423    // InstantSearchPrerenderer has the search query for the Instant search base
424    // page.
425    InstantSearchPrerenderer* prerenderer =
426        InstantSearchPrerenderer::GetForProfile(profile);
427    // TODO(kmadhusu): Remove this CHECK after the investigation of
428    // crbug.com/367204.
429    CHECK(prerenderer);
430    return prerenderer->get_last_query();
431  }
432
433  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
434  base::string16 search_terms;
435  if (template_url)
436    template_url->ExtractSearchTermsFromURL(
437        url, UIThreadSearchTermsData(profile), &search_terms);
438  return search_terms;
439}
440
441bool IsQueryExtractionAllowedForURL(Profile* profile, const GURL& url) {
442  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
443  return template_url && IsSuitableURLForInstant(url, template_url);
444}
445
446base::string16 GetSearchTermsFromNavigationEntry(
447    const content::NavigationEntry* entry) {
448  base::string16 search_terms;
449  if (entry)
450    entry->GetExtraData(sessions::kSearchTermsKey, &search_terms);
451  return search_terms;
452}
453
454base::string16 GetSearchTerms(const content::WebContents* contents) {
455  if (!contents)
456    return base::string16();
457
458  const content::NavigationEntry* entry =
459      contents->GetController().GetVisibleEntry();
460  if (!entry)
461    return base::string16();
462
463  if (IsInstantExtendedAPIEnabled()) {
464    InstantSupportState state =
465        GetInstantSupportStateFromNavigationEntry(*entry);
466    if (state == INSTANT_SUPPORT_NO)
467      return base::string16();
468  }
469
470  return GetSearchTermsImpl(contents, entry);
471}
472
473bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) {
474  return url.is_valid() &&
475         profile &&
476         IsInstantExtendedAPIEnabled() &&
477         (url.SchemeIs(chrome::kChromeSearchScheme) ||
478          IsInstantURL(url, profile));
479}
480
481bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile) {
482  return ShouldAssignURLToInstantRenderer(url, profile) &&
483      (url.host() == chrome::kChromeSearchLocalNtpHost ||
484       url.host() == chrome::kChromeSearchRemoteNtpHost);
485}
486
487bool 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
500bool IsInstantNTP(const content::WebContents* contents) {
501  if (!contents)
502    return false;
503
504  return NavEntryIsInstantNTP(contents,
505                              contents->GetController().GetVisibleEntry());
506}
507
508bool 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
525bool IsSuggestPrefEnabled(Profile* profile) {
526  return profile && !profile->IsOffTheRecord() && profile->GetPrefs() &&
527         profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled);
528}
529
530GURL 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|.
573std::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
586GURL GetNewTabPageURL(Profile* profile) {
587  return NewTabURLDetails::ForProfile(profile).url;
588}
589
590GURL GetSearchResultPrefetchBaseURL(Profile* profile) {
591  return ShouldPrefetchSearchResults() ?
592      GetInstantURL(profile, kDisableStartMargin, true) : GURL();
593}
594
595bool 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
609bool ShouldAllowPrefetchNonDefaultMatch() {
610  if (!ShouldPrefetchSearchResults())
611    return false;
612
613  FieldTrialFlags flags;
614  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
615      kAllowPrefetchNonDefaultMatch, false, flags);
616}
617
618bool ShouldPrerenderInstantUrlOnOmniboxFocus() {
619  if (!ShouldPrefetchSearchResults())
620    return false;
621
622  FieldTrialFlags flags;
623  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
624      kPrerenderInstantUrlOnOmniboxFocus, false, flags);
625}
626
627bool ShouldReuseInstantSearchBasePage() {
628  if (!ShouldPrefetchSearchResults())
629    return false;
630
631  FieldTrialFlags flags;
632  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
633      kReuseInstantSearchBasePage, false, flags);
634}
635
636GURL GetLocalInstantURL(Profile* profile) {
637  return GURL(chrome::kChromeSearchLocalNtpUrl);
638}
639
640bool ShouldHideTopVerbatimMatch() {
641  FieldTrialFlags flags;
642  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
643      kHideVerbatimFlagName, false, flags);
644}
645
646DisplaySearchButtonConditions 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
667bool ShouldDisplayOriginChip() {
668  return GetOriginChipCondition() != ORIGIN_CHIP_DISABLED;
669}
670
671OriginChipCondition 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
689bool ShouldShowGoogleLocalNTP() {
690  FieldTrialFlags flags;
691  return !GetFieldTrialInfo(&flags) || GetBoolValueForFlagWithDefault(
692      kShouldShowGoogleLocalNTPFlagName, true, flags);
693}
694
695GURL 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
724bool 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
744bool 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
770void SetInstantSupportStateInNavigationEntry(InstantSupportState state,
771                                             content::NavigationEntry* entry) {
772  if (!entry)
773    return;
774
775  entry->SetExtraData(kInstantSupportStateKey,
776                      InstantSupportStateToString(state));
777}
778
779InstantSupportState 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
788bool ShouldPrefetchSearchResultsOnSRP() {
789  FieldTrialFlags flags;
790  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
791      kPrefetchSearchResultsOnSRP, false, flags);
792}
793
794void EnableQueryExtractionForTesting() {
795  CommandLine* cl = CommandLine::ForCurrentProcess();
796  cl->AppendSwitch(switches::kEnableQueryExtraction);
797}
798
799bool 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.
830std::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.
843uint64 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.
856bool GetBoolValueForFlagWithDefault(const std::string& flag,
857                                    bool default_value,
858                                    const FieldTrialFlags& flags) {
859  return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
860}
861
862bool ShouldUseAltInstantURL() {
863  FieldTrialFlags flags;
864  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
865      kUseAltInstantURL, false, flags);
866}
867
868}  // namespace chrome
869