search.cc revision 6d86b77056ed63eb6871182f42a9fd5f07550f90
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 45namespace chrome { 46 47namespace { 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: 56const char kEmbeddedPageVersionFlagName[] = "espv"; 57#if defined(OS_IOS) 58const uint64 kEmbeddedPageVersionDefault = 1; 59#elif defined(OS_ANDROID) 60const uint64 kEmbeddedPageVersionDefault = 1; 61// Use this variant to enable EmbeddedSearch SearchBox API in the results page. 62const uint64 kEmbeddedSearchEnabledVersion = 2; 63#else 64const uint64 kEmbeddedPageVersionDefault = 2; 65#endif 66 67const char kHideVerbatimFlagName[] = "hide_verbatim"; 68const char kPrefetchSearchResultsFlagName[] = "prefetch_results"; 69const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp"; 70const char kAllowPrefetchNonDefaultMatch[] = "allow_prefetch_non_default_match"; 71const 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. 76const 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. 80const char kUseAltInstantURL[] = "use_alternate_instant_url"; 81const char kAltInstantURLPath[] = "search"; 82const char kAltInstantURLQueryParams[] = "&qbp=1"; 83 84const char kDisplaySearchButtonFlagName[] = "display_search_button"; 85const char kOriginChipFlagName[] = "origin_chip"; 86#if !defined(OS_IOS) && !defined(OS_ANDROID) 87const char kEnableQueryExtractionFlagName[] = "query_extraction"; 88#endif 89const 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. 98const char kInstantExtendedFieldTrialName[] = "InstantExtended"; 99const 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. 103const 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. 107enum 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. 134const char kInstantSupportStateKey[] = "instant_support_state"; 135 136const char kInstantSupportEnabled[] = "Instant support enabled"; 137const char kInstantSupportDisabled[] = "Instant support disabled"; 138const char kInstantSupportUnknown[] = "Instant support unknown"; 139 140InstantSupportState 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 149base::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 161TemplateURL* 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 171GURL 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 184bool 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// Returns true if |contents| is rendered inside the Instant process for 208// |profile|. 209bool IsRenderedInInstantProcess(const content::WebContents* contents, 210 Profile* profile) { 211 const content::RenderProcessHost* process_host = 212 contents->GetRenderProcessHost(); 213 if (!process_host) 214 return false; 215 216 const InstantService* instant_service = 217 InstantServiceFactory::GetForProfile(profile); 218 if (!instant_service) 219 return false; 220 221 return instant_service->IsInstantProcess(process_host->GetID()); 222} 223 224// |url| should either have a secure scheme or have a non-HTTPS base URL that 225// the user specified using --google-base-url. (This allows testers to use 226// --google-base-url to point at non-HTTPS servers, which eases testing.) 227bool IsSuitableURLForInstant(const GURL& url, const TemplateURL* template_url) { 228 return template_url->HasSearchTermsReplacementKey(url) && 229 (url.SchemeIsSecure() || 230 google_util::StartsWithCommandLineGoogleBaseURL(url)); 231} 232 233// Returns true if |url| can be used as an Instant URL for |profile|. 234bool IsInstantURL(const GURL& url, Profile* profile) { 235 if (!IsInstantExtendedAPIEnabled()) 236 return false; 237 238 if (!url.is_valid()) 239 return false; 240 241 const GURL new_tab_url(GetNewTabPageURL(profile)); 242 if (new_tab_url.is_valid() && 243 search::MatchesOriginAndPath(url, new_tab_url)) 244 return true; 245 246 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 247 if (!template_url) 248 return false; 249 250 if (!IsSuitableURLForInstant(url, template_url)) 251 return false; 252 253 const TemplateURLRef& instant_url_ref = template_url->instant_url_ref(); 254 UIThreadSearchTermsData search_terms_data(profile); 255 const GURL instant_url = TemplateURLRefToGURL( 256 instant_url_ref, search_terms_data, kDisableStartMargin, false, false); 257 if (!instant_url.is_valid()) 258 return false; 259 260 if (search::MatchesOriginAndPath(url, instant_url)) 261 return true; 262 263 return IsQueryExtractionEnabled() && 264 MatchesAnySearchURL(url, template_url, search_terms_data); 265} 266 267base::string16 GetSearchTermsImpl(const content::WebContents* contents, 268 const content::NavigationEntry* entry) { 269 if (!contents || !IsQueryExtractionEnabled()) 270 return base::string16(); 271 272 // For security reasons, don't extract search terms if the page is not being 273 // rendered in the privileged Instant renderer process. This is to protect 274 // against a malicious page somehow scripting the search results page and 275 // faking search terms in the URL. Random pages can't get into the Instant 276 // renderer and scripting doesn't work cross-process, so if the page is in 277 // the Instant process, we know it isn't being exploited. 278 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); 279 if (IsInstantExtendedAPIEnabled() && 280 !IsRenderedInInstantProcess(contents, profile) && 281 ((entry == contents->GetController().GetLastCommittedEntry()) || 282 !ShouldAssignURLToInstantRenderer(entry->GetURL(), profile))) 283 return base::string16(); 284 285 // Check to see if search terms have already been extracted. 286 base::string16 search_terms = GetSearchTermsFromNavigationEntry(entry); 287 if (!search_terms.empty()) 288 return search_terms; 289 290 if (!IsQueryExtractionAllowedForURL(profile, entry->GetVirtualURL())) 291 return base::string16(); 292 293 // Otherwise, extract from the URL. 294 return ExtractSearchTermsFromURL(profile, entry->GetVirtualURL()); 295} 296 297bool IsURLAllowedForSupervisedUser(const GURL& url, Profile* profile) { 298#if defined(ENABLE_MANAGED_USERS) 299 SupervisedUserService* supervised_user_service = 300 SupervisedUserServiceFactory::GetForProfile(profile); 301 SupervisedUserURLFilter* url_filter = 302 supervised_user_service->GetURLFilterForUIThread(); 303 if (url_filter->GetFilteringBehaviorForURL(url) == 304 SupervisedUserURLFilter::BLOCK) { 305 return false; 306 } 307#endif 308 return true; 309} 310 311// Returns whether |new_tab_url| can be used as a URL for the New Tab page. 312// NEW_TAB_URL_VALID means a valid URL; other enum values imply an invalid URL. 313NewTabURLState IsValidNewTabURL(Profile* profile, const GURL& new_tab_url) { 314 if (profile->IsOffTheRecord()) 315 return NEW_TAB_URL_INCOGNITO; 316 if (!new_tab_url.is_valid()) 317 return NEW_TAB_URL_NOT_SET; 318 if (!new_tab_url.SchemeIsSecure()) 319 return NEW_TAB_URL_INSECURE; 320 if (!IsURLAllowedForSupervisedUser(new_tab_url, profile)) 321 return NEW_TAB_URL_BLOCKED; 322 return NEW_TAB_URL_VALID; 323} 324 325// Used to look up the URL to use for the New Tab page. Also tracks how we 326// arrived at that URL so it can be logged with UMA. 327struct NewTabURLDetails { 328 NewTabURLDetails(const GURL& url, NewTabURLState state) 329 : url(url), state(state) {} 330 331 static NewTabURLDetails ForProfile(Profile* profile) { 332 const GURL local_url(chrome::kChromeSearchLocalNtpUrl); 333 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 334 if (!profile || !template_url) 335 return NewTabURLDetails(local_url, NEW_TAB_URL_BAD); 336 337 GURL search_provider_url = TemplateURLRefToGURL( 338 template_url->new_tab_url_ref(), UIThreadSearchTermsData(profile), 339 kDisableStartMargin, false, false); 340 NewTabURLState state = IsValidNewTabURL(profile, search_provider_url); 341 switch (state) { 342 case NEW_TAB_URL_VALID: 343 // We can use the search provider's page. 344 return NewTabURLDetails(search_provider_url, state); 345 case NEW_TAB_URL_INCOGNITO: 346 // Incognito has its own New Tab. 347 return NewTabURLDetails(GURL(), state); 348 default: 349 // Use the local New Tab otherwise. 350 return NewTabURLDetails(local_url, state); 351 } 352 } 353 354 GURL url; 355 NewTabURLState state; 356}; 357 358} // namespace 359 360// Negative start-margin values prevent the "es_sm" parameter from being used. 361const int kDisableStartMargin = -1; 362 363bool IsInstantExtendedAPIEnabled() { 364#if defined(OS_IOS) 365 return false; 366#elif defined(OS_ANDROID) 367 return EmbeddedSearchPageVersion() == kEmbeddedSearchEnabledVersion; 368#else 369 return true; 370#endif // defined(OS_IOS) 371} 372 373// Determine what embedded search page version to request from the user's 374// default search provider. If 0, the embedded search UI should not be enabled. 375uint64 EmbeddedSearchPageVersion() { 376#if defined(OS_ANDROID) 377 if (CommandLine::ForCurrentProcess()->HasSwitch( 378 switches::kEnableEmbeddedSearchAPI)) { 379 return kEmbeddedSearchEnabledVersion; 380 } 381#endif 382 383 FieldTrialFlags flags; 384 if (GetFieldTrialInfo(&flags)) { 385 return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName, 386 kEmbeddedPageVersionDefault, 387 flags); 388 } 389 return kEmbeddedPageVersionDefault; 390} 391 392std::string InstantExtendedEnabledParam(bool for_search) { 393 if (for_search && !chrome::IsQueryExtractionEnabled()) 394 return std::string(); 395 return std::string(google_util::kInstantExtendedAPIParam) + "=" + 396 base::Uint64ToString(EmbeddedSearchPageVersion()) + "&"; 397} 398 399std::string ForceInstantResultsParam(bool for_prerender) { 400 return (for_prerender || !IsInstantExtendedAPIEnabled()) ? 401 "ion=1&" : std::string(); 402} 403 404bool IsQueryExtractionEnabled() { 405#if defined(OS_IOS) || defined(OS_ANDROID) 406 return true; 407#else 408 if (!IsInstantExtendedAPIEnabled()) 409 return false; 410 411 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 412 if (command_line->HasSwitch(switches::kEnableQueryExtraction)) 413 return true; 414 415 FieldTrialFlags flags; 416 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 417 kEnableQueryExtractionFlagName, false, flags); 418#endif // defined(OS_IOS) || defined(OS_ANDROID) 419} 420 421base::string16 ExtractSearchTermsFromURL(Profile* profile, const GURL& url) { 422 if (url.is_valid() && url == GetSearchResultPrefetchBaseURL(profile)) { 423 // InstantSearchPrerenderer has the search query for the Instant search base 424 // page. 425 InstantSearchPrerenderer* prerenderer = 426 InstantSearchPrerenderer::GetForProfile(profile); 427 // TODO(kmadhusu): Remove this CHECK after the investigation of 428 // crbug.com/367204. 429 CHECK(prerenderer); 430 return prerenderer->get_last_query(); 431 } 432 433 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 434 base::string16 search_terms; 435 if (template_url) 436 template_url->ExtractSearchTermsFromURL( 437 url, UIThreadSearchTermsData(profile), &search_terms); 438 return search_terms; 439} 440 441bool IsQueryExtractionAllowedForURL(Profile* profile, const GURL& url) { 442 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 443 return template_url && IsSuitableURLForInstant(url, template_url); 444} 445 446base::string16 GetSearchTermsFromNavigationEntry( 447 const content::NavigationEntry* entry) { 448 base::string16 search_terms; 449 if (entry) 450 entry->GetExtraData(sessions::kSearchTermsKey, &search_terms); 451 return search_terms; 452} 453 454base::string16 GetSearchTerms(const content::WebContents* contents) { 455 if (!contents) 456 return base::string16(); 457 458 const content::NavigationEntry* entry = 459 contents->GetController().GetVisibleEntry(); 460 if (!entry) 461 return base::string16(); 462 463 if (IsInstantExtendedAPIEnabled()) { 464 InstantSupportState state = 465 GetInstantSupportStateFromNavigationEntry(*entry); 466 if (state == INSTANT_SUPPORT_NO) 467 return base::string16(); 468 } 469 470 return GetSearchTermsImpl(contents, entry); 471} 472 473bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) { 474 return url.is_valid() && 475 profile && 476 IsInstantExtendedAPIEnabled() && 477 (url.SchemeIs(chrome::kChromeSearchScheme) || 478 IsInstantURL(url, profile)); 479} 480 481bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile) { 482 return ShouldAssignURLToInstantRenderer(url, profile) && 483 (url.host() == chrome::kChromeSearchLocalNtpHost || 484 url.host() == chrome::kChromeSearchRemoteNtpHost); 485} 486 487bool 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 500bool IsInstantNTP(const content::WebContents* contents) { 501 if (!contents) 502 return false; 503 504 return NavEntryIsInstantNTP(contents, 505 contents->GetController().GetVisibleEntry()); 506} 507 508bool 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 525bool IsSuggestPrefEnabled(Profile* profile) { 526 return profile && !profile->IsOffTheRecord() && profile->GetPrefs() && 527 profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled); 528} 529 530GURL 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|. 573std::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 586GURL GetNewTabPageURL(Profile* profile) { 587 return NewTabURLDetails::ForProfile(profile).url; 588} 589 590GURL GetSearchResultPrefetchBaseURL(Profile* profile) { 591 return ShouldPrefetchSearchResults() ? 592 GetInstantURL(profile, kDisableStartMargin, true) : GURL(); 593} 594 595bool 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 609bool ShouldAllowPrefetchNonDefaultMatch() { 610 if (!ShouldPrefetchSearchResults()) 611 return false; 612 613 FieldTrialFlags flags; 614 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 615 kAllowPrefetchNonDefaultMatch, false, flags); 616} 617 618bool ShouldPrerenderInstantUrlOnOmniboxFocus() { 619 if (!ShouldPrefetchSearchResults()) 620 return false; 621 622 FieldTrialFlags flags; 623 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 624 kPrerenderInstantUrlOnOmniboxFocus, false, flags); 625} 626 627bool ShouldReuseInstantSearchBasePage() { 628 if (!ShouldPrefetchSearchResults()) 629 return false; 630 631 FieldTrialFlags flags; 632 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 633 kReuseInstantSearchBasePage, false, flags); 634} 635 636GURL GetLocalInstantURL(Profile* profile) { 637 return GURL(chrome::kChromeSearchLocalNtpUrl); 638} 639 640bool ShouldHideTopVerbatimMatch() { 641 FieldTrialFlags flags; 642 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 643 kHideVerbatimFlagName, false, flags); 644} 645 646DisplaySearchButtonConditions 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 667bool ShouldDisplayOriginChip() { 668 return GetOriginChipCondition() != ORIGIN_CHIP_DISABLED; 669} 670 671OriginChipCondition 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 689bool ShouldShowGoogleLocalNTP() { 690 FieldTrialFlags flags; 691 return !GetFieldTrialInfo(&flags) || GetBoolValueForFlagWithDefault( 692 kShouldShowGoogleLocalNTPFlagName, true, flags); 693} 694 695GURL 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 724bool 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 744bool 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 770void SetInstantSupportStateInNavigationEntry(InstantSupportState state, 771 content::NavigationEntry* entry) { 772 if (!entry) 773 return; 774 775 entry->SetExtraData(kInstantSupportStateKey, 776 InstantSupportStateToString(state)); 777} 778 779InstantSupportState 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 788bool ShouldPrefetchSearchResultsOnSRP() { 789 FieldTrialFlags flags; 790 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 791 kPrefetchSearchResultsOnSRP, false, flags); 792} 793 794void EnableQueryExtractionForTesting() { 795 CommandLine* cl = CommandLine::ForCurrentProcess(); 796 cl->AppendSwitch(switches::kEnableQueryExtraction); 797} 798 799bool 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. 830std::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. 843uint64 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. 856bool GetBoolValueForFlagWithDefault(const std::string& flag, 857 bool default_value, 858 const FieldTrialFlags& flags) { 859 return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags); 860} 861 862bool ShouldUseAltInstantURL() { 863 FieldTrialFlags flags; 864 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 865 kUseAltInstantURL, false, flags); 866} 867 868} // namespace chrome 869