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