summaryrefslogtreecommitdiffstats
path: root/chrome/browser/search/search.cc
blob: 46ea5d7e4e3ab02515a255dc344330ff31a90a0b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
// Copyright 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/search/search.h"

#include "base/command_line.h"
#include "base/metrics/field_trial.h"
#include "base/metrics/histogram.h"
#include "base/prefs/pref_service.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_split.h"
#include "base/strings/utf_string_conversions.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/google/google_util.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chrome/browser/search/instant_service.h"
#include "chrome/browser/search/instant_service_factory.h"
#include "chrome/browser/search_engines/template_url_service.h"
#include "chrome/browser/search_engines/template_url_service_factory.h"
#include "chrome/browser/ui/browser.h"
#include "chrome/browser/ui/browser_instant_controller.h"
#include "chrome/browser/ui/browser_iterator.h"
#include "chrome/browser/ui/search/instant_search_prerenderer.h"
#include "chrome/common/chrome_switches.h"
#include "chrome/common/pref_names.h"
#include "chrome/common/search_urls.h"
#include "chrome/common/url_constants.h"
#include "components/sessions/serialized_navigation_entry.h"
#include "components/user_prefs/pref_registry_syncable.h"
#include "content/public/browser/navigation_entry.h"
#include "content/public/browser/render_process_host.h"
#include "content/public/browser/web_contents.h"
#include "grit/generated_resources.h"
#include "ui/base/l10n/l10n_util.h"

#if defined(ENABLE_MANAGED_USERS)
#include "chrome/browser/managed_mode/managed_mode_url_filter.h"
#include "chrome/browser/managed_mode/managed_user_service.h"
#include "chrome/browser/managed_mode/managed_user_service_factory.h"
#endif

namespace chrome {

namespace {

// Configuration options for Embedded Search.
// EmbeddedSearch field trials are named in such a way that we can parse out
// the experiment configuration from the trial's group name in order to give
// us maximum flexability in running experiments.
// Field trial groups should be named things like "Group7 espv:2 instant:1".
// The first token is always GroupN for some integer N, followed by a
// space-delimited list of key:value pairs which correspond to these flags:
const char kEmbeddedPageVersionFlagName[] = "espv";
#if defined(OS_IOS)
const uint64 kEmbeddedPageVersionDefault = 1;
#elif defined(OS_ANDROID)
const uint64 kEmbeddedPageVersionDefault = 1;
// Use this variant to enable EmbeddedSearch SearchBox API in the results page.
const uint64 kEmbeddedSearchEnabledVersion = 2;
#else
const uint64 kEmbeddedPageVersionDefault = 2;
#endif

const char kHideVerbatimFlagName[] = "hide_verbatim";
const char kPrefetchSearchResultsFlagName[] = "prefetch_results";
const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp";

// Controls whether to reuse prerendered Instant Search base page to commit any
// search query.
const char kReuseInstantSearchBasePage[] = "reuse_instant_search_base_page";

const char kDisplaySearchButtonFlagName[] = "display_search_button";
const char kOriginChipFlagName[] = "origin_chip";
const char kOriginChipV2FlagName[] = "origin_chip_v2";
#if !defined(OS_IOS) && !defined(OS_ANDROID)
const char kEnableQueryExtractionFlagName[] = "query_extraction";
#endif
const char kShouldShowGoogleLocalNTPFlagName[] = "google_local_ntp";

// Constants for the field trial name and group prefix.
// Note in M30 and below this field trial was named "InstantExtended" and in
// M31 was renamed to EmbeddedSearch for clarity and cleanliness.  Since we
// can't easilly sync up Finch configs with the pushing of this change to
// Dev & Canary, for now the code accepts both names.
// TODO(dcblack): Remove the InstantExtended name once M31 hits the Beta
// channel.
const char kInstantExtendedFieldTrialName[] = "InstantExtended";
const char kEmbeddedSearchFieldTrialName[] = "EmbeddedSearch";

// If the field trial's group name ends with this string its configuration will
// be ignored and Instant Extended will not be enabled by default.
const char kDisablingSuffix[] = "DISABLED";

// Status of the New Tab URL for the default Search provider. NOTE: Used in a
// UMA histogram so values should only be added at the end and not reordered.
enum NewTabURLState {
  // Valid URL that should be used.
  NEW_TAB_URL_VALID = 0,

  // Corrupt state (e.g. no profile or template url).
  NEW_TAB_URL_BAD = 1,

  // URL should not be used because in incognito window.
  NEW_TAB_URL_INCOGNITO = 2,

  // No New Tab URL set for provider.
  NEW_TAB_URL_NOT_SET = 3,

  // URL is not secure.
  NEW_TAB_URL_INSECURE = 4,

  // URL should not be used because Suggest is disabled.
  // Not used anymore, see crbug.com/340424.
  // NEW_TAB_URL_SUGGEST_OFF = 5,

  // URL should not be used because it is blocked for a supervised user.
  NEW_TAB_URL_BLOCKED = 6,

  NEW_TAB_URL_MAX
};

// Used to set the Instant support state of the Navigation entry.
const char kInstantSupportStateKey[] = "instant_support_state";

const char kInstantSupportEnabled[] = "Instant support enabled";
const char kInstantSupportDisabled[] = "Instant support disabled";
const char kInstantSupportUnknown[] = "Instant support unknown";

InstantSupportState StringToInstantSupportState(const base::string16& value) {
  if (value == base::ASCIIToUTF16(kInstantSupportEnabled))
    return INSTANT_SUPPORT_YES;
  else if (value == base::ASCIIToUTF16(kInstantSupportDisabled))
    return INSTANT_SUPPORT_NO;
  else
    return INSTANT_SUPPORT_UNKNOWN;
}

base::string16 InstantSupportStateToString(InstantSupportState state) {
  switch (state) {
    case INSTANT_SUPPORT_NO:
      return base::ASCIIToUTF16(kInstantSupportDisabled);
    case INSTANT_SUPPORT_YES:
      return base::ASCIIToUTF16(kInstantSupportEnabled);
    case INSTANT_SUPPORT_UNKNOWN:
      return base::ASCIIToUTF16(kInstantSupportUnknown);
  }
  return base::ASCIIToUTF16(kInstantSupportUnknown);
}

TemplateURL* GetDefaultSearchProviderTemplateURL(Profile* profile) {
  if (profile) {
    TemplateURLService* template_url_service =
        TemplateURLServiceFactory::GetForProfile(profile);
    if (template_url_service)
      return template_url_service->GetDefaultSearchProvider();
  }
  return NULL;
}

GURL TemplateURLRefToGURL(const TemplateURLRef& ref,
                          int start_margin,
                          bool append_extra_query_params,
                          bool force_instant_results) {
  TemplateURLRef::SearchTermsArgs search_terms_args =
      TemplateURLRef::SearchTermsArgs(base::string16());
  search_terms_args.omnibox_start_margin = start_margin;
  search_terms_args.append_extra_query_params = append_extra_query_params;
  search_terms_args.force_instant_results = force_instant_results;
  return GURL(ref.ReplaceSearchTerms(search_terms_args));
}

bool MatchesAnySearchURL(const GURL& url, TemplateURL* template_url) {
  GURL search_url =
      TemplateURLRefToGURL(template_url->url_ref(), kDisableStartMargin, false,
                           false);
  if (search_url.is_valid() &&
      search::MatchesOriginAndPath(url, search_url))
    return true;

  // "URLCount() - 1" because we already tested url_ref above.
  for (size_t i = 0; i < template_url->URLCount() - 1; ++i) {
    TemplateURLRef ref(template_url, i);
    search_url = TemplateURLRefToGURL(ref, kDisableStartMargin, false, false);
    if (search_url.is_valid() &&
        search::MatchesOriginAndPath(url, search_url))
      return true;
  }

  return false;
}

// Returns true if |contents| is rendered inside the Instant process for
// |profile|.
bool IsRenderedInInstantProcess(const content::WebContents* contents,
                                Profile* profile) {
  const content::RenderProcessHost* process_host =
      contents->GetRenderProcessHost();
  if (!process_host)
    return false;

  const InstantService* instant_service =
      InstantServiceFactory::GetForProfile(profile);
  if (!instant_service)
    return false;

  return instant_service->IsInstantProcess(process_host->GetID());
}

// |url| should either have a secure scheme or have a non-HTTPS base URL that
// the user specified using --google-base-url. (This allows testers to use
// --google-base-url to point at non-HTTPS servers, which eases testing.)
bool IsSuitableURLForInstant(const GURL& url, const TemplateURL* template_url) {
  return template_url->HasSearchTermsReplacementKey(url) &&
      (url.SchemeIsSecure() ||
       google_util::StartsWithCommandLineGoogleBaseURL(url));
}

// Returns true if |url| can be used as an Instant URL for |profile|.
bool IsInstantURL(const GURL& url, Profile* profile) {
  if (!IsInstantExtendedAPIEnabled())
    return false;

  if (!url.is_valid())
    return false;

  const GURL new_tab_url(GetNewTabPageURL(profile));
  if (new_tab_url.is_valid() &&
      search::MatchesOriginAndPath(url, new_tab_url))
    return true;

  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  if (!template_url)
    return false;

  if (!IsSuitableURLForInstant(url, template_url))
    return false;

  const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
  const GURL instant_url =
      TemplateURLRefToGURL(instant_url_ref, kDisableStartMargin, false, false);
  if (!instant_url.is_valid())
    return false;

  if (search::MatchesOriginAndPath(url, instant_url))
    return true;

  return IsQueryExtractionEnabled() && MatchesAnySearchURL(url, template_url);
}

base::string16 GetSearchTermsImpl(const content::WebContents* contents,
                                  const content::NavigationEntry* entry) {
  if (!contents || !IsQueryExtractionEnabled())
    return base::string16();

  // For security reasons, don't extract search terms if the page is not being
  // rendered in the privileged Instant renderer process. This is to protect
  // against a malicious page somehow scripting the search results page and
  // faking search terms in the URL. Random pages can't get into the Instant
  // renderer and scripting doesn't work cross-process, so if the page is in
  // the Instant process, we know it isn't being exploited.
  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
  if (IsInstantExtendedAPIEnabled() &&
      !IsRenderedInInstantProcess(contents, profile) &&
      ((entry == contents->GetController().GetLastCommittedEntry()) ||
       !ShouldAssignURLToInstantRenderer(entry->GetURL(), profile)))
    return base::string16();

  // Check to see if search terms have already been extracted.
  base::string16 search_terms = GetSearchTermsFromNavigationEntry(entry);
  if (!search_terms.empty())
    return search_terms;

  if (!IsQueryExtractionAllowedForURL(profile, entry->GetVirtualURL()))
    return base::string16();

  // Otherwise, extract from the URL.
  return ExtractSearchTermsFromURL(profile, entry->GetVirtualURL());
}

bool IsURLAllowedForSupervisedUser(const GURL& url, Profile* profile) {
#if defined(ENABLE_MANAGED_USERS)
  ManagedUserService* managed_user_service =
      ManagedUserServiceFactory::GetForProfile(profile);
  ManagedModeURLFilter* url_filter =
      managed_user_service->GetURLFilterForUIThread();
  if (url_filter->GetFilteringBehaviorForURL(url) ==
          ManagedModeURLFilter::BLOCK) {
    return false;
  }
#endif
  return true;
}

// Returns whether |new_tab_url| can be used as a URL for the New Tab page.
// NEW_TAB_URL_VALID means a valid URL; other enum values imply an invalid URL.
NewTabURLState IsValidNewTabURL(Profile* profile, const GURL& new_tab_url) {
  if (profile->IsOffTheRecord())
    return NEW_TAB_URL_INCOGNITO;
  if (!new_tab_url.is_valid())
    return NEW_TAB_URL_NOT_SET;
  if (!new_tab_url.SchemeIsSecure())
    return NEW_TAB_URL_INSECURE;
  if (!IsURLAllowedForSupervisedUser(new_tab_url, profile))
    return NEW_TAB_URL_BLOCKED;
  return NEW_TAB_URL_VALID;
}

// Used to look up the URL to use for the New Tab page. Also tracks how we
// arrived at that URL so it can be logged with UMA.
struct NewTabURLDetails {
  NewTabURLDetails(const GURL& url, NewTabURLState state)
      : url(url), state(state) {}

  static NewTabURLDetails ForProfile(Profile* profile) {
    const GURL local_url(chrome::kChromeSearchLocalNtpUrl);
    TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
    if (!profile || !template_url)
      return NewTabURLDetails(local_url, NEW_TAB_URL_BAD);

    GURL search_provider_url = TemplateURLRefToGURL(
        template_url->new_tab_url_ref(), kDisableStartMargin, false, false);
    NewTabURLState state = IsValidNewTabURL(profile, search_provider_url);
    switch (state) {
      case NEW_TAB_URL_VALID:
        // We can use the search provider's page.
        return NewTabURLDetails(search_provider_url, state);
      case NEW_TAB_URL_INCOGNITO:
        // Incognito has its own New Tab.
        return NewTabURLDetails(GURL(), state);
      default:
        // Use the local New Tab otherwise.
        return NewTabURLDetails(local_url, state);
    };
  }

  GURL url;
  NewTabURLState state;
};

}  // namespace

// Negative start-margin values prevent the "es_sm" parameter from being used.
const int kDisableStartMargin = -1;

bool IsInstantExtendedAPIEnabled() {
#if defined(OS_IOS)
  return false;
#elif defined(OS_ANDROID)
  return EmbeddedSearchPageVersion() == kEmbeddedSearchEnabledVersion;
#else
  return true;
#endif  // defined(OS_IOS)
}

// Determine what embedded search page version to request from the user's
// default search provider. If 0, the embedded search UI should not be enabled.
uint64 EmbeddedSearchPageVersion() {
#if defined(OS_ANDROID)
  if (CommandLine::ForCurrentProcess()->HasSwitch(
      switches::kEnableEmbeddedSearchAPI)) {
    return kEmbeddedSearchEnabledVersion;
  }
#endif

  FieldTrialFlags flags;
  if (GetFieldTrialInfo(&flags)) {
    return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
                                            kEmbeddedPageVersionDefault,
                                            flags);
  }
  return kEmbeddedPageVersionDefault;
}

std::string InstantExtendedEnabledParam(bool for_search) {
  if (for_search && !chrome::IsQueryExtractionEnabled())
    return std::string();
  return std::string(google_util::kInstantExtendedAPIParam) + "=" +
      base::Uint64ToString(EmbeddedSearchPageVersion()) + "&";
}

std::string ForceInstantResultsParam(bool for_prerender) {
  return (for_prerender || !IsInstantExtendedAPIEnabled()) ?
      "ion=1&" : std::string();
}

bool IsQueryExtractionEnabled() {
#if defined(OS_IOS) || defined(OS_ANDROID)
  return true;
#else
  if (!IsInstantExtendedAPIEnabled())
    return false;

  const CommandLine* command_line = CommandLine::ForCurrentProcess();
  if (command_line->HasSwitch(switches::kEnableQueryExtraction))
    return true;

  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kEnableQueryExtractionFlagName, false, flags);
#endif  // defined(OS_IOS) || defined(OS_ANDROID)
}

base::string16 ExtractSearchTermsFromURL(Profile* profile, const GURL& url) {
  if (url.is_valid() && url == GetSearchResultPrefetchBaseURL(profile)) {
    // InstantSearchPrerenderer has the search query for the Instant search base
    // page.
    InstantSearchPrerenderer* prerenderer =
        InstantSearchPrerenderer::GetForProfile(profile);
    // TODO(kmadhusu): Remove this CHECK after the investigation of
    // crbug.com/367204.
    CHECK(prerenderer);
    return prerenderer->get_last_query();
  }

  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  base::string16 search_terms;
  if (template_url)
    template_url->ExtractSearchTermsFromURL(url, &search_terms);
  return search_terms;
}

bool IsQueryExtractionAllowedForURL(Profile* profile, const GURL& url) {
  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  return template_url && IsSuitableURLForInstant(url, template_url);
}

base::string16 GetSearchTermsFromNavigationEntry(
    const content::NavigationEntry* entry) {
  base::string16 search_terms;
  if (entry)
    entry->GetExtraData(sessions::kSearchTermsKey, &search_terms);
  return search_terms;
}

base::string16 GetSearchTerms(const content::WebContents* contents) {
  if (!contents)
    return base::string16();

  const content::NavigationEntry* entry =
      contents->GetController().GetVisibleEntry();
  if (!entry)
    return base::string16();

  if (IsInstantExtendedAPIEnabled()) {
    InstantSupportState state =
        GetInstantSupportStateFromNavigationEntry(*entry);
    if (state == INSTANT_SUPPORT_NO)
      return base::string16();
  }

  return GetSearchTermsImpl(contents, entry);
}

bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) {
  return url.is_valid() &&
         profile &&
         IsInstantExtendedAPIEnabled() &&
         (url.SchemeIs(chrome::kChromeSearchScheme) ||
          IsInstantURL(url, profile));
}

bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile) {
  return ShouldAssignURLToInstantRenderer(url, profile) &&
      (url.host() == chrome::kChromeSearchLocalNtpHost ||
       url.host() == chrome::kChromeSearchRemoteNtpHost);
}

bool IsNTPURL(const GURL& url, Profile* profile) {
  if (!url.is_valid())
    return false;

  if (!IsInstantExtendedAPIEnabled())
    return url == GURL(chrome::kChromeUINewTabURL);

  const base::string16 search_terms = ExtractSearchTermsFromURL(profile, url);
  return profile &&
      ((IsInstantURL(url, profile) && search_terms.empty()) ||
       url == GURL(chrome::kChromeSearchLocalNtpUrl));
}

bool IsInstantNTP(const content::WebContents* contents) {
  if (!contents)
    return false;

  return NavEntryIsInstantNTP(contents,
                              contents->GetController().GetVisibleEntry());
}

bool NavEntryIsInstantNTP(const content::WebContents* contents,
                          const content::NavigationEntry* entry) {
  if (!contents || !entry || !IsInstantExtendedAPIEnabled())
    return false;

  Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
  if (!IsRenderedInInstantProcess(contents, profile))
    return false;

  if (entry->GetURL() == GetLocalInstantURL(profile))
    return true;

  GURL new_tab_url(GetNewTabPageURL(profile));
  return new_tab_url.is_valid() &&
      search::MatchesOriginAndPath(entry->GetURL(), new_tab_url);
}

bool IsSuggestPrefEnabled(Profile* profile) {
  return profile && !profile->IsOffTheRecord() && profile->GetPrefs() &&
         profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled);
}

GURL GetInstantURL(Profile* profile, int start_margin,
                   bool force_instant_results) {
  if (!IsInstantExtendedAPIEnabled() || !IsSuggestPrefEnabled(profile))
    return GURL();

  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  if (!template_url)
    return GURL();

  GURL instant_url =
      TemplateURLRefToGURL(template_url->instant_url_ref(), start_margin, true,
                           force_instant_results);
  if (!instant_url.is_valid() ||
      !template_url->HasSearchTermsReplacementKey(instant_url))
    return GURL();

  // Extended mode requires HTTPS.  Force it unless the base URL was overridden
  // on the command line, in which case we allow HTTP (see comments on
  // IsSuitableURLForInstant()).
  if (!instant_url.SchemeIsSecure() &&
      !google_util::StartsWithCommandLineGoogleBaseURL(instant_url)) {
    GURL::Replacements replacements;
    const std::string secure_scheme(content::kHttpsScheme);
    replacements.SetSchemeStr(secure_scheme);
    instant_url = instant_url.ReplaceComponents(replacements);
  }

  if (!IsURLAllowedForSupervisedUser(instant_url, profile))
    return GURL();

  return instant_url;
}

// Returns URLs associated with the default search engine for |profile|.
std::vector<GURL> GetSearchURLs(Profile* profile) {
  std::vector<GURL> result;
  TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
  if (!template_url)
    return result;
  for (size_t i = 0; i < template_url->URLCount(); ++i) {
    TemplateURLRef ref(template_url, i);
    result.push_back(TemplateURLRefToGURL(ref, kDisableStartMargin, false,
                                          false));
  }
  return result;
}

GURL GetNewTabPageURL(Profile* profile) {
  return NewTabURLDetails::ForProfile(profile).url;
}

GURL GetSearchResultPrefetchBaseURL(Profile* profile) {
  return ShouldPrefetchSearchResults() ?
      GetInstantURL(profile, kDisableStartMargin, true) : GURL();
}

bool ShouldPrefetchSearchResults() {
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kPrefetchSearchResults)) {
    return true;
  }

  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kPrefetchSearchResultsFlagName, false, flags);
}

bool ShouldReuseInstantSearchBasePage() {
  if (CommandLine::ForCurrentProcess()->HasSwitch(
          switches::kPrefetchSearchResults)) {
    return true;
  }

  if (!ShouldPrefetchSearchResults())
    return false;

  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kReuseInstantSearchBasePage, false, flags);
}

GURL GetLocalInstantURL(Profile* profile) {
  return GURL(chrome::kChromeSearchLocalNtpUrl);
}

bool ShouldHideTopVerbatimMatch() {
  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kHideVerbatimFlagName, false, flags);
}

DisplaySearchButtonConditions GetDisplaySearchButtonConditions() {
  const CommandLine* cl = CommandLine::ForCurrentProcess();
  if (cl->HasSwitch(switches::kDisableSearchButtonInOmnibox))
    return DISPLAY_SEARCH_BUTTON_NEVER;
  if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStr))
    return DISPLAY_SEARCH_BUTTON_FOR_STR;
  if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStrOrIip))
    return DISPLAY_SEARCH_BUTTON_FOR_STR_OR_IIP;
  if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxAlways))
    return DISPLAY_SEARCH_BUTTON_ALWAYS;

  FieldTrialFlags flags;
  if (!GetFieldTrialInfo(&flags))
    return DISPLAY_SEARCH_BUTTON_NEVER;
  uint64 value =
      GetUInt64ValueForFlagWithDefault(kDisplaySearchButtonFlagName, 0, flags);
  return (value < DISPLAY_SEARCH_BUTTON_NUM_VALUES) ?
      static_cast<DisplaySearchButtonConditions>(value) :
      DISPLAY_SEARCH_BUTTON_NEVER;
}

bool ShouldDisplayOriginChip() {
  return GetOriginChipPosition() != ORIGIN_CHIP_DISABLED;
}

OriginChipPosition GetOriginChipPosition() {
  if (ShouldDisplayOriginChipV2())
    return ORIGIN_CHIP_DISABLED;

  const CommandLine* cl = CommandLine::ForCurrentProcess();
  if (cl->HasSwitch(switches::kDisableOriginChip))
    return ORIGIN_CHIP_DISABLED;
  if (cl->HasSwitch(switches::kEnableOriginChipLeadingLocationBar))
    return ORIGIN_CHIP_LEADING_LOCATION_BAR;
  if (cl->HasSwitch(switches::kEnableOriginChip) ||
      cl->HasSwitch(switches::kEnableOriginChipTrailingLocationBar))
    return ORIGIN_CHIP_TRAILING_LOCATION_BAR;
  if (cl->HasSwitch(switches::kEnableOriginChipLeadingMenuButton))
    return ORIGIN_CHIP_LEADING_MENU_BUTTON;

  FieldTrialFlags flags;
  if (!GetFieldTrialInfo(&flags))
    return ORIGIN_CHIP_DISABLED;
  uint64 value =
      GetUInt64ValueForFlagWithDefault(kOriginChipFlagName, 0, flags);
  return (value < ORIGIN_CHIP_NUM_VALUES) ?
      static_cast<OriginChipPosition>(value) :
      ORIGIN_CHIP_DISABLED;
}

bool ShouldDisplayOriginChipV2() {
  return GetOriginChipV2HideTrigger() != ORIGIN_CHIP_V2_DISABLED;
}

OriginChipV2HideTrigger GetOriginChipV2HideTrigger() {
  const CommandLine* cl = CommandLine::ForCurrentProcess();
  if (cl->HasSwitch(switches::kDisableOriginChipV2))
    return ORIGIN_CHIP_V2_DISABLED;
  if (cl->HasSwitch(switches::kEnableOriginChipV2HideOnMouseRelease))
    return ORIGIN_CHIP_V2_HIDE_ON_MOUSE_RELEASE;
  if (cl->HasSwitch(switches::kEnableOriginChipV2HideOnUserInput))
    return ORIGIN_CHIP_V2_HIDE_ON_USER_INPUT;

  FieldTrialFlags flags;
  if (!GetFieldTrialInfo(&flags))
    return ORIGIN_CHIP_V2_DISABLED;
  uint64 value =
      GetUInt64ValueForFlagWithDefault(kOriginChipV2FlagName, 0, flags);
  return (value < ORIGIN_CHIP_V2_NUM_VALUES) ?
      static_cast<OriginChipV2HideTrigger>(value) :
      ORIGIN_CHIP_V2_DISABLED;
}

bool ShouldShowGoogleLocalNTP() {
  FieldTrialFlags flags;
  return !GetFieldTrialInfo(&flags) || GetBoolValueForFlagWithDefault(
      kShouldShowGoogleLocalNTPFlagName, true, flags);
}

GURL GetEffectiveURLForInstant(const GURL& url, Profile* profile) {
  CHECK(ShouldAssignURLToInstantRenderer(url, profile))
      << "Error granting Instant access.";

  if (url.SchemeIs(chrome::kChromeSearchScheme))
    return url;

  GURL effective_url(url);

  // Replace the scheme with "chrome-search:".
  url::Replacements<char> replacements;
  std::string search_scheme(chrome::kChromeSearchScheme);
  replacements.SetScheme(search_scheme.data(),
                         url::Component(0, search_scheme.length()));

  // If this is the URL for a server-provided NTP, replace the host with
  // "remote-ntp".
  std::string remote_ntp_host(chrome::kChromeSearchRemoteNtpHost);
  NewTabURLDetails details = NewTabURLDetails::ForProfile(profile);
  if (details.state == NEW_TAB_URL_VALID &&
      search::MatchesOriginAndPath(url, details.url)) {
    replacements.SetHost(remote_ntp_host.c_str(),
                         url::Component(0, remote_ntp_host.length()));
  }

  effective_url = effective_url.ReplaceComponents(replacements);
  return effective_url;
}

bool HandleNewTabURLRewrite(GURL* url,
                            content::BrowserContext* browser_context) {
  if (!IsInstantExtendedAPIEnabled())
    return false;

  if (!url->SchemeIs(content::kChromeUIScheme) ||
      url->host() != chrome::kChromeUINewTabHost)
    return false;

  Profile* profile = Profile::FromBrowserContext(browser_context);
  NewTabURLDetails details(NewTabURLDetails::ForProfile(profile));
  UMA_HISTOGRAM_ENUMERATION("NewTabPage.URLState",
                            details.state, NEW_TAB_URL_MAX);
  if (details.url.is_valid()) {
    *url = details.url;
    return true;
  }
  return false;
}

bool HandleNewTabURLReverseRewrite(GURL* url,
                                   content::BrowserContext* browser_context) {
  if (!IsInstantExtendedAPIEnabled())
    return false;

  // Do nothing in incognito.
  Profile* profile = Profile::FromBrowserContext(browser_context);
  if (profile && profile->IsOffTheRecord())
    return false;

  if (search::MatchesOriginAndPath(
      GURL(chrome::kChromeSearchLocalNtpUrl), *url)) {
    *url = GURL(chrome::kChromeUINewTabURL);
    return true;
  }

  GURL new_tab_url(GetNewTabPageURL(profile));
  if (new_tab_url.is_valid() &&
      search::MatchesOriginAndPath(new_tab_url, *url)) {
    *url = GURL(chrome::kChromeUINewTabURL);
    return true;
  }

  return false;
}

void SetInstantSupportStateInNavigationEntry(InstantSupportState state,
                                             content::NavigationEntry* entry) {
  if (!entry)
    return;

  entry->SetExtraData(kInstantSupportStateKey,
                      InstantSupportStateToString(state));
}

InstantSupportState GetInstantSupportStateFromNavigationEntry(
    const content::NavigationEntry& entry) {
  base::string16 value;
  if (!entry.GetExtraData(kInstantSupportStateKey, &value))
    return INSTANT_SUPPORT_UNKNOWN;

  return StringToInstantSupportState(value);
}

bool ShouldPrefetchSearchResultsOnSRP() {
  FieldTrialFlags flags;
  return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault(
      kPrefetchSearchResultsOnSRP, false, flags);
}

void EnableQueryExtractionForTesting() {
  CommandLine* cl = CommandLine::ForCurrentProcess();
  cl->AppendSwitch(switches::kEnableQueryExtraction);
}

bool GetFieldTrialInfo(FieldTrialFlags* flags) {
  // Get the group name.  If the EmbeddedSearch trial doesn't exist, look for
  // the older InstantExtended name.
  std::string group_name = base::FieldTrialList::FindFullName(
      kEmbeddedSearchFieldTrialName);
  if (group_name.empty()) {
    group_name = base::FieldTrialList::FindFullName(
        kInstantExtendedFieldTrialName);
  }

  if (EndsWith(group_name, kDisablingSuffix, true))
    return false;

  // We have a valid trial that isn't disabled. Extract the flags.
  std::string group_prefix(group_name);
  size_t first_space = group_name.find(" ");
  if (first_space != std::string::npos) {
    // There is a flags section of the group name. Split that out and parse it.
    group_prefix = group_name.substr(0, first_space);
    if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space),
                                            ':', ' ', flags)) {
      // Failed to parse the flags section. Assume the whole group name is
      // invalid.
      return false;
    }
  }
  return true;
}

// Given a FieldTrialFlags object, returns the string value of the provided
// flag.
std::string GetStringValueForFlagWithDefault(const std::string& flag,
                                             const std::string& default_value,
                                             const FieldTrialFlags& flags) {
  FieldTrialFlags::const_iterator i;
  for (i = flags.begin(); i != flags.end(); i++) {
    if (i->first == flag)
      return i->second;
  }
  return default_value;
}

// Given a FieldTrialFlags object, returns the uint64 value of the provided
// flag.
uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag,
                                        uint64 default_value,
                                        const FieldTrialFlags& flags) {
  uint64 value;
  std::string str_value =
      GetStringValueForFlagWithDefault(flag, std::string(), flags);
  if (base::StringToUint64(str_value, &value))
    return value;
  return default_value;
}

// Given a FieldTrialFlags object, returns the boolean value of the provided
// flag.
bool GetBoolValueForFlagWithDefault(const std::string& flag,
                                    bool default_value,
                                    const FieldTrialFlags& flags) {
  return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
}

}  // namespace chrome