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