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
44namespace chrome {
45
46namespace {
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:
55const char kEmbeddedPageVersionFlagName[] = "espv";
56#if defined(OS_IOS) || defined(OS_ANDROID)
57const uint64 kEmbeddedPageVersionDefault = 1;
58#else
59const uint64 kEmbeddedPageVersionDefault = 2;
60#endif
61
62// The staleness timeout can be set (in seconds) via this config.
63const char kStalePageTimeoutFlagName[] = "stale";
64const int kStalePageTimeoutDefault = 3 * 3600;  // 3 hours.
65
66const char kHideVerbatimFlagName[] = "hide_verbatim";
67const char kShowNtpFlagName[] = "show_ntp";
68const char kUseCacheableNTP[] = "use_cacheable_ntp";
69const char kPrefetchSearchResultsFlagName[] = "prefetch_results";
70const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp";
71const char kDisplaySearchButtonFlagName[] = "display_search_button";
72const char kEnableOriginChipFlagName[] = "origin_chip";
73#if !defined(OS_IOS) && !defined(OS_ANDROID)
74const 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.
84const char kInstantExtendedFieldTrialName[] = "InstantExtended";
85const 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.
89const char kDisablingSuffix[] = "DISABLED";
90
91// Used to set the Instant support state of the Navigation entry.
92const char kInstantSupportStateKey[] = "instant_support_state";
93
94const char kInstantSupportEnabled[] = "Instant support enabled";
95const char kInstantSupportDisabled[] = "Instant support disabled";
96const char kInstantSupportUnknown[] = "Instant support unknown";
97
98InstantSupportState 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
107base::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
119TemplateURL* 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
127GURL 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
139bool 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|.
161bool 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.)
183bool 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|.
190bool 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
221base::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
250bool 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.
267const int kDisableStartMargin = -1;
268
269bool 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.
279uint64 EmbeddedSearchPageVersion() {
280  FieldTrialFlags flags;
281  if (GetFieldTrialInfo(&flags)) {
282    return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
283                                            kEmbeddedPageVersionDefault,
284                                            flags);
285  }
286  return kEmbeddedPageVersionDefault;
287}
288
289bool 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
306base::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
323base::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
331base::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
351bool 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
359bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile) {
360  return ShouldAssignURLToInstantRenderer(url, profile) &&
361      (url.host() == chrome::kChromeSearchLocalNtpHost ||
362       url.host() == chrome::kChromeSearchOnlineNtpHost);
363}
364
365bool 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
378bool IsInstantNTP(const content::WebContents* contents) {
379  if (!contents)
380    return false;
381
382  return NavEntryIsInstantNTP(contents,
383                              contents->GetController().GetVisibleEntry());
384}
385
386bool 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
408bool IsSuggestPrefEnabled(Profile* profile) {
409  return profile && !profile->IsOffTheRecord() && profile->GetPrefs() &&
410         profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled);
411}
412
413GURL 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|.
447std::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
460GURL 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
485GURL GetSearchResultPrefetchBaseURL(Profile* profile) {
486  return ShouldPrefetchSearchResults() ?
487      GetInstantURL(profile, kDisableStartMargin, true) : GURL();
488}
489
490bool ShouldPrefetchSearchResults() {
491  if (!ShouldUseCacheableNTP())
492    return false;
493
494  FieldTrialFlags flags;
495  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
496      kPrefetchSearchResultsFlagName, false, flags);
497}
498
499GURL GetLocalInstantURL(Profile* profile) {
500  return GURL(chrome::kChromeSearchLocalNtpUrl);
501}
502
503bool ShouldHideTopVerbatimMatch() {
504  FieldTrialFlags flags;
505  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
506      kHideVerbatimFlagName, false, flags);
507}
508
509bool ShouldUseCacheableNTP() {
510  FieldTrialFlags flags;
511  return !GetFieldTrialInfo(&flags) || GetBoolValueForFlagWithDefault(
512      kUseCacheableNTP, true, flags);
513}
514
515bool 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
526DisplaySearchButtonConditions 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
548bool 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
561GURL 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
594int 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
613bool 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
631bool 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
649bool 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
664void SetInstantSupportStateInNavigationEntry(InstantSupportState state,
665                                             content::NavigationEntry* entry) {
666  if (!entry)
667    return;
668
669  entry->SetExtraData(kInstantSupportStateKey,
670                      InstantSupportStateToString(state));
671}
672
673InstantSupportState 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
682bool ShouldPrefetchSearchResultsOnSRP() {
683  FieldTrialFlags flags;
684  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
685      kPrefetchSearchResultsOnSRP, false, flags);
686}
687
688void EnableQueryExtractionForTesting() {
689  CommandLine* cl = CommandLine::ForCurrentProcess();
690  cl->AppendSwitch(switches::kEnableQueryExtraction);
691}
692
693bool 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.
724std::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.
737uint64 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.
750bool 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