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/prefs/pref_service.h" 10 #include "base/rand_util.h" 11 #include "base/strings/string_number_conversions.h" 12 #include "base/strings/string_split.h" 13 #include "base/strings/utf_string_conversions.h" 14 #include "chrome/browser/browser_process.h" 15 #include "chrome/browser/google/google_util.h" 16 #include "chrome/browser/profiles/profile.h" 17 #include "chrome/browser/profiles/profile_manager.h" 18 #include "chrome/browser/search/instant_service.h" 19 #include "chrome/browser/search/instant_service_factory.h" 20 #include "chrome/browser/search_engines/template_url_service.h" 21 #include "chrome/browser/search_engines/template_url_service_factory.h" 22 #include "chrome/browser/ui/browser.h" 23 #include "chrome/browser/ui/browser_instant_controller.h" 24 #include "chrome/browser/ui/browser_iterator.h" 25 #include "chrome/browser/ui/search/instant_search_prerenderer.h" 26 #include "chrome/common/chrome_switches.h" 27 #include "chrome/common/pref_names.h" 28 #include "chrome/common/search_urls.h" 29 #include "chrome/common/url_constants.h" 30 #include "components/sessions/serialized_navigation_entry.h" 31 #include "components/user_prefs/pref_registry_syncable.h" 32 #include "content/public/browser/navigation_entry.h" 33 #include "content/public/browser/render_process_host.h" 34 #include "content/public/browser/web_contents.h" 35 #include "grit/generated_resources.h" 36 #include "ui/base/l10n/l10n_util.h" 37 38 #if defined(ENABLE_MANAGED_USERS) 39 #include "chrome/browser/managed_mode/managed_mode_url_filter.h" 40 #include "chrome/browser/managed_mode/managed_user_service.h" 41 #include "chrome/browser/managed_mode/managed_user_service_factory.h" 42 #endif 43 44 namespace chrome { 45 46 namespace { 47 48 // Configuration options for Embedded Search. 49 // EmbeddedSearch field trials are named in such a way that we can parse out 50 // the experiment configuration from the trial's group name in order to give 51 // us maximum flexability in running experiments. 52 // Field trial groups should be named things like "Group7 espv:2 instant:1". 53 // The first token is always GroupN for some integer N, followed by a 54 // space-delimited list of key:value pairs which correspond to these flags: 55 const char kEmbeddedPageVersionFlagName[] = "espv"; 56 #if defined(OS_IOS) || defined(OS_ANDROID) 57 const uint64 kEmbeddedPageVersionDefault = 1; 58 #else 59 const uint64 kEmbeddedPageVersionDefault = 2; 60 #endif 61 62 // The staleness timeout can be set (in seconds) via this config. 63 const char kStalePageTimeoutFlagName[] = "stale"; 64 const int kStalePageTimeoutDefault = 3 * 3600; // 3 hours. 65 66 const char kHideVerbatimFlagName[] = "hide_verbatim"; 67 const char kShowNtpFlagName[] = "show_ntp"; 68 const char kUseCacheableNTP[] = "use_cacheable_ntp"; 69 const char kPrefetchSearchResultsFlagName[] = "prefetch_results"; 70 const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp"; 71 const char kDisplaySearchButtonFlagName[] = "display_search_button"; 72 const char kEnableOriginChipFlagName[] = "origin_chip"; 73 #if !defined(OS_IOS) && !defined(OS_ANDROID) 74 const char kEnableQueryExtractionFlagName[] = "query_extraction"; 75 #endif 76 77 // Constants for the field trial name and group prefix. 78 // Note in M30 and below this field trial was named "InstantExtended" and in 79 // M31 was renamed to EmbeddedSearch for clarity and cleanliness. Since we 80 // can't easilly sync up Finch configs with the pushing of this change to 81 // Dev & Canary, for now the code accepts both names. 82 // TODO(dcblack): Remove the InstantExtended name once M31 hits the Beta 83 // channel. 84 const char kInstantExtendedFieldTrialName[] = "InstantExtended"; 85 const char kEmbeddedSearchFieldTrialName[] = "EmbeddedSearch"; 86 87 // If the field trial's group name ends with this string its configuration will 88 // be ignored and Instant Extended will not be enabled by default. 89 const char kDisablingSuffix[] = "DISABLED"; 90 91 // Used to set the Instant support state of the Navigation entry. 92 const char kInstantSupportStateKey[] = "instant_support_state"; 93 94 const char kInstantSupportEnabled[] = "Instant support enabled"; 95 const char kInstantSupportDisabled[] = "Instant support disabled"; 96 const char kInstantSupportUnknown[] = "Instant support unknown"; 97 98 InstantSupportState StringToInstantSupportState(const base::string16& value) { 99 if (value == ASCIIToUTF16(kInstantSupportEnabled)) 100 return INSTANT_SUPPORT_YES; 101 else if (value == ASCIIToUTF16(kInstantSupportDisabled)) 102 return INSTANT_SUPPORT_NO; 103 else 104 return INSTANT_SUPPORT_UNKNOWN; 105 } 106 107 base::string16 InstantSupportStateToString(InstantSupportState state) { 108 switch (state) { 109 case INSTANT_SUPPORT_NO: 110 return ASCIIToUTF16(kInstantSupportDisabled); 111 case INSTANT_SUPPORT_YES: 112 return ASCIIToUTF16(kInstantSupportEnabled); 113 case INSTANT_SUPPORT_UNKNOWN: 114 return ASCIIToUTF16(kInstantSupportUnknown); 115 } 116 return ASCIIToUTF16(kInstantSupportUnknown); 117 } 118 119 TemplateURL* GetDefaultSearchProviderTemplateURL(Profile* profile) { 120 TemplateURLService* template_url_service = 121 TemplateURLServiceFactory::GetForProfile(profile); 122 if (template_url_service) 123 return template_url_service->GetDefaultSearchProvider(); 124 return NULL; 125 } 126 127 GURL TemplateURLRefToGURL(const TemplateURLRef& ref, 128 int start_margin, 129 bool append_extra_query_params, 130 bool force_instant_results) { 131 TemplateURLRef::SearchTermsArgs search_terms_args = 132 TemplateURLRef::SearchTermsArgs(base::string16()); 133 search_terms_args.omnibox_start_margin = start_margin; 134 search_terms_args.append_extra_query_params = append_extra_query_params; 135 search_terms_args.force_instant_results = force_instant_results; 136 return GURL(ref.ReplaceSearchTerms(search_terms_args)); 137 } 138 139 bool MatchesAnySearchURL(const GURL& url, TemplateURL* template_url) { 140 GURL search_url = 141 TemplateURLRefToGURL(template_url->url_ref(), kDisableStartMargin, false, 142 false); 143 if (search_url.is_valid() && 144 search::MatchesOriginAndPath(url, search_url)) 145 return true; 146 147 // "URLCount() - 1" because we already tested url_ref above. 148 for (size_t i = 0; i < template_url->URLCount() - 1; ++i) { 149 TemplateURLRef ref(template_url, i); 150 search_url = TemplateURLRefToGURL(ref, kDisableStartMargin, false, false); 151 if (search_url.is_valid() && 152 search::MatchesOriginAndPath(url, search_url)) 153 return true; 154 } 155 156 return false; 157 } 158 159 // Returns true if |contents| is rendered inside the Instant process for 160 // |profile|. 161 bool IsRenderedInInstantProcess(const content::WebContents* contents, 162 Profile* profile) { 163 const content::RenderProcessHost* process_host = 164 contents->GetRenderProcessHost(); 165 if (!process_host) 166 return false; 167 168 const InstantService* instant_service = 169 InstantServiceFactory::GetForProfile(profile); 170 if (!instant_service) 171 return false; 172 173 return instant_service->IsInstantProcess(process_host->GetID()); 174 } 175 176 // Returns true if |url| passes some basic checks that must succeed for it to be 177 // usable as an instant URL: 178 // (1) It contains the search terms replacement key of |template_url|, which is 179 // expected to be the TemplateURL* for the default search provider. 180 // (2) Either it has a secure scheme, or else the user has manually specified a 181 // --google-base-url and it uses that base URL. (This allows testers to use 182 // --google-base-url to point at non-HTTPS servers, which eases testing.) 183 bool IsSuitableURLForInstant(const GURL& url, const TemplateURL* template_url) { 184 return template_url->HasSearchTermsReplacementKey(url) && 185 (url.SchemeIsSecure() || 186 google_util::StartsWithCommandLineGoogleBaseURL(url)); 187 } 188 189 // Returns true if |url| can be used as an Instant URL for |profile|. 190 bool IsInstantURL(const GURL& url, Profile* profile) { 191 if (!IsInstantExtendedAPIEnabled()) 192 return false; 193 194 if (!url.is_valid()) 195 return false; 196 197 const GURL new_tab_url(GetNewTabPageURL(profile)); 198 if (new_tab_url.is_valid() && 199 search::MatchesOriginAndPath(url, new_tab_url)) 200 return true; 201 202 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 203 if (!template_url) 204 return false; 205 206 if (!IsSuitableURLForInstant(url, template_url)) 207 return false; 208 209 const TemplateURLRef& instant_url_ref = template_url->instant_url_ref(); 210 const GURL instant_url = 211 TemplateURLRefToGURL(instant_url_ref, kDisableStartMargin, false, false); 212 if (!instant_url.is_valid()) 213 return false; 214 215 if (search::MatchesOriginAndPath(url, instant_url)) 216 return true; 217 218 return IsQueryExtractionEnabled() && MatchesAnySearchURL(url, template_url); 219 } 220 221 base::string16 GetSearchTermsImpl(const content::WebContents* contents, 222 const content::NavigationEntry* entry) { 223 if (!contents || !IsQueryExtractionEnabled()) 224 return base::string16(); 225 226 // For security reasons, don't extract search terms if the page is not being 227 // rendered in the privileged Instant renderer process. This is to protect 228 // against a malicious page somehow scripting the search results page and 229 // faking search terms in the URL. Random pages can't get into the Instant 230 // renderer and scripting doesn't work cross-process, so if the page is in 231 // the Instant process, we know it isn't being exploited. 232 // Since iOS and Android doesn't use the instant framework, these checks are 233 // disabled for the two platforms. 234 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); 235 #if !defined(OS_IOS) && !defined(OS_ANDROID) 236 if (!IsRenderedInInstantProcess(contents, profile) && 237 ((entry == contents->GetController().GetLastCommittedEntry()) || 238 !ShouldAssignURLToInstantRenderer(entry->GetURL(), profile))) 239 return base::string16(); 240 #endif // !defined(OS_IOS) && !defined(OS_ANDROID) 241 // Check to see if search terms have already been extracted. 242 base::string16 search_terms = GetSearchTermsFromNavigationEntry(entry); 243 if (!search_terms.empty()) 244 return search_terms; 245 246 // Otherwise, extract from the URL. 247 return GetSearchTermsFromURL(profile, entry->GetVirtualURL()); 248 } 249 250 bool IsURLAllowedForSupervisedUser(const GURL& url, Profile* profile) { 251 #if defined(ENABLE_MANAGED_USERS) 252 ManagedUserService* managed_user_service = 253 ManagedUserServiceFactory::GetForProfile(profile); 254 ManagedModeURLFilter* url_filter = 255 managed_user_service->GetURLFilterForUIThread(); 256 if (url_filter->GetFilteringBehaviorForURL(url) == 257 ManagedModeURLFilter::BLOCK) { 258 return false; 259 } 260 #endif 261 return true; 262 } 263 264 } // namespace 265 266 // Negative start-margin values prevent the "es_sm" parameter from being used. 267 const int kDisableStartMargin = -1; 268 269 bool IsInstantExtendedAPIEnabled() { 270 #if defined(OS_IOS) || defined(OS_ANDROID) 271 return false; 272 #else 273 return true; 274 #endif // defined(OS_IOS) || defined(OS_ANDROID) 275 } 276 277 // Determine what embedded search page version to request from the user's 278 // default search provider. If 0, the embedded search UI should not be enabled. 279 uint64 EmbeddedSearchPageVersion() { 280 FieldTrialFlags flags; 281 if (GetFieldTrialInfo(&flags)) { 282 return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName, 283 kEmbeddedPageVersionDefault, 284 flags); 285 } 286 return kEmbeddedPageVersionDefault; 287 } 288 289 bool IsQueryExtractionEnabled() { 290 #if defined(OS_IOS) || defined(OS_ANDROID) 291 return true; 292 #else 293 if (!IsInstantExtendedAPIEnabled()) 294 return false; 295 296 const CommandLine* command_line = CommandLine::ForCurrentProcess(); 297 if (command_line->HasSwitch(switches::kEnableQueryExtraction)) 298 return true; 299 300 FieldTrialFlags flags; 301 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 302 kEnableQueryExtractionFlagName, false, flags); 303 #endif // defined(OS_IOS) || defined(OS_ANDROID) 304 } 305 306 base::string16 GetSearchTermsFromURL(Profile* profile, const GURL& url) { 307 if (url.is_valid() && url == GetSearchResultPrefetchBaseURL(profile)) { 308 // InstantSearchPrerenderer has the search query for the Instant search base 309 // page. 310 InstantSearchPrerenderer* prerenderer = 311 InstantSearchPrerenderer::GetForProfile(profile); 312 DCHECK(prerenderer); 313 return prerenderer->get_last_query(); 314 } 315 316 base::string16 search_terms; 317 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 318 if (template_url && IsSuitableURLForInstant(url, template_url)) 319 template_url->ExtractSearchTermsFromURL(url, &search_terms); 320 return search_terms; 321 } 322 323 base::string16 GetSearchTermsFromNavigationEntry( 324 const content::NavigationEntry* entry) { 325 base::string16 search_terms; 326 if (entry) 327 entry->GetExtraData(sessions::kSearchTermsKey, &search_terms); 328 return search_terms; 329 } 330 331 base::string16 GetSearchTerms(const content::WebContents* contents) { 332 if (!contents) 333 return base::string16(); 334 335 const content::NavigationEntry* entry = 336 contents->GetController().GetVisibleEntry(); 337 if (!entry) 338 return base::string16(); 339 340 #if !defined(OS_IOS) && !defined(OS_ANDROID) 341 // iOS and Android doesn't use the Instant framework, disable this check for 342 // the two platforms. 343 InstantSupportState state = GetInstantSupportStateFromNavigationEntry(*entry); 344 if (state == INSTANT_SUPPORT_NO) 345 return base::string16(); 346 #endif // !defined(OS_IOS) && !defined(OS_ANDROID) 347 348 return GetSearchTermsImpl(contents, entry); 349 } 350 351 bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) { 352 return url.is_valid() && 353 profile && 354 IsInstantExtendedAPIEnabled() && 355 (url.SchemeIs(chrome::kChromeSearchScheme) || 356 IsInstantURL(url, profile)); 357 } 358 359 bool ShouldUseProcessPerSiteForInstantURL(const GURL& url, Profile* profile) { 360 return ShouldAssignURLToInstantRenderer(url, profile) && 361 (url.host() == chrome::kChromeSearchLocalNtpHost || 362 url.host() == chrome::kChromeSearchOnlineNtpHost); 363 } 364 365 bool IsNTPURL(const GURL& url, Profile* profile) { 366 if (!url.is_valid()) 367 return false; 368 369 if (!IsInstantExtendedAPIEnabled()) 370 return url == GURL(chrome::kChromeUINewTabURL); 371 372 return profile && 373 ((IsInstantURL(url, profile) && 374 GetSearchTermsFromURL(profile, url).empty()) || 375 url == GURL(chrome::kChromeSearchLocalNtpUrl)); 376 } 377 378 bool IsInstantNTP(const content::WebContents* contents) { 379 if (!contents) 380 return false; 381 382 return NavEntryIsInstantNTP(contents, 383 contents->GetController().GetVisibleEntry()); 384 } 385 386 bool NavEntryIsInstantNTP(const content::WebContents* contents, 387 const content::NavigationEntry* entry) { 388 if (!contents || !entry || !IsInstantExtendedAPIEnabled()) 389 return false; 390 391 Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext()); 392 if (!IsRenderedInInstantProcess(contents, profile)) 393 return false; 394 395 if (entry->GetURL() == GetLocalInstantURL(profile)) 396 return true; 397 398 if (ShouldUseCacheableNTP()) { 399 GURL new_tab_url(GetNewTabPageURL(profile)); 400 return new_tab_url.is_valid() && 401 search::MatchesOriginAndPath(entry->GetURL(), new_tab_url); 402 } 403 404 return IsInstantURL(entry->GetVirtualURL(), profile) && 405 GetSearchTermsImpl(contents, entry).empty(); 406 } 407 408 bool IsSuggestPrefEnabled(Profile* profile) { 409 return profile && !profile->IsOffTheRecord() && profile->GetPrefs() && 410 profile->GetPrefs()->GetBoolean(prefs::kSearchSuggestEnabled); 411 } 412 413 GURL GetInstantURL(Profile* profile, int start_margin, 414 bool force_instant_results) { 415 if (!IsInstantExtendedAPIEnabled() || !IsSuggestPrefEnabled(profile)) 416 return GURL(); 417 418 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 419 if (!template_url) 420 return GURL(); 421 422 GURL instant_url = 423 TemplateURLRefToGURL(template_url->instant_url_ref(), start_margin, true, 424 force_instant_results); 425 if (!instant_url.is_valid() || 426 !template_url->HasSearchTermsReplacementKey(instant_url)) 427 return GURL(); 428 429 // Extended mode requires HTTPS. Force it unless the base URL was overridden 430 // on the command line, in which case we allow HTTP (see comments on 431 // IsSuitableURLForInstant()). 432 if (!instant_url.SchemeIsSecure() && 433 !google_util::StartsWithCommandLineGoogleBaseURL(instant_url)) { 434 GURL::Replacements replacements; 435 const std::string secure_scheme(content::kHttpsScheme); 436 replacements.SetSchemeStr(secure_scheme); 437 instant_url = instant_url.ReplaceComponents(replacements); 438 } 439 440 if (!IsURLAllowedForSupervisedUser(instant_url, profile)) 441 return GURL(); 442 443 return instant_url; 444 } 445 446 // Returns URLs associated with the default search engine for |profile|. 447 std::vector<GURL> GetSearchURLs(Profile* profile) { 448 std::vector<GURL> result; 449 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 450 if (!template_url) 451 return result; 452 for (size_t i = 0; i < template_url->URLCount(); ++i) { 453 TemplateURLRef ref(template_url, i); 454 result.push_back(TemplateURLRefToGURL(ref, kDisableStartMargin, false, 455 false)); 456 } 457 return result; 458 } 459 460 GURL GetNewTabPageURL(Profile* profile) { 461 if (!ShouldUseCacheableNTP()) 462 return GURL(); 463 464 if (!profile || profile->IsOffTheRecord()) 465 return GURL(); 466 467 if (!IsSuggestPrefEnabled(profile)) 468 return GURL(chrome::kChromeSearchLocalNtpUrl); 469 470 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 471 if (!template_url) 472 return GURL(chrome::kChromeSearchLocalNtpUrl); 473 474 GURL url(TemplateURLRefToGURL(template_url->new_tab_url_ref(), 475 kDisableStartMargin, false, false)); 476 if (!url.is_valid() || !url.SchemeIsSecure()) 477 return GURL(chrome::kChromeSearchLocalNtpUrl); 478 479 if (!IsURLAllowedForSupervisedUser(url, profile)) 480 return GURL(chrome::kChromeSearchLocalNtpUrl); 481 482 return url; 483 } 484 485 GURL GetSearchResultPrefetchBaseURL(Profile* profile) { 486 return ShouldPrefetchSearchResults() ? 487 GetInstantURL(profile, kDisableStartMargin, true) : GURL(); 488 } 489 490 bool ShouldPrefetchSearchResults() { 491 if (!ShouldUseCacheableNTP()) 492 return false; 493 494 FieldTrialFlags flags; 495 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 496 kPrefetchSearchResultsFlagName, false, flags); 497 } 498 499 GURL GetLocalInstantURL(Profile* profile) { 500 return GURL(chrome::kChromeSearchLocalNtpUrl); 501 } 502 503 bool ShouldHideTopVerbatimMatch() { 504 FieldTrialFlags flags; 505 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 506 kHideVerbatimFlagName, false, flags); 507 } 508 509 bool ShouldUseCacheableNTP() { 510 FieldTrialFlags flags; 511 return !GetFieldTrialInfo(&flags) || GetBoolValueForFlagWithDefault( 512 kUseCacheableNTP, true, flags); 513 } 514 515 bool ShouldShowInstantNTP() { 516 // If using the cacheable NTP, load the NTP directly instead of preloading its 517 // contents using InstantNTP. 518 if (ShouldUseCacheableNTP()) 519 return false; 520 521 FieldTrialFlags flags; 522 return !GetFieldTrialInfo(&flags) || 523 GetBoolValueForFlagWithDefault(kShowNtpFlagName, true, flags); 524 } 525 526 DisplaySearchButtonConditions GetDisplaySearchButtonConditions() { 527 const CommandLine* cl = CommandLine::ForCurrentProcess(); 528 if (cl->HasSwitch(switches::kDisableSearchButtonInOmnibox)) { 529 return DISPLAY_SEARCH_BUTTON_NEVER; 530 } else if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStr)) { 531 return DISPLAY_SEARCH_BUTTON_FOR_STR; 532 } else if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxForStrOrIip)) { 533 return DISPLAY_SEARCH_BUTTON_FOR_STR_OR_IIP; 534 } else if (cl->HasSwitch(switches::kEnableSearchButtonInOmniboxAlways)) { 535 return DISPLAY_SEARCH_BUTTON_ALWAYS; 536 } 537 538 FieldTrialFlags flags; 539 if (!GetFieldTrialInfo(&flags)) 540 return DISPLAY_SEARCH_BUTTON_NEVER; 541 uint64 value = 542 GetUInt64ValueForFlagWithDefault(kDisplaySearchButtonFlagName, 0, flags); 543 return (value < DISPLAY_SEARCH_BUTTON_NUM_VALUES) ? 544 static_cast<DisplaySearchButtonConditions>(value) : 545 DISPLAY_SEARCH_BUTTON_NEVER; 546 } 547 548 bool ShouldDisplayOriginChip() { 549 const CommandLine* cl = CommandLine::ForCurrentProcess(); 550 if (cl->HasSwitch(switches::kDisableOriginChip)) { 551 return false; 552 } else if (cl->HasSwitch(switches::kEnableOriginChip)) { 553 return true; 554 } 555 556 FieldTrialFlags flags; 557 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 558 kEnableOriginChipFlagName, false, flags); 559 } 560 561 GURL GetEffectiveURLForInstant(const GURL& url, Profile* profile) { 562 CHECK(ShouldAssignURLToInstantRenderer(url, profile)) 563 << "Error granting Instant access."; 564 565 if (url.SchemeIs(chrome::kChromeSearchScheme)) 566 return url; 567 568 GURL effective_url(url); 569 570 // Replace the scheme with "chrome-search:". 571 url_canon::Replacements<char> replacements; 572 std::string search_scheme(chrome::kChromeSearchScheme); 573 replacements.SetScheme(search_scheme.data(), 574 url_parse::Component(0, search_scheme.length())); 575 576 // If the URL corresponds to an online NTP, replace the host with 577 // "online-ntp". 578 std::string online_ntp_host(chrome::kChromeSearchOnlineNtpHost); 579 TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile); 580 if (template_url) { 581 const GURL instant_url = TemplateURLRefToGURL( 582 template_url->instant_url_ref(), kDisableStartMargin, false, false); 583 if (instant_url.is_valid() && 584 search::MatchesOriginAndPath(url, instant_url)) { 585 replacements.SetHost(online_ntp_host.c_str(), 586 url_parse::Component(0, online_ntp_host.length())); 587 } 588 } 589 590 effective_url = effective_url.ReplaceComponents(replacements); 591 return effective_url; 592 } 593 594 int GetInstantLoaderStalenessTimeoutSec() { 595 int timeout_sec = kStalePageTimeoutDefault; 596 FieldTrialFlags flags; 597 if (GetFieldTrialInfo(&flags)) { 598 timeout_sec = GetUInt64ValueForFlagWithDefault(kStalePageTimeoutFlagName, 599 kStalePageTimeoutDefault, 600 flags); 601 } 602 603 // Require a minimum 5 minute timeout. 604 if (timeout_sec < 0 || (timeout_sec > 0 && timeout_sec < 300)) 605 timeout_sec = kStalePageTimeoutDefault; 606 607 // Randomize by upto 15% either side. 608 timeout_sec = base::RandInt(timeout_sec * 0.85, timeout_sec * 1.15); 609 610 return timeout_sec; 611 } 612 613 bool IsPreloadedInstantExtendedNTP(const content::WebContents* contents) { 614 if (!IsInstantExtendedAPIEnabled()) 615 return false; 616 617 ProfileManager* profile_manager = g_browser_process->profile_manager(); 618 if (!profile_manager) 619 return false; // The profile manager can be NULL while testing. 620 621 const std::vector<Profile*>& profiles = profile_manager->GetLoadedProfiles(); 622 for (size_t i = 0; i < profiles.size(); ++i) { 623 const InstantService* instant_service = 624 InstantServiceFactory::GetForProfile(profiles[i]); 625 if (instant_service && instant_service->GetNTPContents() == contents) 626 return true; 627 } 628 return false; 629 } 630 631 bool HandleNewTabURLRewrite(GURL* url, 632 content::BrowserContext* browser_context) { 633 if (!IsInstantExtendedAPIEnabled()) 634 return false; 635 636 if (!url->SchemeIs(chrome::kChromeUIScheme) || 637 url->host() != chrome::kChromeUINewTabHost) 638 return false; 639 640 Profile* profile = Profile::FromBrowserContext(browser_context); 641 GURL new_tab_url(GetNewTabPageURL(profile)); 642 if (!new_tab_url.is_valid()) 643 return false; 644 645 *url = new_tab_url; 646 return true; 647 } 648 649 bool HandleNewTabURLReverseRewrite(GURL* url, 650 content::BrowserContext* browser_context) { 651 if (!IsInstantExtendedAPIEnabled()) 652 return false; 653 654 Profile* profile = Profile::FromBrowserContext(browser_context); 655 GURL new_tab_url(GetNewTabPageURL(profile)); 656 if (!new_tab_url.is_valid() || 657 !search::MatchesOriginAndPath(new_tab_url, *url)) 658 return false; 659 660 *url = GURL(chrome::kChromeUINewTabURL); 661 return true; 662 } 663 664 void SetInstantSupportStateInNavigationEntry(InstantSupportState state, 665 content::NavigationEntry* entry) { 666 if (!entry) 667 return; 668 669 entry->SetExtraData(kInstantSupportStateKey, 670 InstantSupportStateToString(state)); 671 } 672 673 InstantSupportState GetInstantSupportStateFromNavigationEntry( 674 const content::NavigationEntry& entry) { 675 base::string16 value; 676 if (!entry.GetExtraData(kInstantSupportStateKey, &value)) 677 return INSTANT_SUPPORT_UNKNOWN; 678 679 return StringToInstantSupportState(value); 680 } 681 682 bool ShouldPrefetchSearchResultsOnSRP() { 683 FieldTrialFlags flags; 684 return GetFieldTrialInfo(&flags) && GetBoolValueForFlagWithDefault( 685 kPrefetchSearchResultsOnSRP, false, flags); 686 } 687 688 void EnableQueryExtractionForTesting() { 689 CommandLine* cl = CommandLine::ForCurrentProcess(); 690 cl->AppendSwitch(switches::kEnableQueryExtraction); 691 } 692 693 bool GetFieldTrialInfo(FieldTrialFlags* flags) { 694 // Get the group name. If the EmbeddedSearch trial doesn't exist, look for 695 // the older InstantExtended name. 696 std::string group_name = base::FieldTrialList::FindFullName( 697 kEmbeddedSearchFieldTrialName); 698 if (group_name.empty()) { 699 group_name = base::FieldTrialList::FindFullName( 700 kInstantExtendedFieldTrialName); 701 } 702 703 if (EndsWith(group_name, kDisablingSuffix, true)) 704 return false; 705 706 // We have a valid trial that isn't disabled. Extract the flags. 707 std::string group_prefix(group_name); 708 size_t first_space = group_name.find(" "); 709 if (first_space != std::string::npos) { 710 // There is a flags section of the group name. Split that out and parse it. 711 group_prefix = group_name.substr(0, first_space); 712 if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space), 713 ':', ' ', flags)) { 714 // Failed to parse the flags section. Assume the whole group name is 715 // invalid. 716 return false; 717 } 718 } 719 return true; 720 } 721 722 // Given a FieldTrialFlags object, returns the string value of the provided 723 // flag. 724 std::string GetStringValueForFlagWithDefault(const std::string& flag, 725 const std::string& default_value, 726 const FieldTrialFlags& flags) { 727 FieldTrialFlags::const_iterator i; 728 for (i = flags.begin(); i != flags.end(); i++) { 729 if (i->first == flag) 730 return i->second; 731 } 732 return default_value; 733 } 734 735 // Given a FieldTrialFlags object, returns the uint64 value of the provided 736 // flag. 737 uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag, 738 uint64 default_value, 739 const FieldTrialFlags& flags) { 740 uint64 value; 741 std::string str_value = 742 GetStringValueForFlagWithDefault(flag, std::string(), flags); 743 if (base::StringToUint64(str_value, &value)) 744 return value; 745 return default_value; 746 } 747 748 // Given a FieldTrialFlags object, returns the boolean value of the provided 749 // flag. 750 bool GetBoolValueForFlagWithDefault(const std::string& flag, 751 bool default_value, 752 const FieldTrialFlags& flags) { 753 return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags); 754 } 755 756 } // namespace chrome 757