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