普通文本  |  757行  |  26.82 KB

// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/search/search.h"

#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/prefs/pref_service.h"
#include "base/rand_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/search/instant_service.h"
#include "chrome/browser/search/instant_service_factory.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_instant_controller.h"
#include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/search/instant_search_prerenderer.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/search_urls.h"
#include "chrome/common/url_constants.h"
#include "components/sessions/serialized_navigation_entry.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

#if defined(ENABLE_MANAGED_USERS)
#include "chrome/browser/managed_mode/managed_mode_url_filter.h"
#include "chrome/browser/managed_mode/managed_user_service.h"
#include "chrome/browser/managed_mode/managed_user_service_factory.h"
#endif

namespace chrome {

namespace {

// Configuration options for Embedded Search.
// EmbeddedSearch field trials are named in such a way that we can parse out
// the experiment configuration from the trial's group name in order to give
// us maximum flexability in running experiments.
// Field trial groups should be named things like "Group7 espv:2 instant:1".
// The first token is always GroupN for some integer N, followed by a
// space-delimited list of key:value pairs which correspond to these flags:
const char kEmbeddedPageVersionFlagName[] = "espv";
#if defined(OS_IOS) || defined(OS_ANDROID)
const uint64 kEmbeddedPageVersionDefault = 1;
#else
const uint64 kEmbeddedPageVersionDefault = 2;
#endif

// The staleness timeout can be set (in seconds) via this config.
const char kStalePageTimeoutFlagName[] = "stale";
const int kStalePageTimeoutDefault = 3 * 3600;  // 3 hours.

const char kHideVerbatimFlagName[] = "hide_verbatim";
const char kShowNtpFlagName[] = "show_ntp";
const char kUseCacheableNTP[] = "use_cacheable_ntp";
const char kPrefetchSearchResultsFlagName[] = "prefetch_results";
const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp";
const char kDisplaySearchButtonFlagName[] = "display_search_button";
const char kEnableOriginChipFlagName[] = "origin_chip";
#if !defined(OS_IOS) && !defined(OS_ANDROID)
const char kEnableQueryExtractionFlagName[] = "query_extraction";
#endif

// Constants for the field trial name and group prefix.
// Note in M30 and below this field trial was named "InstantExtended" and in
// M31 was renamed to EmbeddedSearch for clarity and cleanliness.  Since we
// can't easilly sync up Finch configs with the pushing of this change to
// Dev & Canary, for now the code accepts both names.
// TODO(dcblack): Remove the InstantExtended name once M31 hits the Beta
// channel.
const char kInstantExtendedFieldTrialName[] = "InstantExtended";
const char kEmbeddedSearchFieldTrialName[] = "EmbeddedSearch";

// If the field trial's group name ends with this string its configuration will
// be ignored and Instant Extended will not be enabled by default.
const char kDisablingSuffix[] = "DISABLED";

// Used to set the Instant support state of the Navigation entry.
const char kInstantSupportStateKey[] = "instant_support_state";

const char kInstantSupportEnabled[] = "Instant support enabled";
const char kInstantSupportDisabled[] = "Instant support disabled";
const char kInstantSupportUnknown[] = "Instant support unknown";

InstantSupportState StringToInstantSupportState(const base::string16& value) {
  if (value == ASCIIToUTF16(kInstantSupportEnabled))
    return INSTANT_SUPPORT_YES;
  else if (value == ASCIIToUTF16(kInstantSupportDisabled))
    return INSTANT_SUPPORT_NO;
  else
    return INSTANT_SUPPORT_UNKNOWN;
}

base::string16 InstantSupportStateToString(InstantSupportState state) {
  switch (state) {
    case INSTANT_SUPPORT_NO:
      return ASCIIToUTF16(kInstantSupportDisabled);
    case INSTANT_SUPPORT_YES:
      return ASCIIToUTF16(kInstantSupportEnabled);
    case INSTANT_SUPPORT_UNKNOWN:
      return ASCIIToUTF16(kInstantSupportUnknown);
  }
  return ASCIIToUTF16(kInstantSupportUnknown);
}

TemplateURL* GetDefaultSearchProviderTemplateURL(Profile* profile) {
  TemplateURLService* template_url_service =
      TemplateURLServiceFactory::GetForProfile(profile);
  if (template_url_service)
    return template_url_service->GetDefaultSearchProvider();
  return NULL;
}

GURL TemplateURLRefToGURL(const TemplateURLRef& ref,
                          int start_margin,
                          bool append_extra_query_params,
                          bool force_instant_results) {
  TemplateURLRef::SearchTermsArgs search_terms_args =
      TemplateURLRef::SearchTermsArgs(base::string16());
  search_terms_args.omnibox_start_margin = start_margin;
  search_terms_args.append_extra_query_params = append_extra_query_params;
  search_terms_args.force_instant_results = force_instant_results;
  return GURL(ref.ReplaceSearchTerms(search_terms_args));
}

bool MatchesAnySearchURL(const GURL& url, TemplateURL* template_url) {
  GURL search_url =
      TemplateURLRefToGURL(template_url->url_ref(), kDisableStartMargin, false,
                           false);
  if (search_url.is_valid() &&
      search::MatchesOriginAndPath(url, search_url))
    return true;

  // "URLCount() - 1" because we already tested url_ref above.
  for (size_t i = 0; i < template_url->URLCount() - 1; ++i) {
    TemplateURLRef ref(template_url, i);
    search_url = TemplateURLRefToGURL(ref, kDisableStartMargin, false, false);
    if (search_url.is_valid() &&
        search::MatchesOriginAndPath(url, search_url))
      return true;
  }

  return false;
}

// Returns true if |contents| is rendered inside the Instant process for
// |profile|.
bool IsRenderedInInstantProcess(const content::WebContents* contents,
                                Profile* profile) {
  const content::RenderProcessHost* process_host =
      contents->GetRenderProcessHost();
  if (!process_host)
    return false;

  const InstantService* instant_service =
      InstantServiceFactory::GetForProfile(profile);
  if (!instant_service)
    return false;

  return instant_service->IsInstantProcess(process_host->GetID());
}

// Returns true if |url| passes some basic checks that must succeed for it to be
// usable as an instant URL:
// (1) It contains the search terms replacement key of |template_url|, which is
//     expected to be the TemplateURL* for the default search provider.
// (2) Either it has a secure scheme, or else the user has manually specified a
//     --google-base-url and it uses that base URL.  (This allows testers to use
//     --google-base-url to point at non-HTTPS servers, which eases testing.)
bool IsSuitableURLForInstant(const GURL& url, const TemplateURL* template_url) {
  return template_url->HasSearchTermsReplacementKey(url) &&
      (url.SchemeIsSecure() ||
       google_util::StartsWithCommandLineGoogleBaseURL(url));
}

// Returns true if |url| can be used as an Instant URL for |profile|.
bool IsInstantURL(const GURL& url, Profile* profile) {
  if (!IsInstantExtendedAPIEnabled())
    return false;

  if (!url.is_valid())
    return false;

  const GURL new_tab_url(GetNewTabPageURL(profile));
  if (new_tab_url.is_valid() &&
      search::MatchesOriginAndPath(url, new_tab_url))
    return true;

  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  if (!template_url)
    return false;

  if (!IsSuitableURLForInstant(url, template_url))
    return false;

  const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
  const GURL instant_url =
      TemplateURLRefToGURL(instant_url_ref, kDisableStartMargin, false, false);
  if (!instant_url.is_valid())
    return false;

  if (search::MatchesOriginAndPath(url, instant_url))
    return true;

  return IsQueryExtractionEnabled() && MatchesAnySearchURL(url, template_url);
}

base::string16 GetSearchTermsImpl(const content::WebContents* contents,
                                  const content::NavigationEntry* entry) {
  if (!contents || !IsQueryExtractionEnabled())
    return base::string16();

  // For security reasons, don't extract search terms if the page is not being
  // rendered in the privileged Instant renderer process. This is to protect
  // against a malicious page somehow scripting the search results page and
  // faking search terms in the URL. Random pages can't get into the Instant
  // renderer and scripting doesn't work cross-process, so if the page is in
  // the Instant process, we know it isn't being exploited.
  // Since iOS and Android doesn't use the instant framework, these checks are
  // disabled for the two platforms.
  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
#if !defined(OS_IOS) && !defined(OS_ANDROID)
  if (!IsRenderedInInstantProcess(contents, profile) &&
      ((entry == contents->GetController().GetLastCommittedEntry()) ||
       !ShouldAssignURLToInstantRenderer(entry->GetURL(), profile)))
    return base::string16();
#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
  // Check to see if search terms have already been extracted.
  base::string16 search_terms = GetSearchTermsFromNavigationEntry(entry);
  if (!search_terms.empty())
    return search_terms;

  // Otherwise, extract from the URL.
  return GetSearchTermsFromURL(profile, entry->GetVirtualURL());
}

bool IsURLAllowedForSupervisedUser(const GURL& url, Profile* profile) {
#if defined(ENABLE_MANAGED_USERS)
  ManagedUserService* managed_user_service =
      ManagedUserServiceFactory::GetForProfile(profile);
  ManagedModeURLFilter* url_filter =
      managed_user_service->GetURLFilterForUIThread();
  if (url_filter->GetFilteringBehaviorForURL(url) ==
          ManagedModeURLFilter::BLOCK) {
    return false;
  }
#endif
  return true;
}

}  // namespace

// Negative start-margin values prevent the "es_sm" parameter from being used.
const int kDisableStartMargin = -1;

bool IsInstantExtendedAPIEnabled() {
#if defined(OS_IOS) || defined(OS_ANDROID)
  return false;
#else
  return true;
#endif  // defined(OS_IOS) || defined(OS_ANDROID)
}

// Determine what embedded search page version to request from the user's
// default search provider. If 0, the embedded search UI should not be enabled.
uint64 EmbeddedSearchPageVersion() {
  FieldTrialFlags flags;
  if (GetFieldTrialInfo(&flags)) {
    return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
                                            kEmbeddedPageVersionDefault,
                                            flags);
  }
  return kEmbeddedPageVersionDefault;
}

bool IsQueryExtractionEnabled() {
#if defined(OS_IOS) || defined(OS_ANDROID)
  return true;
#else
  if (!IsInstantExtendedAPIEnabled())
    return false;

  const CommandLine* command_line = CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kEnableQueryExtraction))
    return true;

  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kEnableQueryExtractionFlagName, false, flags);
#endif  // defined(OS_IOS) || defined(OS_ANDROID)
}

base::string16 GetSearchTermsFromURL(Profile* profile, const GURL& url) {
  if (url.is_valid() && url == GetSearchResultPrefetchBaseURL(profile)) {
    // InstantSearchPrerenderer has the search query for the Instant search base
    // page.
    InstantSearchPrerenderer* prerenderer =
        InstantSearchPrerenderer::GetForProfile(profile);
    DCHECK(prerenderer);
    return prerenderer->get_last_query();
  }

  base::string16 search_terms;
  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  if (template_url && IsSuitableURLForInstant(url, template_url))
    template_url->ExtractSearchTermsFromURL(url, &search_terms);
  return search_terms;
}

base::string16 GetSearchTermsFromNavigationEntry(
    const content::NavigationEntry* entry) {
  base::string16 search_terms;
  if (entry)
    entry->GetExtraData(sessions::kSearchTermsKey, &search_terms);
  return search_terms;
}

base::string16 GetSearchTerms(const content::WebContents* contents) {
  if (!contents)
    return base::string16();

  const content::NavigationEntry* entry =
      contents->GetController().GetVisibleEntry();
  if (!entry)
    return base::string16();

#if !defined(OS_IOS) && !defined(OS_ANDROID)
  // iOS and Android doesn't use the Instant framework, disable this check for
  // the two platforms.
  InstantSupportState state = GetInstantSupportStateFromNavigationEntry(*entry);
  if (state == INSTANT_SUPPORT_NO)
    return base::string16();
#endif  // !defined(OS_IOS) && !defined(OS_ANDROID)

  return GetSearchTermsImpl(contents, entry);
}

bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) {
  return url.is_valid() &&
         profile &&
         IsInstantExtendedAPIEnabled() &&
         (url.SchemeIs(chrome::kChromeSearchScheme) ||
          IsInstantURL(url, profile));
}

bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile) {
  return ShouldAssignURLToInstantRenderer(url, profile) &&
      (url.host() == chrome::kChromeSearchLocalNtpHost ||
       url.host() == chrome::kChromeSearchOnlineNtpHost);
}

bool IsNTPURL(const GURL& url, Profile* profile) {
  if (!url.is_valid())
    return false;

  if (!IsInstantExtendedAPIEnabled())
    return url == GURL(chrome::kChromeUINewTabURL);

  return profile &&
      ((IsInstantURL(url, profile) &&
        GetSearchTermsFromURL(profile, url).empty()) ||
       url == GURL(chrome::kChromeSearchLocalNtpUrl));
}

bool IsInstantNTP(const content::WebContents* contents) {
  if (!contents)
    return false;

  return NavEntryIsInstantNTP(contents,
                              contents->GetController().GetVisibleEntry());
}

bool NavEntryIsInstantNTP(const content::WebContents* contents,
                          const content::NavigationEntry* entry) {
  if (!contents || !entry || !IsInstantExtendedAPIEnabled())
    return false;

  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
  if (!IsRenderedInInstantProcess(contents, profile))
    return false;

  if (entry->GetURL() == GetLocalInstantURL(profile))
    return true;

  if (ShouldUseCacheableNTP()) {
    GURL new_tab_url(GetNewTabPageURL(profile));
    return new_tab_url.is_valid() &&
        search::MatchesOriginAndPath(entry->GetURL(), new_tab_url);
  }

  return IsInstantURL(entry->GetVirtualURL(), profile) &&
      GetSearchTermsImpl(contents, entry).empty();
}

bool IsSuggestPrefEnabled(Profile* profile) {
  return profile && !profile->IsOffTheRecord() && profile->GetPrefs() &&
         profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled);
}

GURL GetInstantURL(Profile* profile, int start_margin,
                   bool force_instant_results) {
  if (!IsInstantExtendedAPIEnabled() || !IsSuggestPrefEnabled(profile))
    return GURL();

  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  if (!template_url)
    return GURL();

  GURL instant_url =
      TemplateURLRefToGURL(template_url->instant_url_ref(), start_margin, true,
                           force_instant_results);
  if (!instant_url.is_valid() ||
      !template_url->HasSearchTermsReplacementKey(instant_url))
    return GURL();

  // Extended mode requires HTTPS.  Force it unless the base URL was overridden
  // on the command line, in which case we allow HTTP (see comments on
  // IsSuitableURLForInstant()).
  if (!instant_url.SchemeIsSecure() &&
      !google_util::StartsWithCommandLineGoogleBaseURL(instant_url)) {
    GURL::Replacements replacements;
    const std::string secure_scheme(content::kHttpsScheme);
    replacements.SetSchemeStr(secure_scheme);
    instant_url = instant_url.ReplaceComponents(replacements);
  }

  if (!IsURLAllowedForSupervisedUser(instant_url, profile))
    return GURL();

  return instant_url;
}

// Returns URLs associated with the default search engine for |profile|.
std::vector<GURL> GetSearchURLs(Profile* profile) {
  std::vector<GURL> result;
  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  if (!template_url)
    return result;
  for (size_t i = 0; i < template_url->URLCount(); ++i) {
    TemplateURLRef ref(template_url, i);
    result.push_back(TemplateURLRefToGURL(ref, kDisableStartMargin, false,
                                          false));
  }
  return result;
}

GURL GetNewTabPageURL(Profile* profile) {
  if (!ShouldUseCacheableNTP())
    return GURL();

  if (!profile || profile->IsOffTheRecord())
    return GURL();

  if (!IsSuggestPrefEnabled(profile))
    return GURL(chrome::kChromeSearchLocalNtpUrl);

  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  if (!template_url)
    return GURL(chrome::kChromeSearchLocalNtpUrl);

  GURL url(TemplateURLRefToGURL(template_url->new_tab_url_ref(),
                                kDisableStartMargin, false, false));
  if (!url.is_valid() || !url.SchemeIsSecure())
    return GURL(chrome::kChromeSearchLocalNtpUrl);

  if (!IsURLAllowedForSupervisedUser(url, profile))
    return GURL(chrome::kChromeSearchLocalNtpUrl);

  return url;
}

GURL GetSearchResultPrefetchBaseURL(Profile* profile) {
  return ShouldPrefetchSearchResults() ?
      GetInstantURL(profile, kDisableStartMargin, true) : GURL();
}

bool ShouldPrefetchSearchResults() {
  if (!ShouldUseCacheableNTP())
    return false;

  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kPrefetchSearchResultsFlagName, false, flags);
}

GURL GetLocalInstantURL(Profile* profile) {
  return GURL(chrome::kChromeSearchLocalNtpUrl);
}

bool ShouldHideTopVerbatimMatch() {
  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kHideVerbatimFlagName, false, flags);
}

bool ShouldUseCacheableNTP() {
  FieldTrialFlags flags;
  return !GetFieldTrialInfo(&flags) || GetBoolValueForFlagWithDefault(
      kUseCacheableNTP, true, flags);
}

bool ShouldShowInstantNTP() {
  // If using the cacheable NTP, load the NTP directly instead of preloading its
  // contents using InstantNTP.
  if (ShouldUseCacheableNTP())
    return false;

  FieldTrialFlags flags;
  return !GetFieldTrialInfo(&flags) ||
      GetBoolValueForFlagWithDefault(kShowNtpFlagName, true, flags);
}

DisplaySearchButtonConditions GetDisplaySearchButtonConditions() {
  const CommandLine* cl = CommandLine::ForCurrentProcess();
  if (cl->HasSwitch(switches::kDisableSearchButtonInOmnibox)) {
    return DISPLAY_SEARCH_BUTTON_NEVER;
  } else if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStr)) {
    return DISPLAY_SEARCH_BUTTON_FOR_STR;
  } else if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStrOrIip)) {
    return DISPLAY_SEARCH_BUTTON_FOR_STR_OR_IIP;
  } else if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxAlways)) {
    return DISPLAY_SEARCH_BUTTON_ALWAYS;
  }

  FieldTrialFlags flags;
  if (!GetFieldTrialInfo(&flags))
    return DISPLAY_SEARCH_BUTTON_NEVER;
  uint64 value =
      GetUInt64ValueForFlagWithDefault(kDisplaySearchButtonFlagName, 0, flags);
  return (value < DISPLAY_SEARCH_BUTTON_NUM_VALUES) ?
      static_cast<DisplaySearchButtonConditions>(value) :
      DISPLAY_SEARCH_BUTTON_NEVER;
}

bool ShouldDisplayOriginChip() {
  const CommandLine* cl = CommandLine::ForCurrentProcess();
  if (cl->HasSwitch(switches::kDisableOriginChip)) {
    return false;
  } else if (cl->HasSwitch(switches::kEnableOriginChip)) {
    return true;
  }

  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kEnableOriginChipFlagName, false, flags);
}

GURL GetEffectiveURLForInstant(const GURL& url, Profile* profile) {
  CHECK(ShouldAssignURLToInstantRenderer(url, profile))
      << "Error granting Instant access.";

  if (url.SchemeIs(chrome::kChromeSearchScheme))
    return url;

  GURL effective_url(url);

  // Replace the scheme with "chrome-search:".
  url_canon::Replacements<char> replacements;
  std::string search_scheme(chrome::kChromeSearchScheme);
  replacements.SetScheme(search_scheme.data(),
                         url_parse::Component(0, search_scheme.length()));

  // If the URL corresponds to an online NTP, replace the host with
  // "online-ntp".
  std::string online_ntp_host(chrome::kChromeSearchOnlineNtpHost);
  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  if (template_url) {
    const GURL instant_url = TemplateURLRefToGURL(
        template_url->instant_url_ref(), kDisableStartMargin, false, false);
    if (instant_url.is_valid() &&
        search::MatchesOriginAndPath(url, instant_url)) {
      replacements.SetHost(online_ntp_host.c_str(),
                           url_parse::Component(0, online_ntp_host.length()));
    }
  }

  effective_url = effective_url.ReplaceComponents(replacements);
  return effective_url;
}

int GetInstantLoaderStalenessTimeoutSec() {
  int timeout_sec = kStalePageTimeoutDefault;
  FieldTrialFlags flags;
  if (GetFieldTrialInfo(&flags)) {
    timeout_sec = GetUInt64ValueForFlagWithDefault(kStalePageTimeoutFlagName,
                                                   kStalePageTimeoutDefault,
                                                   flags);
  }

  // Require a minimum 5 minute timeout.
  if (timeout_sec < 0 || (timeout_sec > 0 && timeout_sec < 300))
    timeout_sec = kStalePageTimeoutDefault;

  // Randomize by upto 15% either side.
  timeout_sec = base::RandInt(timeout_sec * 0.85, timeout_sec * 1.15);

  return timeout_sec;
}

bool IsPreloadedInstantExtendedNTP(const content::WebContents* contents) {
  if (!IsInstantExtendedAPIEnabled())
    return false;

  ProfileManager* profile_manager = g_browser_process->profile_manager();
  if (!profile_manager)
    return false;  // The profile manager can be NULL while testing.

  const std::vector<Profile*>& profiles = profile_manager->GetLoadedProfiles();
  for (size_t i = 0; i < profiles.size(); ++i) {
    const InstantService* instant_service =
        InstantServiceFactory::GetForProfile(profiles[i]);
    if (instant_service && instant_service->GetNTPContents() == contents)
      return true;
  }
  return false;
}

bool HandleNewTabURLRewrite(GURL* url,
                            content::BrowserContext* browser_context) {
  if (!IsInstantExtendedAPIEnabled())
    return false;

  if (!url->SchemeIs(chrome::kChromeUIScheme) ||
      url->host() != chrome::kChromeUINewTabHost)
    return false;

  Profile* profile = Profile::FromBrowserContext(browser_context);
  GURL new_tab_url(GetNewTabPageURL(profile));
  if (!new_tab_url.is_valid())
    return false;

  *url = new_tab_url;
  return true;
}

bool HandleNewTabURLReverseRewrite(GURL* url,
                                   content::BrowserContext* browser_context) {
  if (!IsInstantExtendedAPIEnabled())
    return false;

  Profile* profile = Profile::FromBrowserContext(browser_context);
  GURL new_tab_url(GetNewTabPageURL(profile));
  if (!new_tab_url.is_valid() ||
      !search::MatchesOriginAndPath(new_tab_url, *url))
    return false;

  *url = GURL(chrome::kChromeUINewTabURL);
  return true;
}

void SetInstantSupportStateInNavigationEntry(InstantSupportState state,
                                             content::NavigationEntry* entry) {
  if (!entry)
    return;

  entry->SetExtraData(kInstantSupportStateKey,
                      InstantSupportStateToString(state));
}

InstantSupportState GetInstantSupportStateFromNavigationEntry(
    const content::NavigationEntry& entry) {
  base::string16 value;
  if (!entry.GetExtraData(kInstantSupportStateKey, &value))
    return INSTANT_SUPPORT_UNKNOWN;

  return StringToInstantSupportState(value);
}

bool ShouldPrefetchSearchResultsOnSRP() {
  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kPrefetchSearchResultsOnSRP, false, flags);
}

void EnableQueryExtractionForTesting() {
  CommandLine* cl = CommandLine::ForCurrentProcess();
  cl->AppendSwitch(switches::kEnableQueryExtraction);
}

bool GetFieldTrialInfo(FieldTrialFlags* flags) {
  // Get the group name.  If the EmbeddedSearch trial doesn't exist, look for
  // the older InstantExtended name.
  std::string group_name = base::FieldTrialList::FindFullName(
      kEmbeddedSearchFieldTrialName);
  if (group_name.empty()) {
    group_name = base::FieldTrialList::FindFullName(
        kInstantExtendedFieldTrialName);
  }

  if (EndsWith(group_name, kDisablingSuffix, true))
    return false;

  // We have a valid trial that isn't disabled. Extract the flags.
  std::string group_prefix(group_name);
  size_t first_space = group_name.find(" ");
  if (first_space != std::string::npos) {
    // There is a flags section of the group name. Split that out and parse it.
    group_prefix = group_name.substr(0, first_space);
    if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space),
                                            ':', ' ', flags)) {
      // Failed to parse the flags section. Assume the whole group name is
      // invalid.
      return false;
    }
  }
  return true;
}

// Given a FieldTrialFlags object, returns the string value of the provided
// flag.
std::string GetStringValueForFlagWithDefault(const std::string& flag,
                                             const std::string& default_value,
                                             const FieldTrialFlags& flags) {
  FieldTrialFlags::const_iterator i;
  for (i = flags.begin(); i != flags.end(); i++) {
    if (i->first == flag)
      return i->second;
  }
  return default_value;
}

// Given a FieldTrialFlags object, returns the uint64 value of the provided
// flag.
uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag,
                                        uint64 default_value,
                                        const FieldTrialFlags& flags) {
  uint64 value;
  std::string str_value =
      GetStringValueForFlagWithDefault(flag, std::string(), flags);
  if (base::StringToUint64(str_value, &value))
    return value;
  return default_value;
}

// Given a FieldTrialFlags object, returns the boolean value of the provided
// flag.
bool GetBoolValueForFlagWithDefault(const std::string& flag,
                                    bool default_value,
                                    const FieldTrialFlags& flags) {
  return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
}

}  // namespace chrome