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