| Index: chrome/browser/ui/search/search.cc
|
| diff --git a/chrome/browser/ui/search/search.cc b/chrome/browser/ui/search/search.cc
|
| index a39700e7ae2756498cc7bd4c91fbd83312943fc8..194b6e15834f40b9003e7a850b927700e38129f9 100644
|
| --- a/chrome/browser/ui/search/search.cc
|
| +++ b/chrome/browser/ui/search/search.cc
|
| @@ -7,17 +7,17 @@
|
| #include "base/command_line.h"
|
| #include "base/metrics/field_trial.h"
|
| #include "base/string_split.h"
|
| -#include "base/string_util.h"
|
| #include "base/strings/string_number_conversions.h"
|
| +#include "chrome/browser/instant/instant_service.h"
|
| +#include "chrome/browser/instant/instant_service_factory.h"
|
| #include "chrome/browser/profiles/profile.h"
|
| +#include "chrome/browser/search_engines/template_url_service.h"
|
| +#include "chrome/browser/search_engines/template_url_service_factory.h"
|
| #include "chrome/common/chrome_switches.h"
|
| -#include "chrome/common/chrome_version_info.h"
|
| +#include "chrome/common/url_constants.h"
|
| #include "content/public/browser/navigation_entry.h"
|
| -
|
| -#if !defined(OS_ANDROID)
|
| -#include "chrome/browser/themes/theme_service.h"
|
| -#include "chrome/browser/themes/theme_service_factory.h"
|
| -#endif
|
| +#include "content/public/browser/render_process_host.h"
|
| +#include "content/public/browser/web_contents.h"
|
|
|
| namespace {
|
|
|
| @@ -25,12 +25,9 @@ namespace {
|
| // InstantExtended field trials are named in such a way that we can parse out
|
| // the experiment configuration from the trial's group name in order to give
|
| // us maximum flexability in running experiments.
|
| -// Field trials should be named things like "Group7 espv:2 themes:0".
|
| +// Field trial groups should be named things like "Group7 espv:2 instant:1".
|
| // The first token is always GroupN for some integer N, followed by a
|
| // space-delimited list of key:value pairs which correspond to these flags:
|
| -const char kEnableOnThemesFlagName[] = "themes";
|
| -const bool kEnableOnThemesDefault = false;
|
| -
|
| const char kEmbeddedPageVersionFlagName[] = "espv";
|
| const int kEmbeddedPageVersionDefault = 1;
|
|
|
| @@ -46,153 +43,270 @@ const char kGroupNumberPrefix[] = "Group";
|
| // be ignored and Instant Extended will not be enabled by default.
|
| const char kDisablingSuffix[] = "DISABLED";
|
|
|
| +chrome::search::InstantExtendedDefault InstantExtendedDefaultFromInt64(
|
| + int64 default_value) {
|
| + switch (default_value) {
|
| + case 0: return chrome::search::INSTANT_DEFAULT_ON;
|
| + case 1: return chrome::search::INSTANT_USE_EXISTING;
|
| + case 2: return chrome::search::INSTANT_DEFAULT_OFF;
|
| + default: return chrome::search::INSTANT_USE_EXISTING;
|
| + }
|
| +}
|
| +
|
| +TemplateURL* GetDefaultSearchProviderTemplateURL(Profile* profile) {
|
| + TemplateURLService* template_url_service =
|
| + TemplateURLServiceFactory::GetForProfile(profile);
|
| + if (template_url_service)
|
| + return template_url_service->GetDefaultSearchProvider();
|
| + return NULL;
|
| +}
|
| +
|
| +GURL TemplateURLRefToGURL(const TemplateURLRef& ref) {
|
| + return GURL(
|
| + ref.ReplaceSearchTerms(TemplateURLRef::SearchTermsArgs(string16())));
|
| +}
|
| +
|
| +bool MatchesOriginAndPath(const GURL& my_url, const GURL& other_url) {
|
| + return my_url.host() == other_url.host() &&
|
| + my_url.port() == other_url.port() &&
|
| + my_url.path() == other_url.path() &&
|
| + (my_url.scheme() == other_url.scheme() ||
|
| + (my_url.SchemeIs(chrome::kHttpsScheme) &&
|
| + other_url.SchemeIs(chrome::kHttpScheme)));
|
| +}
|
| +
|
| +bool IsCommandLineInstantURL(const GURL& url) {
|
| + const CommandLine* cl = CommandLine::ForCurrentProcess();
|
| + GURL instant_url(cl->GetSwitchValueASCII(switches::kInstantURL));
|
| + return instant_url.is_valid() && MatchesOriginAndPath(url, instant_url);
|
| +}
|
| +
|
| +// Coerces the commandline Instant URL to look like a template URL, so that we
|
| +// can extract search terms from it.
|
| +GURL CoerceCommandLineURLToTemplateURL(const GURL& instant_url,
|
| + const TemplateURLRef& ref) {
|
| + GURL search_url = TemplateURLRefToGURL(ref);
|
| +
|
| + GURL::Replacements replacements;
|
| + replacements.SetSchemeStr(chrome::kHttpsScheme);
|
| + replacements.SetHostStr(search_url.host());
|
| + replacements.SetPortStr(search_url.port());
|
| + replacements.SetPathStr(search_url.path());
|
| +
|
| + return instant_url.ReplaceComponents(replacements);
|
| +}
|
| +
|
| +bool MatchesAnySearchURL(const GURL& url, TemplateURL* template_url) {
|
| + GURL search_url = TemplateURLRefToGURL(template_url->url_ref());
|
| + if (search_url.is_valid() && MatchesOriginAndPath(url, search_url))
|
| + return true;
|
| +
|
| + // "URLCount() - 1" because we already tested url_ref above.
|
| + for (size_t i = 0; i < template_url->URLCount() - 1; ++i) {
|
| + TemplateURLRef ref(template_url, i);
|
| + search_url = TemplateURLRefToGURL(ref);
|
| + if (search_url.is_valid() && MatchesOriginAndPath(url, search_url))
|
| + return true;
|
| + }
|
| +
|
| + return false;
|
| +}
|
| +
|
| } // namespace
|
|
|
| namespace chrome {
|
| namespace search {
|
|
|
| -// static
|
| const char kInstantExtendedSearchTermsKey[] = "search_terms";
|
|
|
| -InstantExtendedDefault InstantExtendedDefaultFromInt64(int64 default_value) {
|
| - switch (default_value) {
|
| - case 0: return INSTANT_FORCE_ON;
|
| - case 1: return INSTANT_USE_EXISTING;
|
| - case 2: return INSTANT_FORCE_OFF;
|
| - default: return INSTANT_USE_EXISTING;
|
| - }
|
| -}
|
| +const char kLocalOmniboxPopupURL[] =
|
| + "chrome://local-omnibox-popup/local-omnibox-popup.html";
|
|
|
| InstantExtendedDefault GetInstantExtendedDefaultSetting() {
|
| - InstantExtendedDefault default_setting = INSTANT_USE_EXISTING;
|
| -
|
| FieldTrialFlags flags;
|
| if (GetFieldTrialInfo(
|
| base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName),
|
| &flags, NULL)) {
|
| uint64 trial_default = GetUInt64ValueForFlagWithDefault(
|
| - kInstantExtendedActivationName,
|
| - kInstantExtendedActivationDefault,
|
| - flags);
|
| - default_setting = InstantExtendedDefaultFromInt64(trial_default);
|
| + kInstantExtendedActivationName,
|
| + kInstantExtendedActivationDefault,
|
| + flags);
|
| + return InstantExtendedDefaultFromInt64(trial_default);
|
| }
|
|
|
| - return default_setting;
|
| + return INSTANT_USE_EXISTING;
|
| }
|
|
|
| -// Check whether or not the Extended API should be used on the given profile.
|
| -bool IsInstantExtendedAPIEnabled(Profile* profile) {
|
| +bool IsInstantExtendedAPIEnabled(const Profile* profile) {
|
| return EmbeddedSearchPageVersion(profile) != 0;
|
| }
|
|
|
| // Determine what embedded search page version to request from the user's
|
| -// default search provider. If 0, the embedded search UI should not be enabled.
|
| -// Note that the profile object here isn't const because we need to determine
|
| -// whether or not the user has a theme installed as part of this check, and
|
| -// that logic requires a non-const profile for whatever reason.
|
| -uint64 EmbeddedSearchPageVersion(Profile* profile) {
|
| - // Incognito windows do not currently use the embedded search API.
|
| +// default search provider. If 0, the embedded search UI should not be enabled.
|
| +uint64 EmbeddedSearchPageVersion(const Profile* profile) {
|
| if (!profile || profile->IsOffTheRecord())
|
| return 0;
|
|
|
| - // Check Finch field trials.
|
| FieldTrialFlags flags;
|
| if (GetFieldTrialInfo(
|
| base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName),
|
| &flags, NULL)) {
|
| - uint64 espv = GetUInt64ValueForFlagWithDefault(
|
| - kEmbeddedPageVersionFlagName,
|
| - kEmbeddedPageVersionDefault,
|
| - flags);
|
| -
|
| - // Check for themes.
|
| - bool has_theme = false;
|
| -#if !defined(OS_ANDROID)
|
| - has_theme =
|
| - !ThemeServiceFactory::GetForProfile(profile)->UsingDefaultTheme();
|
| -#endif
|
| -
|
| - bool enable_for_themes =
|
| - GetBoolValueForFlagWithDefault(kEnableOnThemesFlagName,
|
| - kEnableOnThemesDefault,
|
| - flags);
|
| - if (!has_theme || enable_for_themes)
|
| - return espv;
|
| + return GetUInt64ValueForFlagWithDefault(kEmbeddedPageVersionFlagName,
|
| + kEmbeddedPageVersionDefault,
|
| + flags);
|
| }
|
|
|
| - if (CommandLine::ForCurrentProcess()->HasSwitch(
|
| - switches::kEnableInstantExtendedAPI)) {
|
| + const CommandLine* cl = CommandLine::ForCurrentProcess();
|
| + if (cl->HasSwitch(switches::kEnableInstantExtendedAPI)) {
|
| // The user has manually flipped the about:flags switch - give the default
|
| // UI version.
|
| return kEmbeddedPageVersionDefault;
|
| }
|
| - return 0;
|
| -}
|
|
|
| -void EnableInstantExtendedAPIForTesting() {
|
| - CommandLine::ForCurrentProcess()->AppendSwitch(
|
| - switches::kEnableInstantExtendedAPI);
|
| + return 0;
|
| }
|
|
|
| -bool IsQueryExtractionEnabled(Profile* profile) {
|
| +bool IsQueryExtractionEnabled(const Profile* profile) {
|
| #if defined(OS_IOS)
|
| - return CommandLine::ForCurrentProcess()->HasSwitch(
|
| - switches::kEnableQueryExtraction);
|
| + const CommandLine* cl = CommandLine::ForCurrentProcess();
|
| + return cl->HasSwitch(switches::kEnableQueryExtraction);
|
| #else
|
| - if (!profile || profile->IsOffTheRecord())
|
| - return false;
|
| -
|
| // On desktop, query extraction is controlled by the instant-extended-api
|
| // flag.
|
| - bool enabled = IsInstantExtendedAPIEnabled(profile);
|
| -
|
| - // Running with --enable-query-extraction but not
|
| - // --enable-instant-extended-api is an error.
|
| - DCHECK(!(CommandLine::ForCurrentProcess()->HasSwitch(
|
| - switches::kEnableQueryExtraction) &&
|
| - !enabled));
|
| - return enabled;
|
| + return IsInstantExtendedAPIEnabled(profile);
|
| #endif
|
| }
|
|
|
| +string16 GetSearchTermsFromNavigationEntry(
|
| + const content::NavigationEntry* entry) {
|
| + string16 search_terms;
|
| + if (entry)
|
| + entry->GetExtraData(kInstantExtendedSearchTermsKey, &search_terms);
|
| + return search_terms;
|
| +}
|
| +
|
| +string16 GetSearchTerms(const content::WebContents* contents) {
|
| + if (!contents)
|
| + return string16();
|
| +
|
| + Profile* profile = Profile::FromBrowserContext(contents->GetBrowserContext());
|
| + if (!IsQueryExtractionEnabled(profile))
|
| + return string16();
|
| +
|
| + // For security reasons, don't extract search terms if the page is not being
|
| + // rendered in the privileged Instant renderer process. This is to protect
|
| + // against a malicious page somehow scripting the search results page and
|
| + // faking search terms in the URL. Random pages can't get into the Instant
|
| + // renderer and scripting doesn't work cross-process, so if the page is in
|
| + // the Instant process, we know it isn't being exploited.
|
| + const content::RenderProcessHost* process_host =
|
| + contents->GetRenderProcessHost();
|
| + if (!process_host)
|
| + return string16();
|
| +
|
| + const InstantService* instant_service =
|
| + InstantServiceFactory::GetForProfile(profile);
|
| + if (!instant_service)
|
| + return string16();
|
| +
|
| + if (!instant_service->IsInstantProcess(process_host->GetID()))
|
| + return string16();
|
| +
|
| + // Check to see if search terms have already been extracted.
|
| + const content::NavigationEntry* entry =
|
| + contents->GetController().GetVisibleEntry();
|
| + if (!entry)
|
| + return string16();
|
| +
|
| + string16 search_terms = GetSearchTermsFromNavigationEntry(entry);
|
| + if (!search_terms.empty())
|
| + return search_terms;
|
| +
|
| + // Otherwise, extract from the URL.
|
| + TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
|
| + if (!template_url)
|
| + return string16();
|
| +
|
| + GURL url = entry->GetVirtualURL();
|
| +
|
| + if (IsCommandLineInstantURL(url))
|
| + url = CoerceCommandLineURLToTemplateURL(url, template_url->url_ref());
|
| +
|
| + if (url.SchemeIsSecure() && template_url->HasSearchTermsReplacementKey(url))
|
| + template_url->ExtractSearchTermsFromURL(url, &search_terms);
|
| +
|
| + return search_terms;
|
| +}
|
| +
|
| +bool ShouldAssignURLToInstantRenderer(const GURL& url, Profile* profile) {
|
| + TemplateURL* template_url = GetDefaultSearchProviderTemplateURL(profile);
|
| + if (!template_url)
|
| + return false;
|
| +
|
| + GURL effective_url = url;
|
| +
|
| + if (IsCommandLineInstantURL(url)) {
|
| + const TemplateURLRef& instant_url_ref = template_url->instant_url_ref();
|
| + effective_url = CoerceCommandLineURLToTemplateURL(url, instant_url_ref);
|
| + }
|
| +
|
| + return ShouldAssignURLToInstantRendererImpl(
|
| + effective_url,
|
| + IsInstantExtendedAPIEnabled(profile),
|
| + template_url);
|
| +}
|
| +
|
| +void EnableInstantExtendedAPIForTesting() {
|
| + CommandLine* cl = CommandLine::ForCurrentProcess();
|
| + cl->AppendSwitch(switches::kEnableInstantExtendedAPI);
|
| +}
|
| +
|
| void EnableQueryExtractionForTesting() {
|
| #if defined(OS_IOS)
|
| - CommandLine::ForCurrentProcess()->AppendSwitch(
|
| - switches::kEnableQueryExtraction);
|
| + CommandLine* cl = CommandLine::ForCurrentProcess();
|
| + cl->AppendSwitch(switches::kEnableQueryExtraction);
|
| #else
|
| - // On desktop, query extraction is controlled by the instant-extended-api
|
| - // flag.
|
| - CommandLine::ForCurrentProcess()->AppendSwitch(
|
| - switches::kEnableInstantExtendedAPI);
|
| + EnableInstantExtendedAPIForTesting();
|
| #endif
|
| }
|
|
|
| -string16 GetSearchTermsFromNavigationEntry(
|
| - const content::NavigationEntry* entry) {
|
| - string16 search_terms;
|
| - entry->GetExtraData(kInstantExtendedSearchTermsKey, &search_terms);
|
| - return search_terms;
|
| -}
|
| +bool ShouldAssignURLToInstantRendererImpl(const GURL& url,
|
| + bool extended_api_enabled,
|
| + TemplateURL* template_url) {
|
| + if (!url.is_valid())
|
| + return false;
|
| +
|
| + if (url.SchemeIs(chrome::kChromeSearchScheme))
|
| + return true;
|
|
|
| -bool IsForcedInstantURL(const GURL& url) {
|
| - CommandLine* command_line = CommandLine::ForCurrentProcess();
|
| - if (!command_line->HasSwitch(switches::kInstantURL))
|
| + if (extended_api_enabled && url == GURL(kLocalOmniboxPopupURL))
|
| + return true;
|
| +
|
| + if (extended_api_enabled && !url.SchemeIsSecure())
|
| + return false;
|
| +
|
| + if (extended_api_enabled && !template_url->HasSearchTermsReplacementKey(url))
|
| + return false;
|
| +
|
| + GURL instant_url = TemplateURLRefToGURL(template_url->instant_url_ref());
|
| + if (!instant_url.is_valid())
|
| return false;
|
|
|
| - GURL instant_url(command_line->GetSwitchValueASCII(switches::kInstantURL));
|
| - return url.scheme() == instant_url.scheme() &&
|
| - url.host() == instant_url.host() &&
|
| - url.port() == instant_url.port() &&
|
| - url.path() == instant_url.path();
|
| + if (MatchesOriginAndPath(url, instant_url))
|
| + return true;
|
| +
|
| + if (extended_api_enabled && MatchesAnySearchURL(url, template_url))
|
| + return true;
|
| +
|
| + return false;
|
| }
|
|
|
| bool GetFieldTrialInfo(const std::string& group_name,
|
| FieldTrialFlags* flags,
|
| uint64* group_number) {
|
| if (EndsWith(group_name, kDisablingSuffix, true) ||
|
| - !StartsWithASCII(group_name, kGroupNumberPrefix, true)) {
|
| + !StartsWithASCII(group_name, kGroupNumberPrefix, true))
|
| return false;
|
| - }
|
|
|
| // We have a valid trial that starts with "Group" and isn't disabled.
|
| // First extract the flags.
|
| @@ -200,8 +314,7 @@ bool GetFieldTrialInfo(const std::string& group_name,
|
|
|
| size_t first_space = group_name.find(" ");
|
| if (first_space != std::string::npos) {
|
| - // There is a flags section of the group name. Split that out and parse
|
| - // it.
|
| + // There is a flags section of the group name. Split that out and parse it.
|
| group_prefix = group_name.substr(0, first_space);
|
| if (!base::SplitStringIntoKeyValuePairs(group_name.substr(first_space),
|
| ':', ' ', flags)) {
|
| @@ -213,11 +326,10 @@ bool GetFieldTrialInfo(const std::string& group_name,
|
|
|
| // Now extract the group number, making sure we get a non-zero value.
|
| uint64 temp_group_number = 0;
|
| - if (!base::StringToUint64(group_prefix.substr(strlen(kGroupNumberPrefix)),
|
| - &temp_group_number) ||
|
| - temp_group_number == 0) {
|
| + std::string group_suffix = group_prefix.substr(strlen(kGroupNumberPrefix));
|
| + if (!base::StringToUint64(group_suffix, &temp_group_number) ||
|
| + temp_group_number == 0)
|
| return false;
|
| - }
|
|
|
| if (group_number)
|
| *group_number = temp_group_number;
|
| @@ -227,10 +339,9 @@ bool GetFieldTrialInfo(const std::string& group_name,
|
|
|
| // Given a FieldTrialFlags object, returns the string value of the provided
|
| // flag.
|
| -std::string GetStringValueForFlagWithDefault(
|
| - const std::string& flag,
|
| - const std::string& default_value,
|
| - FieldTrialFlags& flags) {
|
| +std::string GetStringValueForFlagWithDefault(const std::string& flag,
|
| + const std::string& default_value,
|
| + const FieldTrialFlags& flags) {
|
| FieldTrialFlags::const_iterator i;
|
| for (i = flags.begin(); i != flags.end(); i++) {
|
| if (i->first == flag)
|
| @@ -241,19 +352,21 @@ std::string GetStringValueForFlagWithDefault(
|
|
|
| // Given a FieldTrialFlags object, returns the uint64 value of the provided
|
| // flag.
|
| -uint64 GetUInt64ValueForFlagWithDefault(
|
| - const std::string& flag, uint64 default_value, FieldTrialFlags& flags) {
|
| +uint64 GetUInt64ValueForFlagWithDefault(const std::string& flag,
|
| + uint64 default_value,
|
| + const FieldTrialFlags& flags) {
|
| uint64 value;
|
| - if (!base::StringToUint64(GetStringValueForFlagWithDefault(flag, "", flags),
|
| - &value))
|
| - return default_value;
|
| - return value;
|
| + std::string str_value = GetStringValueForFlagWithDefault(flag, "", flags);
|
| + if (base::StringToUint64(str_value, &value))
|
| + return value;
|
| + return default_value;
|
| }
|
|
|
| // Given a FieldTrialFlags object, returns the boolean value of the provided
|
| // flag.
|
| -bool GetBoolValueForFlagWithDefault(
|
| - const std::string& flag, bool default_value, FieldTrialFlags& flags) {
|
| +bool GetBoolValueForFlagWithDefault(const std::string& flag,
|
| + bool default_value,
|
| + const FieldTrialFlags& flags) {
|
| return !!GetUInt64ValueForFlagWithDefault(flag, default_value ? 1 : 0, flags);
|
| }
|
|
|
|
|