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