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 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 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 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 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 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.) 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|. 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 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 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. 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 { 313 NewTabURLDetails(const GURL& url, NewTabURLState state) 314 : url(url), state(state) {} 315 316 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 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. 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 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 384 std::string ForceInstantResultsParam(bool for_prerender) { 385 return (for_prerender || !IsInstantExtendedAPIEnabled()) ? 386 "ion=1&" : std::string(); 387 } 388 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 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 426 bool IsQueryExtractionAllowedForURL(Profile* profile, const GURL& url) { 427 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 428 return template_url && IsSuitableURLForInstant(url, template_url); 429 } 430 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 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 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 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 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 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 500 bool IsInstantNTP(const content::WebContents* contents) { 501 if (!contents) 502 return false; 503 504 return NavEntryIsInstantNTP(contents, 505 contents->GetController().GetVisibleEntry()); 506 } 507 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 525 bool IsSuggestPrefEnabled(Profile* profile) { 526 return profile && !profile->IsOffTheRecord() && profile->GetPrefs() && 527 profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled); 528 } 529 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|. 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 586 GURL GetNewTabPageURL(Profile* profile) { 587 return NewTabURLDetails::ForProfile(profile).url; 588 } 589 590 GURL GetSearchResultPrefetchBaseURL(Profile* profile) { 591 return ShouldPrefetchSearchResults() ? 592 GetInstantURL(profile, kDisableStartMargin, true) : GURL(); 593 } 594 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 609 bool ShouldAllowPrefetchNonDefaultMatch() { 610 if (!ShouldPrefetchSearchResults()) 611 return false; 612 613 FieldTrialFlags flags; 614 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 615 kAllowPrefetchNonDefaultMatch, false, flags); 616 } 617 618 bool ShouldPrerenderInstantUrlOnOmniboxFocus() { 619 if (!ShouldPrefetchSearchResults()) 620 return false; 621 622 FieldTrialFlags flags; 623 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 624 kPrerenderInstantUrlOnOmniboxFocus, false, flags); 625 } 626 627 bool ShouldReuseInstantSearchBasePage() { 628 if (!ShouldPrefetchSearchResults()) 629 return false; 630 631 FieldTrialFlags flags; 632 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 633 kReuseInstantSearchBasePage, false, flags); 634 } 635 636 GURL GetLocalInstantURL(Profile* profile) { 637 return GURL(chrome::kChromeSearchLocalNtpUrl); 638 } 639 640 bool ShouldHideTopVerbatimMatch() { 641 FieldTrialFlags flags; 642 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 643 kHideVerbatimFlagName, false, flags); 644 } 645 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 667 bool ShouldDisplayOriginChip() { 668 return GetOriginChipCondition() != ORIGIN_CHIP_DISABLED; 669 } 670 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 689 bool ShouldShowGoogleLocalNTP() { 690 FieldTrialFlags flags; 691 return !GetFieldTrialInfo(&flags) || GetBoolValueForFlagWithDefault( 692 kShouldShowGoogleLocalNTPFlagName, true, flags); 693 } 694 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 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 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 770 void SetInstantSupportStateInNavigationEntry(InstantSupportState state, 771 content::NavigationEntry* entry) { 772 if (!entry) 773 return; 774 775 entry->SetExtraData(kInstantSupportStateKey, 776 InstantSupportStateToString(state)); 777 } 778 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 788 bool ShouldPrefetchSearchResultsOnSRP() { 789 FieldTrialFlags flags; 790 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 791 kPrefetchSearchResultsOnSRP, false, flags); 792 } 793 794 void EnableQueryExtractionForTesting() { 795 CommandLine* cl = CommandLine::ForCurrentProcess(); 796 cl->AppendSwitch(switches::kEnableQueryExtraction); 797 } 798 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. 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. 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. 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 862 bool ShouldUseAltInstantURL() { 863 FieldTrialFlags flags; 864 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 865 kUseAltInstantURL, false, flags); 866 } 867 868 } // namespace chrome 869