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