Home | History | Annotate | Download | only in search
      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/prefs/pref_service.h"
     10 #include "base/rand_util.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/google/google_util.h"
     16 #include "chrome/browser/profiles/profile.h"
     17 #include "chrome/browser/profiles/profile_manager.h"
     18 #include "chrome/browser/search/instant_service.h"
     19 #include "chrome/browser/search/instant_service_factory.h"
     20 #include "chrome/browser/search_engines/template_url_service.h"
     21 #include "chrome/browser/search_engines/template_url_service_factory.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/sessions/serialized_navigation_entry.h"
     31 #include "components/user_prefs/pref_registry_syncable.h"
     32 #include "content/public/browser/navigation_entry.h"
     33 #include "content/public/browser/render_process_host.h"
     34 #include "content/public/browser/web_contents.h"
     35 #include "grit/generated_resources.h"
     36 #include "ui/base/l10n/l10n_util.h"
     37 
     38 #if defined(ENABLE_MANAGED_USERS)
     39 #include "chrome/browser/managed_mode/managed_mode_url_filter.h"
     40 #include "chrome/browser/managed_mode/managed_user_service.h"
     41 #include "chrome/browser/managed_mode/managed_user_service_factory.h"
     42 #endif
     43 
     44 namespace chrome {
     45 
     46 namespace {
     47 
     48 // Configuration options for Embedded Search.
     49 // EmbeddedSearch field trials are named in such a way that we can parse out
     50 // the experiment configuration from the trial's group name in order to give
     51 // us maximum flexability in running experiments.
     52 // Field trial groups should be named things like "Group7 espv:2 instant:1".
     53 // The first token is always GroupN for some integer N, followed by a
     54 // space-delimited list of key:value pairs which correspond to these flags:
     55 const char kEmbeddedPageVersionFlagName[] = "espv";
     56 #if defined(OS_IOS) || defined(OS_ANDROID)
     57 const uint64 kEmbeddedPageVersionDefault = 1;
     58 #else
     59 const uint64 kEmbeddedPageVersionDefault = 2;
     60 #endif
     61 
     62 // The staleness timeout can be set (in seconds) via this config.
     63 const char kStalePageTimeoutFlagName[] = "stale";
     64 const int kStalePageTimeoutDefault = 3 * 3600;  // 3 hours.
     65 
     66 const char kHideVerbatimFlagName[] = "hide_verbatim";
     67 const char kShowNtpFlagName[] = "show_ntp";
     68 const char kUseCacheableNTP[] = "use_cacheable_ntp";
     69 const char kPrefetchSearchResultsFlagName[] = "prefetch_results";
     70 const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp";
     71 const char kDisplaySearchButtonFlagName[] = "display_search_button";
     72 const char kEnableOriginChipFlagName[] = "origin_chip";
     73 #if !defined(OS_IOS) && !defined(OS_ANDROID)
     74 const char kEnableQueryExtractionFlagName[] = "query_extraction";
     75 #endif
     76 
     77 // Constants for the field trial name and group prefix.
     78 // Note in M30 and below this field trial was named "InstantExtended" and in
     79 // M31 was renamed to EmbeddedSearch for clarity and cleanliness.  Since we
     80 // can't easilly sync up Finch configs with the pushing of this change to
     81 // Dev & Canary, for now the code accepts both names.
     82 // TODO(dcblack): Remove the InstantExtended name once M31 hits the Beta
     83 // channel.
     84 const char kInstantExtendedFieldTrialName[] = "InstantExtended";
     85 const char kEmbeddedSearchFieldTrialName[] = "EmbeddedSearch";
     86 
     87 // If the field trial's group name ends with this string its configuration will
     88 // be ignored and Instant Extended will not be enabled by default.
     89 const char kDisablingSuffix[] = "DISABLED";
     90 
     91 // Used to set the Instant support state of the Navigation entry.
     92 const char kInstantSupportStateKey[] = "instant_support_state";
     93 
     94 const char kInstantSupportEnabled[] = "Instant support enabled";
     95 const char kInstantSupportDisabled[] = "Instant support disabled";
     96 const char kInstantSupportUnknown[] = "Instant support unknown";
     97 
     98 InstantSupportState StringToInstantSupportState(const base::string16& value) {
     99   if (value == ASCIIToUTF16(kInstantSupportEnabled))
    100     return INSTANT_SUPPORT_YES;
    101   else if (value == ASCIIToUTF16(kInstantSupportDisabled))
    102     return INSTANT_SUPPORT_NO;
    103   else
    104     return INSTANT_SUPPORT_UNKNOWN;
    105 }
    106 
    107 base::string16 InstantSupportStateToString(InstantSupportState state) {
    108   switch (state) {
    109     case INSTANT_SUPPORT_NO:
    110       return ASCIIToUTF16(kInstantSupportDisabled);
    111     case INSTANT_SUPPORT_YES:
    112       return ASCIIToUTF16(kInstantSupportEnabled);
    113     case INSTANT_SUPPORT_UNKNOWN:
    114       return ASCIIToUTF16(kInstantSupportUnknown);
    115   }
    116   return ASCIIToUTF16(kInstantSupportUnknown);
    117 }
    118 
    119 TemplateURL* GetDefaultSearchProviderTemplateURL(Profile* profile) {
    120   TemplateURLService* template_url_service =
    121       TemplateURLServiceFactory::GetForProfile(profile);
    122   if (template_url_service)
    123     return template_url_service->GetDefaultSearchProvider();
    124   return NULL;
    125 }
    126 
    127 GURL TemplateURLRefToGURL(const TemplateURLRef& ref,
    128                           int start_margin,
    129                           bool append_extra_query_params,
    130                           bool force_instant_results) {
    131   TemplateURLRef::SearchTermsArgs search_terms_args =
    132       TemplateURLRef::SearchTermsArgs(base::string16());
    133   search_terms_args.omnibox_start_margin = start_margin;
    134   search_terms_args.append_extra_query_params = append_extra_query_params;
    135   search_terms_args.force_instant_results = force_instant_results;
    136   return GURL(ref.ReplaceSearchTerms(search_terms_args));
    137 }
    138 
    139 bool MatchesAnySearchURL(const GURL& url, TemplateURL* template_url) {
    140   GURL search_url =
    141       TemplateURLRefToGURL(template_url->url_ref(), kDisableStartMargin, false,
    142                            false);
    143   if (search_url.is_valid() &&
    144       search::MatchesOriginAndPath(url, search_url))
    145     return true;
    146 
    147   // "URLCount() - 1" because we already tested url_ref above.
    148   for (size_t i = 0; i < template_url->URLCount() - 1; ++i) {
    149     TemplateURLRef ref(template_url, i);
    150     search_url = TemplateURLRefToGURL(ref, kDisableStartMargin, false, false);
    151     if (search_url.is_valid() &&
    152         search::MatchesOriginAndPath(url, search_url))
    153       return true;
    154   }
    155 
    156   return false;
    157 }
    158 
    159 // Returns true if |contents| is rendered inside the Instant process for
    160 // |profile|.
    161 bool IsRenderedInInstantProcess(const content::WebContents* contents,
    162                                 Profile* profile) {
    163   const content::RenderProcessHost* process_host =
    164       contents->GetRenderProcessHost();
    165   if (!process_host)
    166     return false;
    167 
    168   const InstantService* instant_service =
    169       InstantServiceFactory::GetForProfile(profile);
    170   if (!instant_service)
    171     return false;
    172 
    173   return instant_service->IsInstantProcess(process_host->GetID());
    174 }
    175 
    176 // Returns true if |url| passes some basic checks that must succeed for it to be
    177 // usable as an instant URL:
    178 // (1) It contains the search terms replacement key of |template_url|, which is
    179 //     expected to be the TemplateURL* for the default search provider.
    180 // (2) Either it has a secure scheme, or else the user has manually specified a
    181 //     --google-base-url and it uses that base URL.  (This allows testers to use
    182 //     --google-base-url to point at non-HTTPS servers, which eases testing.)
    183 bool IsSuitableURLForInstant(const GURL& url, const TemplateURL* template_url) {
    184   return template_url->HasSearchTermsReplacementKey(url) &&
    185       (url.SchemeIsSecure() ||
    186        google_util::StartsWithCommandLineGoogleBaseURL(url));
    187 }
    188 
    189 // Returns true if |url| can be used as an Instant URL for |profile|.
    190 bool IsInstantURL(const GURL& url, Profile* profile) {
    191   if (!IsInstantExtendedAPIEnabled())
    192     return false;
    193 
    194   if (!url.is_valid())
    195     return false;
    196 
    197   const GURL new_tab_url(GetNewTabPageURL(profile));
    198   if (new_tab_url.is_valid() &&
    199       search::MatchesOriginAndPath(url, new_tab_url))
    200     return true;
    201 
    202   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
    203   if (!template_url)
    204     return false;
    205 
    206   if (!IsSuitableURLForInstant(url, template_url))
    207     return false;
    208 
    209   const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
    210   const GURL instant_url =
    211       TemplateURLRefToGURL(instant_url_ref, kDisableStartMargin, false, false);
    212   if (!instant_url.is_valid())
    213     return false;
    214 
    215   if (search::MatchesOriginAndPath(url, instant_url))
    216     return true;
    217 
    218   return IsQueryExtractionEnabled() && MatchesAnySearchURL(url, template_url);
    219 }
    220 
    221 base::string16 GetSearchTermsImpl(const content::WebContents* contents,
    222                                   const content::NavigationEntry* entry) {
    223   if (!contents || !IsQueryExtractionEnabled())
    224     return base::string16();
    225 
    226   // For security reasons, don't extract search terms if the page is not being
    227   // rendered in the privileged Instant renderer process. This is to protect
    228   // against a malicious page somehow scripting the search results page and
    229   // faking search terms in the URL. Random pages can't get into the Instant
    230   // renderer and scripting doesn't work cross-process, so if the page is in
    231   // the Instant process, we know it isn't being exploited.
    232   // Since iOS and Android doesn't use the instant framework, these checks are
    233   // disabled for the two platforms.
    234   Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
    235 #if !defined(OS_IOS) && !defined(OS_ANDROID)
    236   if (!IsRenderedInInstantProcess(contents, profile) &&
    237       ((entry == contents->GetController().GetLastCommittedEntry()) ||
    238        !ShouldAssignURLToInstantRenderer(entry->GetURL(), profile)))
    239     return base::string16();
    240 #endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
    241   // Check to see if search terms have already been extracted.
    242   base::string16 search_terms = GetSearchTermsFromNavigationEntry(entry);
    243   if (!search_terms.empty())
    244     return search_terms;
    245 
    246   // Otherwise, extract from the URL.
    247   return GetSearchTermsFromURL(profile, entry->GetVirtualURL());
    248 }
    249 
    250 bool IsURLAllowedForSupervisedUser(const GURL& url, Profile* profile) {
    251 #if defined(ENABLE_MANAGED_USERS)
    252   ManagedUserService* managed_user_service =
    253       ManagedUserServiceFactory::GetForProfile(profile);
    254   ManagedModeURLFilter* url_filter =
    255       managed_user_service->GetURLFilterForUIThread();
    256   if (url_filter->GetFilteringBehaviorForURL(url) ==
    257           ManagedModeURLFilter::BLOCK) {
    258     return false;
    259   }
    260 #endif
    261   return true;
    262 }
    263 
    264 }  // namespace
    265 
    266 // Negative start-margin values prevent the "es_sm" parameter from being used.
    267 const int kDisableStartMargin = -1;
    268 
    269 bool IsInstantExtendedAPIEnabled() {
    270 #if defined(OS_IOS) || defined(OS_ANDROID)
    271   return false;
    272 #else
    273   return true;
    274 #endif  // defined(OS_IOS) || defined(OS_ANDROID)
    275 }
    276 
    277 // Determine what embedded search page version to request from the user's
    278 // default search provider. If 0, the embedded search UI should not be enabled.
    279 uint64 EmbeddedSearchPageVersion() {
    280   FieldTrialFlags flags;
    281   if (GetFieldTrialInfo(&flags)) {
    282     return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
    283                                             kEmbeddedPageVersionDefault,
    284                                             flags);
    285   }
    286   return kEmbeddedPageVersionDefault;
    287 }
    288 
    289 bool IsQueryExtractionEnabled() {
    290 #if defined(OS_IOS) || defined(OS_ANDROID)
    291   return true;
    292 #else
    293   if (!IsInstantExtendedAPIEnabled())
    294     return false;
    295 
    296   const CommandLine* command_line = CommandLine::ForCurrentProcess();
    297   if (command_line->HasSwitch(switches::kEnableQueryExtraction))
    298     return true;
    299 
    300   FieldTrialFlags flags;
    301   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
    302       kEnableQueryExtractionFlagName, false, flags);
    303 #endif  // defined(OS_IOS) || defined(OS_ANDROID)
    304 }
    305 
    306 base::string16 GetSearchTermsFromURL(Profile* profile, const GURL& url) {
    307   if (url.is_valid() && url == GetSearchResultPrefetchBaseURL(profile)) {
    308     // InstantSearchPrerenderer has the search query for the Instant search base
    309     // page.
    310     InstantSearchPrerenderer* prerenderer =
    311         InstantSearchPrerenderer::GetForProfile(profile);
    312     DCHECK(prerenderer);
    313     return prerenderer->get_last_query();
    314   }
    315 
    316   base::string16 search_terms;
    317   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
    318   if (template_url && IsSuitableURLForInstant(url, template_url))
    319     template_url->ExtractSearchTermsFromURL(url, &search_terms);
    320   return search_terms;
    321 }
    322 
    323 base::string16 GetSearchTermsFromNavigationEntry(
    324     const content::NavigationEntry* entry) {
    325   base::string16 search_terms;
    326   if (entry)
    327     entry->GetExtraData(sessions::kSearchTermsKey, &search_terms);
    328   return search_terms;
    329 }
    330 
    331 base::string16 GetSearchTerms(const content::WebContents* contents) {
    332   if (!contents)
    333     return base::string16();
    334 
    335   const content::NavigationEntry* entry =
    336       contents->GetController().GetVisibleEntry();
    337   if (!entry)
    338     return base::string16();
    339 
    340 #if !defined(OS_IOS) && !defined(OS_ANDROID)
    341   // iOS and Android doesn't use the Instant framework, disable this check for
    342   // the two platforms.
    343   InstantSupportState state = GetInstantSupportStateFromNavigationEntry(*entry);
    344   if (state == INSTANT_SUPPORT_NO)
    345     return base::string16();
    346 #endif  // !defined(OS_IOS) && !defined(OS_ANDROID)
    347 
    348   return GetSearchTermsImpl(contents, entry);
    349 }
    350 
    351 bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) {
    352   return url.is_valid() &&
    353          profile &&
    354          IsInstantExtendedAPIEnabled() &&
    355          (url.SchemeIs(chrome::kChromeSearchScheme) ||
    356           IsInstantURL(url, profile));
    357 }
    358 
    359 bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile) {
    360   return ShouldAssignURLToInstantRenderer(url, profile) &&
    361       (url.host() == chrome::kChromeSearchLocalNtpHost ||
    362        url.host() == chrome::kChromeSearchOnlineNtpHost);
    363 }
    364 
    365 bool IsNTPURL(const GURL& url, Profile* profile) {
    366   if (!url.is_valid())
    367     return false;
    368 
    369   if (!IsInstantExtendedAPIEnabled())
    370     return url == GURL(chrome::kChromeUINewTabURL);
    371 
    372   return profile &&
    373       ((IsInstantURL(url, profile) &&
    374         GetSearchTermsFromURL(profile, url).empty()) ||
    375        url == GURL(chrome::kChromeSearchLocalNtpUrl));
    376 }
    377 
    378 bool IsInstantNTP(const content::WebContents* contents) {
    379   if (!contents)
    380     return false;
    381 
    382   return NavEntryIsInstantNTP(contents,
    383                               contents->GetController().GetVisibleEntry());
    384 }
    385 
    386 bool NavEntryIsInstantNTP(const content::WebContents* contents,
    387                           const content::NavigationEntry* entry) {
    388   if (!contents || !entry || !IsInstantExtendedAPIEnabled())
    389     return false;
    390 
    391   Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
    392   if (!IsRenderedInInstantProcess(contents, profile))
    393     return false;
    394 
    395   if (entry->GetURL() == GetLocalInstantURL(profile))
    396     return true;
    397 
    398   if (ShouldUseCacheableNTP()) {
    399     GURL new_tab_url(GetNewTabPageURL(profile));
    400     return new_tab_url.is_valid() &&
    401         search::MatchesOriginAndPath(entry->GetURL(), new_tab_url);
    402   }
    403 
    404   return IsInstantURL(entry->GetVirtualURL(), profile) &&
    405       GetSearchTermsImpl(contents, entry).empty();
    406 }
    407 
    408 bool IsSuggestPrefEnabled(Profile* profile) {
    409   return profile && !profile->IsOffTheRecord() && profile->GetPrefs() &&
    410          profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled);
    411 }
    412 
    413 GURL GetInstantURL(Profile* profile, int start_margin,
    414                    bool force_instant_results) {
    415   if (!IsInstantExtendedAPIEnabled() || !IsSuggestPrefEnabled(profile))
    416     return GURL();
    417 
    418   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
    419   if (!template_url)
    420     return GURL();
    421 
    422   GURL instant_url =
    423       TemplateURLRefToGURL(template_url->instant_url_ref(), start_margin, true,
    424                            force_instant_results);
    425   if (!instant_url.is_valid() ||
    426       !template_url->HasSearchTermsReplacementKey(instant_url))
    427     return GURL();
    428 
    429   // Extended mode requires HTTPS.  Force it unless the base URL was overridden
    430   // on the command line, in which case we allow HTTP (see comments on
    431   // IsSuitableURLForInstant()).
    432   if (!instant_url.SchemeIsSecure() &&
    433       !google_util::StartsWithCommandLineGoogleBaseURL(instant_url)) {
    434     GURL::Replacements replacements;
    435     const std::string secure_scheme(content::kHttpsScheme);
    436     replacements.SetSchemeStr(secure_scheme);
    437     instant_url = instant_url.ReplaceComponents(replacements);
    438   }
    439 
    440   if (!IsURLAllowedForSupervisedUser(instant_url, profile))
    441     return GURL();
    442 
    443   return instant_url;
    444 }
    445 
    446 // Returns URLs associated with the default search engine for |profile|.
    447 std::vector<GURL> GetSearchURLs(Profile* profile) {
    448   std::vector<GURL> result;
    449   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
    450   if (!template_url)
    451     return result;
    452   for (size_t i = 0; i < template_url->URLCount(); ++i) {
    453     TemplateURLRef ref(template_url, i);
    454     result.push_back(TemplateURLRefToGURL(ref, kDisableStartMargin, false,
    455                                           false));
    456   }
    457   return result;
    458 }
    459 
    460 GURL GetNewTabPageURL(Profile* profile) {
    461   if (!ShouldUseCacheableNTP())
    462     return GURL();
    463 
    464   if (!profile || profile->IsOffTheRecord())
    465     return GURL();
    466 
    467   if (!IsSuggestPrefEnabled(profile))
    468     return GURL(chrome::kChromeSearchLocalNtpUrl);
    469 
    470   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
    471   if (!template_url)
    472     return GURL(chrome::kChromeSearchLocalNtpUrl);
    473 
    474   GURL url(TemplateURLRefToGURL(template_url->new_tab_url_ref(),
    475                                 kDisableStartMargin, false, false));
    476   if (!url.is_valid() || !url.SchemeIsSecure())
    477     return GURL(chrome::kChromeSearchLocalNtpUrl);
    478 
    479   if (!IsURLAllowedForSupervisedUser(url, profile))
    480     return GURL(chrome::kChromeSearchLocalNtpUrl);
    481 
    482   return url;
    483 }
    484 
    485 GURL GetSearchResultPrefetchBaseURL(Profile* profile) {
    486   return ShouldPrefetchSearchResults() ?
    487       GetInstantURL(profile, kDisableStartMargin, true) : GURL();
    488 }
    489 
    490 bool ShouldPrefetchSearchResults() {
    491   if (!ShouldUseCacheableNTP())
    492     return false;
    493 
    494   FieldTrialFlags flags;
    495   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
    496       kPrefetchSearchResultsFlagName, false, flags);
    497 }
    498 
    499 GURL GetLocalInstantURL(Profile* profile) {
    500   return GURL(chrome::kChromeSearchLocalNtpUrl);
    501 }
    502 
    503 bool ShouldHideTopVerbatimMatch() {
    504   FieldTrialFlags flags;
    505   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
    506       kHideVerbatimFlagName, false, flags);
    507 }
    508 
    509 bool ShouldUseCacheableNTP() {
    510   FieldTrialFlags flags;
    511   return !GetFieldTrialInfo(&flags) || GetBoolValueForFlagWithDefault(
    512       kUseCacheableNTP, true, flags);
    513 }
    514 
    515 bool ShouldShowInstantNTP() {
    516   // If using the cacheable NTP, load the NTP directly instead of preloading its
    517   // contents using InstantNTP.
    518   if (ShouldUseCacheableNTP())
    519     return false;
    520 
    521   FieldTrialFlags flags;
    522   return !GetFieldTrialInfo(&flags) ||
    523       GetBoolValueForFlagWithDefault(kShowNtpFlagName, true, flags);
    524 }
    525 
    526 DisplaySearchButtonConditions GetDisplaySearchButtonConditions() {
    527   const CommandLine* cl = CommandLine::ForCurrentProcess();
    528   if (cl->HasSwitch(switches::kDisableSearchButtonInOmnibox)) {
    529     return DISPLAY_SEARCH_BUTTON_NEVER;
    530   } else if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStr)) {
    531     return DISPLAY_SEARCH_BUTTON_FOR_STR;
    532   } else if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStrOrIip)) {
    533     return DISPLAY_SEARCH_BUTTON_FOR_STR_OR_IIP;
    534   } else if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxAlways)) {
    535     return DISPLAY_SEARCH_BUTTON_ALWAYS;
    536   }
    537 
    538   FieldTrialFlags flags;
    539   if (!GetFieldTrialInfo(&flags))
    540     return DISPLAY_SEARCH_BUTTON_NEVER;
    541   uint64 value =
    542       GetUInt64ValueForFlagWithDefault(kDisplaySearchButtonFlagName, 0, flags);
    543   return (value < DISPLAY_SEARCH_BUTTON_NUM_VALUES) ?
    544       static_cast<DisplaySearchButtonConditions>(value) :
    545       DISPLAY_SEARCH_BUTTON_NEVER;
    546 }
    547 
    548 bool ShouldDisplayOriginChip() {
    549   const CommandLine* cl = CommandLine::ForCurrentProcess();
    550   if (cl->HasSwitch(switches::kDisableOriginChip)) {
    551     return false;
    552   } else if (cl->HasSwitch(switches::kEnableOriginChip)) {
    553     return true;
    554   }
    555 
    556   FieldTrialFlags flags;
    557   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
    558       kEnableOriginChipFlagName, false, flags);
    559 }
    560 
    561 GURL GetEffectiveURLForInstant(const GURL& url, Profile* profile) {
    562   CHECK(ShouldAssignURLToInstantRenderer(url, profile))
    563       << "Error granting Instant access.";
    564 
    565   if (url.SchemeIs(chrome::kChromeSearchScheme))
    566     return url;
    567 
    568   GURL effective_url(url);
    569 
    570   // Replace the scheme with "chrome-search:".
    571   url_canon::Replacements<char> replacements;
    572   std::string search_scheme(chrome::kChromeSearchScheme);
    573   replacements.SetScheme(search_scheme.data(),
    574                          url_parse::Component(0, search_scheme.length()));
    575 
    576   // If the URL corresponds to an online NTP, replace the host with
    577   // "online-ntp".
    578   std::string online_ntp_host(chrome::kChromeSearchOnlineNtpHost);
    579   TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
    580   if (template_url) {
    581     const GURL instant_url = TemplateURLRefToGURL(
    582         template_url->instant_url_ref(), kDisableStartMargin, false, false);
    583     if (instant_url.is_valid() &&
    584         search::MatchesOriginAndPath(url, instant_url)) {
    585       replacements.SetHost(online_ntp_host.c_str(),
    586                            url_parse::Component(0, online_ntp_host.length()));
    587     }
    588   }
    589 
    590   effective_url = effective_url.ReplaceComponents(replacements);
    591   return effective_url;
    592 }
    593 
    594 int GetInstantLoaderStalenessTimeoutSec() {
    595   int timeout_sec = kStalePageTimeoutDefault;
    596   FieldTrialFlags flags;
    597   if (GetFieldTrialInfo(&flags)) {
    598     timeout_sec = GetUInt64ValueForFlagWithDefault(kStalePageTimeoutFlagName,
    599                                                    kStalePageTimeoutDefault,
    600                                                    flags);
    601   }
    602 
    603   // Require a minimum 5 minute timeout.
    604   if (timeout_sec < 0 || (timeout_sec > 0 && timeout_sec < 300))
    605     timeout_sec = kStalePageTimeoutDefault;
    606 
    607   // Randomize by upto 15% either side.
    608   timeout_sec = base::RandInt(timeout_sec * 0.85, timeout_sec * 1.15);
    609 
    610   return timeout_sec;
    611 }
    612 
    613 bool IsPreloadedInstantExtendedNTP(const content::WebContents* contents) {
    614   if (!IsInstantExtendedAPIEnabled())
    615     return false;
    616 
    617   ProfileManager* profile_manager = g_browser_process->profile_manager();
    618   if (!profile_manager)
    619     return false;  // The profile manager can be NULL while testing.
    620 
    621   const std::vector<Profile*>& profiles = profile_manager->GetLoadedProfiles();
    622   for (size_t i = 0; i < profiles.size(); ++i) {
    623     const InstantService* instant_service =
    624         InstantServiceFactory::GetForProfile(profiles[i]);
    625     if (instant_service && instant_service->GetNTPContents() == contents)
    626       return true;
    627   }
    628   return false;
    629 }
    630 
    631 bool HandleNewTabURLRewrite(GURL* url,
    632                             content::BrowserContext* browser_context) {
    633   if (!IsInstantExtendedAPIEnabled())
    634     return false;
    635 
    636   if (!url->SchemeIs(chrome::kChromeUIScheme) ||
    637       url->host() != chrome::kChromeUINewTabHost)
    638     return false;
    639 
    640   Profile* profile = Profile::FromBrowserContext(browser_context);
    641   GURL new_tab_url(GetNewTabPageURL(profile));
    642   if (!new_tab_url.is_valid())
    643     return false;
    644 
    645   *url = new_tab_url;
    646   return true;
    647 }
    648 
    649 bool HandleNewTabURLReverseRewrite(GURL* url,
    650                                    content::BrowserContext* browser_context) {
    651   if (!IsInstantExtendedAPIEnabled())
    652     return false;
    653 
    654   Profile* profile = Profile::FromBrowserContext(browser_context);
    655   GURL new_tab_url(GetNewTabPageURL(profile));
    656   if (!new_tab_url.is_valid() ||
    657       !search::MatchesOriginAndPath(new_tab_url, *url))
    658     return false;
    659 
    660   *url = GURL(chrome::kChromeUINewTabURL);
    661   return true;
    662 }
    663 
    664 void SetInstantSupportStateInNavigationEntry(InstantSupportState state,
    665                                              content::NavigationEntry* entry) {
    666   if (!entry)
    667     return;
    668 
    669   entry->SetExtraData(kInstantSupportStateKey,
    670                       InstantSupportStateToString(state));
    671 }
    672 
    673 InstantSupportState GetInstantSupportStateFromNavigationEntry(
    674     const content::NavigationEntry& entry) {
    675   base::string16 value;
    676   if (!entry.GetExtraData(kInstantSupportStateKey, &value))
    677     return INSTANT_SUPPORT_UNKNOWN;
    678 
    679   return StringToInstantSupportState(value);
    680 }
    681 
    682 bool ShouldPrefetchSearchResultsOnSRP() {
    683   FieldTrialFlags flags;
    684   return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
    685       kPrefetchSearchResultsOnSRP, false, flags);
    686 }
    687 
    688 void EnableQueryExtractionForTesting() {
    689   CommandLine* cl = CommandLine::ForCurrentProcess();
    690   cl->AppendSwitch(switches::kEnableQueryExtraction);
    691 }
    692 
    693 bool GetFieldTrialInfo(FieldTrialFlags* flags) {
    694   // Get the group name.  If the EmbeddedSearch trial doesn't exist, look for
    695   // the older InstantExtended name.
    696   std::string group_name = base::FieldTrialList::FindFullName(
    697       kEmbeddedSearchFieldTrialName);
    698   if (group_name.empty()) {
    699     group_name = base::FieldTrialList::FindFullName(
    700         kInstantExtendedFieldTrialName);
    701   }
    702 
    703   if (EndsWith(group_name, kDisablingSuffix, true))
    704     return false;
    705 
    706   // We have a valid trial that isn't disabled. Extract the flags.
    707   std::string group_prefix(group_name);
    708   size_t first_space = group_name.find(" ");
    709   if (first_space != std::string::npos) {
    710     // There is a flags section of the group name. Split that out and parse it.
    711     group_prefix = group_name.substr(0, first_space);
    712     if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space),
    713                                             ':', ' ', flags)) {
    714       // Failed to parse the flags section. Assume the whole group name is
    715       // invalid.
    716       return false;
    717     }
    718   }
    719   return true;
    720 }
    721 
    722 // Given a FieldTrialFlags object, returns the string value of the provided
    723 // flag.
    724 std::string GetStringValueForFlagWithDefault(const std::string& flag,
    725                                              const std::string& default_value,
    726                                              const FieldTrialFlags& flags) {
    727   FieldTrialFlags::const_iterator i;
    728   for (i = flags.begin(); i != flags.end(); i++) {
    729     if (i->first == flag)
    730       return i->second;
    731   }
    732   return default_value;
    733 }
    734 
    735 // Given a FieldTrialFlags object, returns the uint64 value of the provided
    736 // flag.
    737 uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag,
    738                                         uint64 default_value,
    739                                         const FieldTrialFlags& flags) {
    740   uint64 value;
    741   std::string str_value =
    742       GetStringValueForFlagWithDefault(flag, std::string(), flags);
    743   if (base::StringToUint64(str_value, &value))
    744     return value;
    745   return default_value;
    746 }
    747 
    748 // Given a FieldTrialFlags object, returns the boolean value of the provided
    749 // flag.
    750 bool GetBoolValueForFlagWithDefault(const std::string& flag,
    751                                     bool default_value,
    752                                     const FieldTrialFlags& flags) {
    753   return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
    754 }
    755 
    756 }  // namespace chrome
    757