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.
5 #include "chrome/browser/search/search.h"
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/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"
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"
48 // Configuration options for Embedded Search.
49 // EmbeddedSearch field trials are named in such a way that we can parse out
50 // the experiment configuration from the trial's group name in order to give
51 // us maximum flexability in running experiments.
52 // Field trial groups should be named things like "Group7 espv:2 instant:1".
53 // The first token is always GroupN for some integer N, followed by a
54 // space-delimited list of key:value pairs which correspond to these flags:
55 const char kEmbeddedPageVersionFlagName[] = "espv";
56 const uint64 kEmbeddedPageVersionDisabled = 0;
57 #if defined(OS_IOS) || defined(OS_ANDROID)
58 const uint64 kEmbeddedPageVersionDefault = 1;
60 const uint64 kEmbeddedPageVersionDefault = 2;
63 // The staleness timeout can be set (in seconds) via this config.
64 const char kStalePageTimeoutFlagName[] = "stale";
65 const int kStalePageTimeoutDefault = 3 * 3600; // 3 hours.
67 const char kHideVerbatimFlagName[] = "hide_verbatim";
68 const char kUseRemoteNTPOnStartupFlagName[] = "use_remote_ntp_on_startup";
69 const char kShowNtpFlagName[] = "show_ntp";
70 const char kRecentTabsOnNTPFlagName[] = "show_recent_tabs";
71 const char kUseCacheableNTP[] = "use_cacheable_ntp";
72 const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp";
73 const char kSuppressInstantExtendedOnSRPFlagName[] = "suppress_on_srp";
75 // Constants for the field trial name and group prefix.
76 // Note in M30 and below this field trial was named "InstantExtended" and in
77 // M31 was renamed to EmbeddedSearch for clarity and cleanliness. Since we
78 // can't easilly sync up Finch configs with the pushing of this change to
79 // Dev & Canary, for now the code accepts both names.
80 // TODO(dcblack): Remove the InstantExtended name once M31 hits the Beta
82 const char kInstantExtendedFieldTrialName[] = "InstantExtended";
83 const char kEmbeddedSearchFieldTrialName[] = "EmbeddedSearch";
84 const char kGroupNumberPrefix[] = "Group";
86 // If the field trial's group name ends with this string its configuration will
87 // be ignored and Instant Extended will not be enabled by default.
88 const char kDisablingSuffix[] = "DISABLED";
90 // Remember if we reported metrics about opt-in/out state.
91 bool instant_extended_opt_in_state_gate = false;
93 // Used to set the Instant support state of the Navigation entry.
94 const char kInstantSupportStateKey[] = "instant_support_state";
96 const char kInstantSupportEnabled[] = "Instant support enabled";
97 const char kInstantSupportDisabled[] = "Instant support disabled";
98 const char kInstantSupportUnknown[] = "Instant support unknown";
100 InstantSupportState StringToInstantSupportState(const string16& value) {
101 if (value == ASCIIToUTF16(kInstantSupportEnabled))
102 return INSTANT_SUPPORT_YES;
103 else if (value == ASCIIToUTF16(kInstantSupportDisabled))
104 return INSTANT_SUPPORT_NO;
106 return INSTANT_SUPPORT_UNKNOWN;
109 string16 InstantSupportStateToString(InstantSupportState state) {
111 case INSTANT_SUPPORT_NO:
112 return ASCIIToUTF16(kInstantSupportDisabled);
113 case INSTANT_SUPPORT_YES:
114 return ASCIIToUTF16(kInstantSupportEnabled);
115 case INSTANT_SUPPORT_UNKNOWN:
116 return ASCIIToUTF16(kInstantSupportUnknown);
118 return ASCIIToUTF16(kInstantSupportUnknown);
121 TemplateURL* GetDefaultSearchProviderTemplateURL(Profile* profile) {
122 TemplateURLService* template_url_service =
123 TemplateURLServiceFactory::GetForProfile(profile);
124 if (template_url_service)
125 return template_url_service->GetDefaultSearchProvider();
129 GURL TemplateURLRefToGURL(const TemplateURLRef& ref,
131 bool append_extra_query_params,
132 bool force_instant_results) {
133 TemplateURLRef::SearchTermsArgs search_terms_args =
134 TemplateURLRef::SearchTermsArgs(string16());
135 search_terms_args.omnibox_start_margin = start_margin;
136 search_terms_args.append_extra_query_params = append_extra_query_params;
137 search_terms_args.force_instant_results = force_instant_results;
138 return GURL(ref.ReplaceSearchTerms(search_terms_args));
141 bool MatchesAnySearchURL(const GURL& url, TemplateURL* template_url) {
143 TemplateURLRefToGURL(template_url->url_ref(), kDisableStartMargin, false,
145 if (search_url.is_valid() &&
146 search::MatchesOriginAndPath(url, search_url))
149 // "URLCount() - 1" because we already tested url_ref above.
150 for (size_t i = 0; i < template_url->URLCount() - 1; ++i) {
151 TemplateURLRef ref(template_url, i);
152 search_url = TemplateURLRefToGURL(ref, kDisableStartMargin, false, false);
153 if (search_url.is_valid() &&
154 search::MatchesOriginAndPath(url, search_url))
161 void RecordInstantExtendedOptInState() {
162 if (instant_extended_opt_in_state_gate)
165 instant_extended_opt_in_state_gate = true;
166 OptInState state = INSTANT_EXTENDED_NOT_SET;
167 const CommandLine* command_line = CommandLine::ForCurrentProcess();
168 if (command_line->HasSwitch(switches::kDisableInstantExtendedAPI))
169 state = INSTANT_EXTENDED_OPT_OUT;
170 else if (command_line->HasSwitch(switches::kEnableInstantExtendedAPI))
171 state = INSTANT_EXTENDED_OPT_IN;
173 UMA_HISTOGRAM_ENUMERATION("InstantExtended.NewOptInState", state,
174 INSTANT_EXTENDED_OPT_IN_STATE_ENUM_COUNT);
177 // Returns true if |contents| is rendered inside the Instant process for
179 bool IsRenderedInInstantProcess(const content::WebContents* contents,
181 const content::RenderProcessHost* process_host =
182 contents->GetRenderProcessHost();
186 const InstantService* instant_service =
187 InstantServiceFactory::GetForProfile(profile);
188 if (!instant_service)
191 return instant_service->IsInstantProcess(process_host->GetID());
194 // Returns true if |url| passes some basic checks that must succeed for it to be
195 // usable as an instant URL:
196 // (1) It contains the search terms replacement key of |template_url|, which is
197 // expected to be the TemplateURL* for the default search provider.
198 // (2) Either it has a secure scheme, or else the user has manually specified a
199 // --google-base-url and it uses that base URL. (This allows testers to use
200 // --google-base-url to point at non-HTTPS servers, which eases testing.)
201 bool IsSuitableURLForInstant(const GURL& url, const TemplateURL* template_url) {
202 return template_url->HasSearchTermsReplacementKey(url) &&
203 (url.SchemeIsSecure() ||
204 google_util::StartsWithCommandLineGoogleBaseURL(url));
207 // Returns true if |url| can be used as an Instant URL for |profile|.
208 bool IsInstantURL(const GURL& url, Profile* profile) {
209 if (!IsInstantExtendedAPIEnabled())
215 const GURL new_tab_url(GetNewTabPageURL(profile));
216 if (new_tab_url.is_valid() &&
217 search::MatchesOriginAndPath(url, new_tab_url))
220 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
224 if (!IsSuitableURLForInstant(url, template_url))
227 const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
228 const GURL instant_url =
229 TemplateURLRefToGURL(instant_url_ref, kDisableStartMargin, false, false);
230 if (!instant_url.is_valid())
233 if (search::MatchesOriginAndPath(url, instant_url))
236 return !ShouldSuppressInstantExtendedOnSRP() &&
237 MatchesAnySearchURL(url, template_url);
240 string16 GetSearchTermsImpl(const content::WebContents* contents,
241 const content::NavigationEntry* entry) {
242 if (!contents || !IsQueryExtractionEnabled())
245 // For security reasons, don't extract search terms if the page is not being
246 // rendered in the privileged Instant renderer process. This is to protect
247 // against a malicious page somehow scripting the search results page and
248 // faking search terms in the URL. Random pages can't get into the Instant
249 // renderer and scripting doesn't work cross-process, so if the page is in
250 // the Instant process, we know it isn't being exploited.
251 // Since iOS and Android doesn't use the instant framework, these checks are
252 // disabled for the two platforms.
253 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
254 #if !defined(OS_IOS) && !defined(OS_ANDROID)
255 if (!IsRenderedInInstantProcess(contents, profile) &&
256 ((entry == contents->GetController().GetLastCommittedEntry()) ||
257 !ShouldAssignURLToInstantRenderer(entry->GetURL(), profile)))
259 #endif // !defined(OS_IOS) && !defined(OS_ANDROID)
260 // Check to see if search terms have already been extracted.
261 string16 search_terms = GetSearchTermsFromNavigationEntry(entry);
262 if (!search_terms.empty())
265 // Otherwise, extract from the URL.
266 return GetSearchTermsFromURL(profile, entry->GetVirtualURL());
269 bool IsURLAllowedForSupervisedUser(const GURL& url, Profile* profile) {
270 #if defined(ENABLE_MANAGED_USERS)
271 ManagedUserService* managed_user_service =
272 ManagedUserServiceFactory::GetForProfile(profile);
273 ManagedModeURLFilter* url_filter =
274 managed_user_service->GetURLFilterForUIThread();
275 if (url_filter->GetFilteringBehaviorForURL(url) ==
276 ManagedModeURLFilter::BLOCK) {
285 // Negative start-margin values prevent the "es_sm" parameter from being used.
286 const int kDisableStartMargin = -1;
288 bool IsInstantExtendedAPIEnabled() {
289 #if defined(OS_IOS) || defined(OS_ANDROID)
292 RecordInstantExtendedOptInState();
293 return EmbeddedSearchPageVersion() != kEmbeddedPageVersionDisabled;
294 #endif // defined(OS_IOS) || defined(OS_ANDROID)
297 // Determine what embedded search page version to request from the user's
298 // default search provider. If 0, the embedded search UI should not be enabled.
299 uint64 EmbeddedSearchPageVersion() {
300 RecordInstantExtendedOptInState();
302 // Check the command-line/about:flags setting first, which should have
303 // precedence and allows the trial to not be reported (if it's never queried).
304 const CommandLine* command_line = CommandLine::ForCurrentProcess();
305 if (command_line->HasSwitch(switches::kDisableInstantExtendedAPI))
306 return kEmbeddedPageVersionDisabled;
307 if (command_line->HasSwitch(switches::kEnableInstantExtendedAPI)) {
308 // The user has set the about:flags switch to Enabled - give the default
310 return kEmbeddedPageVersionDefault;
313 FieldTrialFlags flags;
314 uint64 group_num = 0;
315 if (GetFieldTrialInfo(&flags, &group_num)) {
317 return kEmbeddedPageVersionDisabled;
318 return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
319 kEmbeddedPageVersionDefault,
322 return kEmbeddedPageVersionDisabled;
325 bool IsQueryExtractionEnabled() {
326 return EmbeddedSearchPageVersion() != kEmbeddedPageVersionDisabled &&
327 !ShouldSuppressInstantExtendedOnSRP();
330 string16 GetSearchTermsFromURL(Profile* profile, const GURL& url) {
331 string16 search_terms;
332 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
333 if (template_url && IsSuitableURLForInstant(url, template_url))
334 template_url->ExtractSearchTermsFromURL(url, &search_terms);
338 string16 GetSearchTermsFromNavigationEntry(
339 const content::NavigationEntry* entry) {
340 string16 search_terms;
342 entry->GetExtraData(sessions::kSearchTermsKey, &search_terms);
346 string16 GetSearchTerms(const content::WebContents* contents) {
350 const content::NavigationEntry* entry =
351 contents->GetController().GetVisibleEntry();
355 #if !defined(OS_IOS) && !defined(OS_ANDROID)
356 // iOS and Android doesn't use the Instant framework, disable this check for
357 // the two platforms.
358 InstantSupportState state = GetInstantSupportStateFromNavigationEntry(*entry);
359 if (state == INSTANT_SUPPORT_NO)
361 #endif // !defined(OS_IOS) && !defined(OS_ANDROID)
363 return GetSearchTermsImpl(contents, entry);
366 bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) {
367 return url.is_valid() &&
369 IsInstantExtendedAPIEnabled() &&
370 (url.SchemeIs(chrome::kChromeSearchScheme) ||
371 IsInstantURL(url, profile));
374 bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile) {
375 return ShouldAssignURLToInstantRenderer(url, profile) &&
376 (url.host() == chrome::kChromeSearchLocalNtpHost ||
377 url.host() == chrome::kChromeSearchOnlineNtpHost);
380 bool IsNTPURL(const GURL& url, Profile* profile) {
384 if (!IsInstantExtendedAPIEnabled())
385 return url == GURL(chrome::kChromeUINewTabURL);
388 ((IsInstantURL(url, profile) &&
389 GetSearchTermsFromURL(profile, url).empty()) ||
390 url == GURL(chrome::kChromeSearchLocalNtpUrl));
393 bool IsInstantNTP(const content::WebContents* contents) {
397 return NavEntryIsInstantNTP(contents,
398 contents->GetController().GetVisibleEntry());
401 bool NavEntryIsInstantNTP(const content::WebContents* contents,
402 const content::NavigationEntry* entry) {
403 if (!contents || !entry || !IsInstantExtendedAPIEnabled())
406 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
407 if (!IsRenderedInInstantProcess(contents, profile))
410 if (entry->GetVirtualURL() == GetLocalInstantURL(profile))
413 if (ShouldUseCacheableNTP()) {
414 GURL new_tab_url(GetNewTabPageURL(profile));
415 return new_tab_url.is_valid() &&
416 search::MatchesOriginAndPath(entry->GetURL(), new_tab_url);
419 return IsInstantURL(entry->GetVirtualURL(), profile) &&
420 GetSearchTermsImpl(contents, entry).empty();
423 bool IsSuggestPrefEnabled(Profile* profile) {
424 return profile && !profile->IsOffTheRecord() && profile->GetPrefs() &&
425 profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled);
428 GURL GetInstantURL(Profile* profile, int start_margin,
429 bool force_instant_results) {
430 if (!IsInstantExtendedAPIEnabled() || !IsSuggestPrefEnabled(profile))
433 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
438 TemplateURLRefToGURL(template_url->instant_url_ref(), start_margin, true,
439 force_instant_results);
440 if (!instant_url.is_valid() ||
441 !template_url->HasSearchTermsReplacementKey(instant_url))
444 // Extended mode requires HTTPS. Force it unless the base URL was overridden
445 // on the command line, in which case we allow HTTP (see comments on
446 // IsSuitableURLForInstant()).
447 if (!instant_url.SchemeIsSecure() &&
448 !google_util::StartsWithCommandLineGoogleBaseURL(instant_url)) {
449 GURL::Replacements replacements;
450 const std::string secure_scheme(content::kHttpsScheme);
451 replacements.SetSchemeStr(secure_scheme);
452 instant_url = instant_url.ReplaceComponents(replacements);
455 if (!IsURLAllowedForSupervisedUser(instant_url, profile))
461 // Returns URLs associated with the default search engine for |profile|.
462 std::vector<GURL> GetSearchURLs(Profile* profile) {
463 std::vector<GURL> result;
464 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
467 for (size_t i = 0; i < template_url->URLCount(); ++i) {
468 TemplateURLRef ref(template_url, i);
469 result.push_back(TemplateURLRefToGURL(ref, kDisableStartMargin, false,
475 GURL GetNewTabPageURL(Profile* profile) {
476 if (!ShouldUseCacheableNTP())
479 if (!profile || profile->IsOffTheRecord())
482 if (!IsSuggestPrefEnabled(profile))
483 return GURL(chrome::kChromeSearchLocalNtpUrl);
485 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
487 return GURL(chrome::kChromeSearchLocalNtpUrl);
489 GURL url(TemplateURLRefToGURL(template_url->new_tab_url_ref(),
490 kDisableStartMargin, false, false));
491 if (!url.is_valid() || !url.SchemeIsSecure())
492 return GURL(chrome::kChromeSearchLocalNtpUrl);
494 if (!IsURLAllowedForSupervisedUser(url, profile))
495 return GURL(chrome::kChromeSearchLocalNtpUrl);
500 GURL GetLocalInstantURL(Profile* profile) {
501 return GURL(chrome::kChromeSearchLocalNtpUrl);
504 bool ShouldPreferRemoteNTPOnStartup() {
505 // Check the command-line/about:flags setting first, which should have
506 // precedence and allows the trial to not be reported (if it's never queried).
507 const CommandLine* command_line = CommandLine::ForCurrentProcess();
508 if (command_line->HasSwitch(switches::kDisableInstantExtendedAPI) ||
509 command_line->HasSwitch(switches::kEnableLocalFirstLoadNTP)) {
512 if (command_line->HasSwitch(switches::kDisableLocalFirstLoadNTP))
515 FieldTrialFlags flags;
516 if (GetFieldTrialInfo(&flags, NULL)) {
517 return GetBoolValueForFlagWithDefault(kUseRemoteNTPOnStartupFlagName, true,
523 bool ShouldHideTopVerbatimMatch() {
524 FieldTrialFlags flags;
525 if (GetFieldTrialInfo(&flags, NULL)) {
526 return GetBoolValueForFlagWithDefault(kHideVerbatimFlagName, false, flags);
531 bool ShouldUseCacheableNTP() {
532 const CommandLine* command_line = CommandLine::ForCurrentProcess();
533 if (command_line->HasSwitch(switches::kUseCacheableNewTabPage))
536 FieldTrialFlags flags;
537 if (GetFieldTrialInfo(&flags, NULL)) {
538 return GetBoolValueForFlagWithDefault(kUseCacheableNTP, false, flags);
543 bool ShouldShowInstantNTP() {
544 // If using the cacheable NTP, load the NTP directly instead of preloading its
545 // contents using InstantNTP.
546 if (ShouldUseCacheableNTP())
549 FieldTrialFlags flags;
550 if (GetFieldTrialInfo(&flags, NULL)) {
551 return GetBoolValueForFlagWithDefault(kShowNtpFlagName, true, flags);
556 bool ShouldShowRecentTabsOnNTP() {
557 FieldTrialFlags flags;
558 if (GetFieldTrialInfo(&flags, NULL)) {
559 return GetBoolValueForFlagWithDefault(
560 kRecentTabsOnNTPFlagName, false, flags);
566 bool ShouldSuppressInstantExtendedOnSRP() {
567 FieldTrialFlags flags;
568 if (GetFieldTrialInfo(&flags, NULL)) {
569 return GetBoolValueForFlagWithDefault(
570 kSuppressInstantExtendedOnSRPFlagName, false, flags);
576 GURL GetEffectiveURLForInstant(const GURL& url, Profile* profile) {
577 CHECK(ShouldAssignURLToInstantRenderer(url, profile))
578 << "Error granting Instant access.";
580 if (url.SchemeIs(chrome::kChromeSearchScheme))
583 GURL effective_url(url);
585 // Replace the scheme with "chrome-search:".
586 url_canon::Replacements<char> replacements;
587 std::string search_scheme(chrome::kChromeSearchScheme);
588 replacements.SetScheme(search_scheme.data(),
589 url_parse::Component(0, search_scheme.length()));
591 // If the URL corresponds to an online NTP, replace the host with
593 std::string online_ntp_host(chrome::kChromeSearchOnlineNtpHost);
594 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
596 const GURL instant_url = TemplateURLRefToGURL(
597 template_url->instant_url_ref(), kDisableStartMargin, false, false);
598 if (instant_url.is_valid() &&
599 search::MatchesOriginAndPath(url, instant_url)) {
600 replacements.SetHost(online_ntp_host.c_str(),
601 url_parse::Component(0, online_ntp_host.length()));
605 effective_url = effective_url.ReplaceComponents(replacements);
606 return effective_url;
609 int GetInstantLoaderStalenessTimeoutSec() {
610 int timeout_sec = kStalePageTimeoutDefault;
611 FieldTrialFlags flags;
612 if (GetFieldTrialInfo(&flags, NULL)) {
613 timeout_sec = GetUInt64ValueForFlagWithDefault(kStalePageTimeoutFlagName,
614 kStalePageTimeoutDefault,
618 // Require a minimum 5 minute timeout.
619 if (timeout_sec < 0 || (timeout_sec > 0 && timeout_sec < 300))
620 timeout_sec = kStalePageTimeoutDefault;
622 // Randomize by upto 15% either side.
623 timeout_sec = base::RandInt(timeout_sec * 0.85, timeout_sec * 1.15);
628 bool IsPreloadedInstantExtendedNTP(const content::WebContents* contents) {
629 if (!IsInstantExtendedAPIEnabled())
632 ProfileManager* profile_manager = g_browser_process->profile_manager();
633 if (!profile_manager)
634 return false; // The profile manager can be NULL while testing.
636 const std::vector<Profile*>& profiles = profile_manager->GetLoadedProfiles();
637 for (size_t i = 0; i < profiles.size(); ++i) {
638 const InstantService* instant_service =
639 InstantServiceFactory::GetForProfile(profiles[i]);
640 if (instant_service && instant_service->GetNTPContents() == contents)
646 bool HandleNewTabURLRewrite(GURL* url,
647 content::BrowserContext* browser_context) {
648 if (!IsInstantExtendedAPIEnabled())
651 if (!url->SchemeIs(chrome::kChromeUIScheme) ||
652 url->host() != chrome::kChromeUINewTabHost)
655 Profile* profile = Profile::FromBrowserContext(browser_context);
656 GURL new_tab_url(GetNewTabPageURL(profile));
657 if (!new_tab_url.is_valid())
664 bool HandleNewTabURLReverseRewrite(GURL* url,
665 content::BrowserContext* browser_context) {
666 if (!IsInstantExtendedAPIEnabled())
669 Profile* profile = Profile::FromBrowserContext(browser_context);
670 GURL new_tab_url(GetNewTabPageURL(profile));
671 if (!new_tab_url.is_valid() ||
672 !search::MatchesOriginAndPath(new_tab_url, *url))
675 *url = GURL(chrome::kChromeUINewTabURL);
679 void SetInstantSupportStateInNavigationEntry(InstantSupportState state,
680 content::NavigationEntry* entry) {
684 entry->SetExtraData(kInstantSupportStateKey,
685 InstantSupportStateToString(state));
688 InstantSupportState GetInstantSupportStateFromNavigationEntry(
689 const content::NavigationEntry& entry) {
691 if (!entry.GetExtraData(kInstantSupportStateKey, &value))
692 return INSTANT_SUPPORT_UNKNOWN;
694 return StringToInstantSupportState(value);
697 bool ShouldPrefetchSearchResultsOnSRP() {
698 // Check the command-line/about:flags setting first, which should have
699 // precedence and allows the trial to not be reported (if it's never queried).
700 const CommandLine* command_line = CommandLine::ForCurrentProcess();
701 if (command_line->HasSwitch(switches::kDisableInstantExtendedAPI) ||
702 command_line->HasSwitch(switches::kEnableInstantExtendedAPI)) {
706 FieldTrialFlags flags;
707 if (GetFieldTrialInfo(&flags, NULL)) {
708 return GetBoolValueForFlagWithDefault(kPrefetchSearchResultsOnSRP, false,
714 void EnableInstantExtendedAPIForTesting() {
715 CommandLine* cl = CommandLine::ForCurrentProcess();
716 cl->AppendSwitch(switches::kEnableInstantExtendedAPI);
719 void DisableInstantExtendedAPIForTesting() {
720 CommandLine* cl = CommandLine::ForCurrentProcess();
721 cl->AppendSwitch(switches::kDisableInstantExtendedAPI);
724 bool GetFieldTrialInfo(FieldTrialFlags* flags,
725 uint64* group_number) {
726 // Get the group name. If the EmbeddedSearch trial doesn't exist, look for
727 // the older InstantExtended name.
728 std::string group_name = base::FieldTrialList::FindFullName(
729 kEmbeddedSearchFieldTrialName);
730 if (group_name.empty()) {
731 group_name = base::FieldTrialList::FindFullName(
732 kInstantExtendedFieldTrialName);
735 if (EndsWith(group_name, kDisablingSuffix, true))
738 // We have a valid trial that isn't disabled.
739 // First extract the flags.
740 std::string group_prefix(group_name);
742 size_t first_space = group_name.find(" ");
743 if (first_space != std::string::npos) {
744 // There is a flags section of the group name. Split that out and parse it.
745 group_prefix = group_name.substr(0, first_space);
746 if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space),
748 // Failed to parse the flags section. Assume the whole group name is
754 // Now extract the group number, making sure we get a non-zero value.
755 uint64 temp_group_number = 0;
756 if (StartsWithASCII(group_name, kGroupNumberPrefix, true)) {
757 std::string group_suffix = group_prefix.substr(strlen(kGroupNumberPrefix));
758 if (!base::StringToUint64(group_suffix, &temp_group_number))
761 *group_number = temp_group_number;
763 // Instant Extended is not enabled.
771 // Given a FieldTrialFlags object, returns the string value of the provided
773 std::string GetStringValueForFlagWithDefault(const std::string& flag,
774 const std::string& default_value,
775 const FieldTrialFlags& flags) {
776 FieldTrialFlags::const_iterator i;
777 for (i = flags.begin(); i != flags.end(); i++) {
778 if (i->first == flag)
781 return default_value;
784 // Given a FieldTrialFlags object, returns the uint64 value of the provided
786 uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag,
787 uint64 default_value,
788 const FieldTrialFlags& flags) {
790 std::string str_value =
791 GetStringValueForFlagWithDefault(flag, std::string(), flags);
792 if (base::StringToUint64(str_value, &value))
794 return default_value;
797 // Given a FieldTrialFlags object, returns the boolean value of the provided
799 bool GetBoolValueForFlagWithDefault(const std::string& flag,
801 const FieldTrialFlags& flags) {
802 return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
805 void ResetInstantExtendedOptInStateGateForTest() {
806 instant_extended_opt_in_state_gate = false;
809 } // namespace chrome