diff options
author | kmadhusu@chromium.org <kmadhusu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-27 05:33:07 +0000 |
---|---|---|
committer | kmadhusu@chromium.org <kmadhusu@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-08-27 05:33:07 +0000 |
commit | 13b2f209caf0296f502154ad1912b1ce9fbf93a2 (patch) | |
tree | 14cb637e1248df2cbbcfd3b643cfd493ff9bd171 | |
parent | 4d11593da862bdbf5f647b6fee7744370c7236f3 (diff) | |
download | chromium_src-13b2f209caf0296f502154ad1912b1ce9fbf93a2.zip chromium_src-13b2f209caf0296f502154ad1912b1ce9fbf93a2.tar.gz chromium_src-13b2f209caf0296f502154ad1912b1ce9fbf93a2.tar.bz2 |
InstantExtended: Simplified Instant search.
Enables result page to prefetch results for certain search suggestions via field trial.
Omnibox suggest response includes metadata regarding the prefetch query. SearchProvider parses the suggest response and records the additional details in AutocompleteMatch. When the user types in the omnibox, browser sends a message to the renderer to set the prefetch query on the server which in turn sends the prefetched search results back to Chrome.
BUG=256857
TEST=none
Review URL: https://chromiumcodereview.appspot.com/19772008
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@219700 0039d316-1c4b-4281-b951-d872f2087c98
27 files changed, 603 insertions, 102 deletions
diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc index 38f0b9e..5b366b6 100644 --- a/chrome/browser/autocomplete/search_provider.cc +++ b/chrome/browser/autocomplete/search_provider.cc @@ -136,9 +136,11 @@ SearchProvider::Result::~Result() { SearchProvider::SuggestResult::SuggestResult(const string16& suggestion, bool from_keyword_provider, int relevance, - bool relevance_from_server) + bool relevance_from_server, + bool should_prefetch) : Result(from_keyword_provider, relevance, relevance_from_server), - suggestion_(suggestion) { + suggestion_(suggestion), + should_prefetch_(should_prefetch) { } SearchProvider::SuggestResult::~SuggestResult() { @@ -212,6 +214,7 @@ void SearchProvider::Results::Clear() { suggest_results.clear(); navigation_results.clear(); verbatim_relevance = -1; + metadata.clear(); } bool SearchProvider::Results::HasServerProvidedScores() const { @@ -243,6 +246,8 @@ const int SearchProvider::kDefaultProviderURLFetcherID = 1; const int SearchProvider::kKeywordProviderURLFetcherID = 2; int SearchProvider::kMinimumTimeBetweenSuggestQueriesMs = 100; const char SearchProvider::kRelevanceFromServerKey[] = "relevance_from_server"; +const char SearchProvider::kShouldPrefetchKey[] = "should_prefetch"; +const char SearchProvider::kSuggestMetadataKey[] = "suggest_metadata"; const char SearchProvider::kTrue[] = "true"; const char SearchProvider::kFalse[] = "false"; @@ -352,6 +357,16 @@ AutocompleteMatch SearchProvider::CreateSearchSuggestion( return match; } +// static +bool SearchProvider::ShouldPrefetch(const AutocompleteMatch& match) { + return match.GetAdditionalInfo(kShouldPrefetchKey) == kTrue; +} + +// static +std::string SearchProvider::GetSuggestMetadata(const AutocompleteMatch& match) { + return match.GetAdditionalInfo(kSuggestMetadataKey); +} + void SearchProvider::AddProviderInfo(ProvidersInfo* provider_info) const { provider_info->push_back(metrics::OmniboxEventProto_ProviderInfo()); metrics::OmniboxEventProto_ProviderInfo& new_entry = provider_info->back(); @@ -881,6 +896,7 @@ bool SearchProvider::ParseSuggestResults(Value* root_val, bool is_keyword) { ListValue* types = NULL; ListValue* relevances = NULL; DictionaryValue* extras = NULL; + int prefetch_index = -1; if (root_list->GetDictionary(4, &extras)) { extras->GetList("google:suggesttype", &types); @@ -897,6 +913,16 @@ bool SearchProvider::ParseSuggestResults(Value* root_val, bool is_keyword) { extras->GetBoolean("google:fieldtrialtriggered", &triggered); field_trial_triggered_ |= triggered; field_trial_triggered_in_session_ |= triggered; + + // Extract the prefetch hint. + DictionaryValue* client_data = NULL; + if (extras->GetDictionary("google:clientdata", &client_data) && client_data) + client_data->GetInteger("phi", &prefetch_index); + + // Store the metadata that came with the response in case we need to pass it + // along with the prefetch query to Instant. + JSONStringValueSerializer json_serializer(&results->metadata); + json_serializer.Serialize(*extras); } // Clear the previous results now that new results are available. @@ -925,9 +951,10 @@ bool SearchProvider::ParseSuggestResults(Value* root_val, bool is_keyword) { *this, url, title, is_keyword, relevance, true)); } } else { + bool should_prefetch = static_cast<int>(index) == prefetch_index; // TODO(kochi): Improve calculator result presentation. results->suggest_results.push_back( - SuggestResult(result, is_keyword, relevance, true)); + SuggestResult(result, is_keyword, relevance, true, should_prefetch)); } } @@ -965,7 +992,7 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { TemplateURLRef::NO_SUGGESTION_CHOSEN; if (verbatim_relevance > 0) { AddMatchToMap(input_.text(), input_.text(), verbatim_relevance, - relevance_from_server, + relevance_from_server, false, std::string(), AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, did_not_accept_default_suggestion, false, &map); } @@ -984,6 +1011,7 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { if (keyword_verbatim_relevance > 0) { AddMatchToMap(keyword_input_.text(), keyword_input_.text(), keyword_verbatim_relevance, keyword_relevance_from_server, + false, std::string(), AutocompleteMatchType::SEARCH_OTHER_ENGINE, did_not_accept_keyword_suggestion, true, &map); } @@ -994,8 +1022,9 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { AddHistoryResultsToMap(default_history_results_, false, did_not_accept_default_suggestion, &map); - AddSuggestResultsToMap(keyword_results_.suggest_results, &map); - AddSuggestResultsToMap(default_results_.suggest_results, &map); + AddSuggestResultsToMap(keyword_results_.suggest_results, std::string(), &map); + AddSuggestResultsToMap(default_results_.suggest_results, + default_results_.metadata, &map); ACMatches matches; for (MatchMap::const_iterator i(map.begin()); i != map.end(); ++i) @@ -1209,9 +1238,8 @@ void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, for (SuggestResults::const_iterator i(scored_results.begin()); i != scored_results.end(); ++i) { AddMatchToMap(i->suggestion(), input_text, i->relevance(), false, - AutocompleteMatchType::SEARCH_HISTORY, - did_not_accept_suggestion, - is_keyword, map); + false, std::string(), AutocompleteMatchType::SEARCH_HISTORY, + did_not_accept_suggestion, is_keyword, map); } } @@ -1258,7 +1286,7 @@ SearchProvider::SuggestResults SearchProvider::ScoreHistoryResults( i->time, is_keyword, !prevent_inline_autocomplete, prevent_search_history_inlining); scored_results.push_back( - SuggestResult(i->term, is_keyword, relevance, false)); + SuggestResult(i->term, is_keyword, relevance, false, false)); } // History returns results sorted for us. However, we may have docked some @@ -1280,12 +1308,14 @@ SearchProvider::SuggestResults SearchProvider::ScoreHistoryResults( } void SearchProvider::AddSuggestResultsToMap(const SuggestResults& results, + const std::string& metadata, MatchMap* map) { for (size_t i = 0; i < results.size(); ++i) { const bool is_keyword = results[i].from_keyword_provider(); const string16& input = is_keyword ? keyword_input_.text() : input_.text(); AddMatchToMap(results[i].suggestion(), input, results[i].relevance(), results[i].relevance_from_server(), + results[i].should_prefetch(), metadata, AutocompleteMatchType::SEARCH_SUGGEST, i, is_keyword, map); } } @@ -1402,6 +1432,8 @@ void SearchProvider::AddMatchToMap(const string16& query_string, const string16& input_text, int relevance, bool relevance_from_server, + bool should_prefetch, + const std::string& metadata, AutocompleteMatch::Type type, int accepted_suggestion, bool is_keyword, @@ -1431,22 +1463,50 @@ void SearchProvider::AddMatchToMap(const string16& query_string, return; match.RecordAdditionalInfo(kRelevanceFromServerKey, relevance_from_server ? kTrue : kFalse); + match.RecordAdditionalInfo(kShouldPrefetchKey, + should_prefetch ? kTrue : kFalse); + + // Metadata is needed only for prefetching queries. + if (should_prefetch) + match.RecordAdditionalInfo(kSuggestMetadataKey, metadata); // Try to add |match| to |map|. If a match for |query_string| is already in // |map|, replace it if |match| is more relevant. // NOTE: Keep this ToLower() call in sync with url_database.cc. const std::pair<MatchMap::iterator, bool> i( map->insert(std::make_pair(base::i18n::ToLower(query_string), match))); - // NOTE: We purposefully do a direct relevance comparison here instead of - // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items added - // first" rather than "items alphabetically first" when the scores are equal. - // The only case this matters is when a user has results with the same score - // that differ only by capitalization; because the history system returns - // results sorted by recency, this means we'll pick the most recent such - // result even if the precision of our relevance score is too low to - // distinguish the two. - if (!i.second && (match.relevance > i.first->second.relevance)) - i.first->second = match; + + if (!i.second) { + // NOTE: We purposefully do a direct relevance comparison here instead of + // using AutocompleteMatch::MoreRelevant(), so that we'll prefer "items + // added first" rather than "items alphabetically first" when the scores are + // equal. The only case this matters is when a user has results with the + // same score that differ only by capitalization; because the history system + // returns results sorted by recency, this means we'll pick the most + // recent such result even if the precision of our relevance score is too + // low to distinguish the two. + if (match.relevance > i.first->second.relevance) { + i.first->second = match; + } else if (match.keyword == i.first->second.keyword) { + // Old and new matches are from the same search provider. It is okay to + // record one match's prefetch data onto a different match (for the same + // query string) for the following reasons: + // 1. Because the suggest server only sends down a query string from which + // we construct a URL, rather than sending a full URL, and because we + // construct URLs from query strings in the same way every time, the URLs + // for the two matches will be the same. Therefore, we won't end up + // prefetching something the server didn't intend. + // 2. Presumably the server sets the prefetch bit on a match it things is + // sufficiently relevant that the user is likely to choose it. Surely + // setting the prefetch bit on a match of even higher relevance won't + // violate this assumption. + should_prefetch |= ShouldPrefetch(i.first->second); + i.first->second.RecordAdditionalInfo(kShouldPrefetchKey, + should_prefetch ? kTrue : kFalse); + if (should_prefetch) + i.first->second.RecordAdditionalInfo(kSuggestMetadataKey, metadata); + } + } } AutocompleteMatch SearchProvider::NavigationToMatch( @@ -1513,6 +1573,7 @@ AutocompleteMatch SearchProvider::NavigationToMatch( match.RecordAdditionalInfo( kRelevanceFromServerKey, navigation.relevance_from_server() ? kTrue : kFalse); + match.RecordAdditionalInfo(kShouldPrefetchKey, kFalse); return match; } diff --git a/chrome/browser/autocomplete/search_provider.h b/chrome/browser/autocomplete/search_provider.h index 281684d..5993a46 100644 --- a/chrome/browser/autocomplete/search_provider.h +++ b/chrome/browser/autocomplete/search_provider.h @@ -90,6 +90,14 @@ class SearchProvider : public AutocompleteProvider, int omnibox_start_margin, bool append_extra_query_params); + // Returns whether the SearchProvider previously flagged |match| as a query + // that should be prefetched. + static bool ShouldPrefetch(const AutocompleteMatch& match); + + // Extracts the suggest response metadata which SearchProvider previously + // stored for |match|. + static std::string GetSuggestMetadata(const AutocompleteMatch& match); + // AutocompleteProvider: virtual void AddProviderInfo(ProvidersInfo* provider_info) const OVERRIDE; virtual void ResetSession() OVERRIDE; @@ -109,6 +117,8 @@ class SearchProvider : public AutocompleteProvider, FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, RemoveStaleResultsTest); FRIEND_TEST_ALL_PREFIXES(SearchProviderTest, SuggestRelevanceExperiment); FRIEND_TEST_ALL_PREFIXES(AutocompleteProviderTest, GetDestinationURL); + FRIEND_TEST_ALL_PREFIXES(InstantExtendedPrefetchTest, ClearPrefetchedResults); + FRIEND_TEST_ALL_PREFIXES(InstantExtendedPrefetchTest, SetPrefetchQuery); // Manages the providers (TemplateURLs) used by SearchProvider. Two providers // may be used: @@ -213,10 +223,12 @@ class SearchProvider : public AutocompleteProvider, SuggestResult(const string16& suggestion, bool from_keyword_provider, int relevance, - bool relevance_from_server); + bool relevance_from_server, + bool should_prefetch); virtual ~SuggestResult(); const string16& suggestion() const { return suggestion_; } + bool should_prefetch() const { return should_prefetch_; } // Result: virtual bool IsInlineable(const string16& input) const OVERRIDE; @@ -227,6 +239,9 @@ class SearchProvider : public AutocompleteProvider, private: // The search suggestion string. string16 suggestion_; + + // Should this result be prefetched? + bool should_prefetch_; }; class NavigationResult : public Result { @@ -298,6 +313,9 @@ class SearchProvider : public AutocompleteProvider, // suppresses the verbatim result. int verbatim_relevance; + // The JSON metadata associated with this server response. + std::string metadata; + private: DISALLOW_COPY_AND_ASSIGN(Results); }; @@ -404,7 +422,9 @@ class SearchProvider : public AutocompleteProvider, bool is_keyword); // Adds matches for |results| to |map|. - void AddSuggestResultsToMap(const SuggestResults& results, MatchMap* map); + void AddSuggestResultsToMap(const SuggestResults& results, + const std::string& metadata, + MatchMap* map); // Gets the relevance score for the verbatim result. This value may be // provided by the suggest server or calculated locally; if @@ -449,6 +469,8 @@ class SearchProvider : public AutocompleteProvider, const string16& input_text, int relevance, bool relevance_from_server, + bool should_prefetch, + const std::string& metadata, AutocompleteMatch::Type type, int accepted_suggestion, bool is_keyword, @@ -474,10 +496,20 @@ class SearchProvider : public AutocompleteProvider, // previous one. Non-const because some unittests modify this value. static int kMinimumTimeBetweenSuggestQueriesMs; + // The following keys are used to record additional information on matches. + // We annotate our AutocompleteMatches with whether their relevance scores // were server-provided using this key in the |additional_info| field. static const char kRelevanceFromServerKey[]; - // These are the values we record with the above key. + + // Indicates whether the server said a match should be prefetched. + static const char kShouldPrefetchKey[]; + + // Used to store metadata from the server response, which is needed for + // prefetching. + static const char kSuggestMetadataKey[]; + + // These are the values for the above keys. static const char kTrue[]; static const char kFalse[]; diff --git a/chrome/browser/autocomplete/search_provider_unittest.cc b/chrome/browser/autocomplete/search_provider_unittest.cc index 5aa485f..7df07d2 100644 --- a/chrome/browser/autocomplete/search_provider_unittest.cc +++ b/chrome/browser/autocomplete/search_provider_unittest.cc @@ -2429,7 +2429,7 @@ TEST_F(SearchProviderTest, RemoveStaleResultsTest) { provider_->default_results_.suggest_results.push_back( SearchProvider::SuggestResult(ASCIIToUTF16(suggestion), false, cases[i].results[j].relevance, - false)); + false, false)); } } @@ -2468,3 +2468,137 @@ TEST_F(SearchProviderTest, RemoveStaleResultsTest) { EXPECT_EQ(nav_end, nav_it); } } + +// A basic test that verifies the prefetch metadata parsing logic. +TEST_F(SearchProviderTest, PrefetchMetadataParsing) { + struct Match { + std::string contents; + bool allowed_to_be_prefetched; + AutocompleteMatchType::Type type; + bool from_keyword; + }; + const Match kEmptyMatch = { kNotApplicable, + false, + AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, + false }; + + struct { + const std::string input_text; + bool prefer_keyword_provider_results; + const std::string default_provider_response_json; + const std::string keyword_provider_response_json; + const Match matches[5]; + } cases[] = { + // Default provider response does not have prefetch details. Ensure that the + // suggestions are not marked as prefetch query. + { "a", + false, + "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", + std::string(), + { { "a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, + { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, + { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, + kEmptyMatch, + kEmptyMatch + }, + }, + // Ensure that default provider suggest response prefetch details are + // parsed and recorded in AutocompleteMatch. + { "ab", + false, + "[\"ab\",[\"abc\", \"http://b.com\", \"http://c.com\"],[],[]," + "{\"google:clientdata\":{\"phi\": 0}," + "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\", \"NAVIGATION\"]," + "\"google:suggestrelevance\":[999, 12, 1]}]", + std::string(), + { { "ab", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, + { "abc", true, AutocompleteMatchType::SEARCH_SUGGEST, false }, + { "b.com", false, AutocompleteMatchType::NAVSUGGEST, false }, + { "c.com", false, AutocompleteMatchType::NAVSUGGEST, false }, + kEmptyMatch + }, + }, + // Default provider suggest response has prefetch details. + // SEARCH_WHAT_YOU_TYPE suggestion outranks SEARCH_SUGGEST suggestion for + // the same query string. Ensure that the prefetch details from + // SEARCH_SUGGEST match are set onto SEARCH_WHAT_YOU_TYPE match. + { "ab", + false, + "[\"ab\",[\"ab\", \"http://ab.com\"],[],[]," + "{\"google:clientdata\":{\"phi\": 0}," + "\"google:suggesttype\":[\"QUERY\", \"NAVIGATION\"]," + "\"google:suggestrelevance\":[99, 98]}]", + std::string(), + { {"ab", true, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, + {"ab.com", false, AutocompleteMatchType::NAVSUGGEST, false }, + kEmptyMatch, + kEmptyMatch, + kEmptyMatch + }, + }, + // Default provider response has prefetch details. We prefer keyword + // provider results. Ensure that prefetch bit for a suggestion from the + // default search provider does not get copied onto a higher-scoring match + // for the same query string from the keyword provider. + { "k a", + true, + "[\"k a\",[\"a\", \"ab\"],[],[], {\"google:clientdata\":{\"phi\": 0}," + "\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," + "\"google:suggestrelevance\":[9, 12]}]", + "[\"a\",[\"b\", \"c\"],[],[],{\"google:suggestrelevance\":[1, 2]}]", + { { "a", false, AutocompleteMatchType::SEARCH_OTHER_ENGINE, true}, + { "k a", false, AutocompleteMatchType::SEARCH_WHAT_YOU_TYPED, false }, + { "ab", false, AutocompleteMatchType::SEARCH_SUGGEST, false }, + { "c", false, AutocompleteMatchType::SEARCH_SUGGEST, true }, + { "b", false, AutocompleteMatchType::SEARCH_SUGGEST, true } + }, + } + }; + + for (size_t i = 0; i < ARRAYSIZE_UNSAFE(cases); i++) { + QueryForInput(ASCIIToUTF16(cases[i].input_text), false, + cases[i].prefer_keyword_provider_results); + + // Set up a default fetcher with provided results. + net::TestURLFetcher* fetcher = + test_factory_.GetFetcherByID( + SearchProvider::kDefaultProviderURLFetcherID); + ASSERT_TRUE(fetcher); + fetcher->set_response_code(200); + fetcher->SetResponseString(cases[i].default_provider_response_json); + fetcher->delegate()->OnURLFetchComplete(fetcher); + + if (cases[i].prefer_keyword_provider_results) { + // Set up a keyword fetcher with provided results. + net::TestURLFetcher* keyword_fetcher = + test_factory_.GetFetcherByID( + SearchProvider::kKeywordProviderURLFetcherID); + ASSERT_TRUE(keyword_fetcher); + keyword_fetcher->set_response_code(200); + keyword_fetcher->SetResponseString( + cases[i].keyword_provider_response_json); + keyword_fetcher->delegate()->OnURLFetchComplete(keyword_fetcher); + keyword_fetcher = NULL; + } + + RunTillProviderDone(); + + const std::string description = + "for input with json =" + cases[i].default_provider_response_json; + const ACMatches& matches = provider_->matches(); + // The top match must inline and score as highly as calculated verbatim. + ASSERT_FALSE(matches.empty()); + EXPECT_GE(matches[0].relevance, 1300); + + // Ensure that the returned matches equal the expectations. + for (size_t j = 0; j < matches.size(); ++j) { + SCOPED_TRACE(description); + EXPECT_EQ(cases[i].matches[j].contents, UTF16ToUTF8(matches[j].contents)); + EXPECT_EQ(cases[i].matches[j].allowed_to_be_prefetched, + SearchProvider::ShouldPrefetch(matches[j])); + EXPECT_EQ(cases[i].matches[j].type, matches[j].type); + EXPECT_EQ(cases[i].matches[j].from_keyword, + matches[j].keyword == ASCIIToUTF16("k")); + } + } +} diff --git a/chrome/browser/autocomplete/zero_suggest_provider.cc b/chrome/browser/autocomplete/zero_suggest_provider.cc index 4369aba..b4cdb55 100644 --- a/chrome/browser/autocomplete/zero_suggest_provider.cc +++ b/chrome/browser/autocomplete/zero_suggest_provider.cc @@ -296,7 +296,7 @@ void ZeroSuggestProvider::FillResults( } } else { suggest_results->push_back(SearchProvider::SuggestResult( - result, false, relevance, relevances != NULL)); + result, false, relevance, relevances != NULL, false)); } } } diff --git a/chrome/browser/search/search.cc b/chrome/browser/search/search.cc index 17b7854..de725fb 100644 --- a/chrome/browser/search/search.cc +++ b/chrome/browser/search/search.cc @@ -63,6 +63,7 @@ const char kUseRemoteNTPOnStartupFlagName[] = "use_remote_ntp_on_startup"; const char kShowNtpFlagName[] = "show_ntp"; const char kRecentTabsOnNTPFlagName[] = "show_recent_tabs"; const char kUseCacheableNTP[] = "use_cacheable_ntp"; +const char kPrefetchSearchResultsOnSRP[] = "prefetch_results_srp"; // Constants for the field trial name and group prefix. const char kInstantExtendedFieldTrialName[] = "InstantExtended"; @@ -632,6 +633,25 @@ InstantSupportState GetInstantSupportStateFromNavigationEntry( return StringToInstantSupportState(value); } +bool ShouldPrefetchSearchResultsOnSRP() { + // Check the command-line/about:flags setting first, which should have + // precedence and allows the trial to not be reported (if it's never queried). + const CommandLine* command_line = CommandLine::ForCurrentProcess(); + if (command_line->HasSwitch(switches::kDisableInstantExtendedAPI) || + command_line->HasSwitch(switches::kEnableInstantExtendedAPI)) { + return false; + } + + FieldTrialFlags flags; + if (GetFieldTrialInfo( + base::FieldTrialList::FindFullName(kInstantExtendedFieldTrialName), + &flags, NULL)) { + return GetBoolValueForFlagWithDefault(kPrefetchSearchResultsOnSRP, false, + flags); + } + return false; +} + void EnableInstantExtendedAPIForTesting() { CommandLine* cl = CommandLine::ForCurrentProcess(); cl->AppendSwitch(switches::kEnableInstantExtendedAPI); diff --git a/chrome/browser/search/search.h b/chrome/browser/search/search.h index 5888529..716fa95 100644 --- a/chrome/browser/search/search.h +++ b/chrome/browser/search/search.h @@ -186,6 +186,9 @@ void SetInstantSupportStateInNavigationEntry(InstantSupportState state, InstantSupportState GetInstantSupportStateFromNavigationEntry( const content::NavigationEntry& entry); +// Returns true if the field trial flag is enabled to prefetch results on SRP. +bool ShouldPrefetchSearchResultsOnSRP(); + // ----------------------------------------------------- // The following APIs are exposed for use in tests only. // ----------------------------------------------------- diff --git a/chrome/browser/ui/browser_instant_controller.cc b/chrome/browser/ui/browser_instant_controller.cc index 2e6c12df2..662eca7 100644 --- a/chrome/browser/ui/browser_instant_controller.cc +++ b/chrome/browser/ui/browser_instant_controller.cc @@ -240,6 +240,11 @@ void BrowserInstantController::SetOmniboxBounds(const gfx::Rect& bounds) { instant_.SetOmniboxBounds(bounds); } +void BrowserInstantController::SetSuggestionToPrefetch( + const InstantSuggestion& suggestion) { + instant_.SetSuggestionToPrefetch(suggestion); +} + void BrowserInstantController::ToggleVoiceSearch() { instant_.ToggleVoiceSearch(); } diff --git a/chrome/browser/ui/browser_instant_controller.h b/chrome/browser/ui/browser_instant_controller.h index 8ddcb48..66598ef 100644 --- a/chrome/browser/ui/browser_instant_controller.h +++ b/chrome/browser/ui/browser_instant_controller.h @@ -80,6 +80,9 @@ class BrowserInstantController : public SearchModelObserver { // Sets the stored omnibox bounds. void SetOmniboxBounds(const gfx::Rect& bounds); + // Sets the current query to prefetch if any. + void SetSuggestionToPrefetch(const InstantSuggestion& suggestion); + // Notifies |instant_| to toggle voice search. void ToggleVoiceSearch(); diff --git a/chrome/browser/ui/omnibox/omnibox_controller.cc b/chrome/browser/ui/omnibox/omnibox_controller.cc index a15dd14..94ba872 100644 --- a/chrome/browser/ui/omnibox/omnibox_controller.cc +++ b/chrome/browser/ui/omnibox/omnibox_controller.cc @@ -19,9 +19,38 @@ #include "chrome/browser/ui/omnibox/omnibox_edit_model.h" #include "chrome/browser/ui/omnibox/omnibox_popup_model.h" #include "chrome/browser/ui/omnibox/omnibox_popup_view.h" +#include "chrome/browser/ui/search/instant_controller.h" +#include "chrome/common/instant_types.h" #include "extensions/common/constants.h" #include "ui/gfx/rect.h" +namespace { + +// Returns the AutocompleteMatch that the InstantController should prefetch, if +// any. +// +// The SearchProvider may mark some suggestions to be prefetched based on +// instructions from the suggest server. If such a match ranks sufficiently +// highly, we'll return it. We only care about matches that are the default or +// else the very first entry in the dropdown (which can happen for non-default +// matches only if we're hiding a top verbatim match); for other matches, we +// think the likelihood of the user selecting them is low enough that +// prefetching isn't worth doing. +const AutocompleteMatch* GetMatchToPrefetch(const AutocompleteResult& result) { + const AutocompleteResult::const_iterator default_match( + result.default_match()); + if (default_match == result.end()) + return NULL; + + if (SearchProvider::ShouldPrefetch(*default_match)) + return &(*default_match); + + return (result.ShouldHideTopMatch() && (result.size() > 1) && + SearchProvider::ShouldPrefetch(result.match_at(1))) ? + &result.match_at(1) : NULL; +} + +} // namespace OmniboxController::OmniboxController(OmniboxEditModel* omnibox_edit_model, Profile* profile) @@ -68,6 +97,22 @@ void OmniboxController::OnResultChanged(bool default_match_changed) { if (!prerender::IsOmniboxEnabled(profile_)) DoPreconnect(*match); omnibox_edit_model_->OnCurrentMatchChanged(); + + if (chrome::IsInstantExtendedAPIEnabled() && + omnibox_edit_model_->GetInstantController()) { + InstantSuggestion prefetch_suggestion; + const AutocompleteMatch* match_to_prefetch = GetMatchToPrefetch(result); + if (match_to_prefetch) { + prefetch_suggestion.text = match_to_prefetch->contents; + prefetch_suggestion.metadata = + SearchProvider::GetSuggestMetadata(*match_to_prefetch); + } + // Send the prefetch suggestion unconditionally to the InstantPage. If + // there is no suggestion to prefetch, we need to send a blank query to + // clear the prefetched results. + omnibox_edit_model_->GetInstantController()->SetSuggestionToPrefetch( + prefetch_suggestion); + } } else { InvalidateCurrentMatch(); popup_->OnResultChanged(); diff --git a/chrome/browser/ui/search/instant_controller.cc b/chrome/browser/ui/search/instant_controller.cc index 3b42999..2c43c22 100644 --- a/chrome/browser/ui/search/instant_controller.cc +++ b/chrome/browser/ui/search/instant_controller.cc @@ -122,6 +122,12 @@ void InstantController::SetOmniboxBounds(const gfx::Rect& bounds) { instant_tab_->sender()->SetOmniboxBounds(omnibox_bounds_); } +void InstantController::SetSuggestionToPrefetch( + const InstantSuggestion& suggestion) { + if (instant_tab_ && search_mode_.is_search()) + instant_tab_->sender()->SetSuggestionToPrefetch(suggestion); +} + void InstantController::ToggleVoiceSearch() { if (instant_tab_) instant_tab_->sender()->ToggleVoiceSearch(); diff --git a/chrome/browser/ui/search/instant_controller.h b/chrome/browser/ui/search/instant_controller.h index 412a9b3..d088111 100644 --- a/chrome/browser/ui/search/instant_controller.h +++ b/chrome/browser/ui/search/instant_controller.h @@ -58,6 +58,9 @@ class InstantController : public InstantPage::Delegate { // Sets the stored start-edge margin and width of the omnibox. void SetOmniboxBounds(const gfx::Rect& bounds); + // Sends the current SearchProvider suggestion to the Instant page if any. + void SetSuggestionToPrefetch(const InstantSuggestion& suggestion); + // Notifies |instant_Tab_| to toggle voice search. void ToggleVoiceSearch(); diff --git a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc index dd6544b..9678d76 100644 --- a/chrome/browser/ui/search/instant_extended_interactive_uitest.cc +++ b/chrome/browser/ui/search/instant_extended_interactive_uitest.cc @@ -26,6 +26,7 @@ #include "chrome/browser/extensions/extension_browsertest.h" #include "chrome/browser/extensions/extension_service.h" #include "chrome/browser/favicon/favicon_tab_helper.h" +#include "chrome/browser/google/google_url_tracker.h" #include "chrome/browser/history/history_db_task.h" #include "chrome/browser/history/history_service.h" #include "chrome/browser/history/history_service_factory.h" @@ -74,6 +75,8 @@ #include "content/public/test/test_utils.h" #include "grit/generated_resources.h" #include "net/base/network_change_notifier.h" +#include "net/url_request/test_url_fetcher_factory.h" +#include "net/url_request/url_fetcher_impl.h" #include "testing/gmock/include/gmock/gmock.h" #include "third_party/skia/include/core/SkBitmap.h" #include "ui/base/l10n/l10n_util.h" @@ -155,7 +158,7 @@ class InstantExtendedTest : public InProcessBrowserTest, ASSERT_TRUE(https_test_server().Start()); GURL instant_url = https_test_server().GetURL( "files/instant_extended.html?strk=1&"); - InstantTestBase::Init(instant_url); + InstantTestBase::Init(instant_url, false); } int64 GetHistogramCount(const char* name) { @@ -209,7 +212,9 @@ class InstantExtendedTest : public InProcessBrowserTest, GetBoolFromJS(contents, "isFocused", &is_focused_) && GetIntFromJS(contents, "onToggleVoiceSearchCalls", - &on_toggle_voice_search_calls_); + &on_toggle_voice_search_calls_) && + GetStringFromJS(contents, "prefetchQuery", &prefetch_query_value_); + } TemplateURL* GetDefaultSearchProviderTemplateURL() { @@ -264,6 +269,29 @@ class InstantExtendedTest : public InProcessBrowserTest, int on_focus_changed_calls_; bool is_focused_; int on_toggle_voice_search_calls_; + std::string prefetch_query_value_; +}; + +class InstantExtendedPrefetchTest : public InstantExtendedTest { + public: + InstantExtendedPrefetchTest() + : factory_(new net::FakeURLFetcherFactory( + net::URLFetcherImpl::factory())) { + } + + virtual void SetUpInProcessBrowserTestFixture() OVERRIDE { + chrome::EnableInstantExtendedAPIForTesting(); + ASSERT_TRUE(https_test_server().Start()); + GURL instant_url = https_test_server().GetURL( + "files/instant_extended.html?strk=1&"); + InstantTestBase::Init(instant_url, true); + factory_->SetFakeResponse(GoogleURLTracker::kSearchDomainCheckURL, + ".google.com", true); + } + + scoped_ptr<net::FakeURLFetcherFactory> factory_; + + DISALLOW_COPY_AND_ASSIGN(InstantExtendedPrefetchTest); }; class InstantExtendedNetworkTest : public InstantExtendedTest { @@ -302,7 +330,7 @@ class InstantPolicyTest : public ExtensionBrowserTest, public InstantTestBase { ASSERT_TRUE(https_test_server().Start()); GURL instant_url = https_test_server().GetURL( "files/instant_extended.html?strk=1&"); - InstantTestBase::Init(instant_url); + InstantTestBase::Init(instant_url, false); } void InstallThemeSource() { @@ -1653,3 +1681,93 @@ IN_PROC_BROWSER_TEST_F(InstantExtendedTest, // Make sure the URL remains the same. EXPECT_EQ(ntp_url, ntp_contents->GetURL()); } + +IN_PROC_BROWSER_TEST_F(InstantExtendedPrefetchTest, SetPrefetchQuery) { + ASSERT_NO_FATAL_FAILURE(SetupInstant(browser())); + FocusOmniboxAndWaitForInstantNTPSupport(); + + content::WindowedNotificationObserver new_tab_observer( + content::NOTIFICATION_NAV_ENTRY_COMMITTED, + content::NotificationService::AllSources()); + ui_test_utils::NavigateToURLWithDisposition( + browser(), + GURL(chrome::kChromeUINewTabURL), + CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + new_tab_observer.Wait(); + + omnibox()->model()->autocomplete_controller()->search_provider()-> + kMinimumTimeBetweenSuggestQueriesMs = 0; + + // Set the fake response for suggest request. Response has prefetch details. + // Ensure that the page received the prefetch query. + factory_->SetFakeResponse( + instant_url().spec() + "#q=pupp", + "[\"pupp\",[\"puppy\", \"puppies\"],[],[]," + "{\"google:clientdata\":{\"phi\": 0}," + "\"google:suggesttype\":[\"QUERY\", \"QUERY\"]," + "\"google:suggestrelevance\":[1400, 9]}]", + true); + + SetOmniboxText("pupp"); + while (!omnibox()->model()->autocomplete_controller()->done()) { + content::WindowedNotificationObserver ready_observer( + chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY, + content::Source<AutocompleteController>( + omnibox()->model()->autocomplete_controller())); + ready_observer.Wait(); + } + + ASSERT_EQ(3, CountSearchProviderSuggestions()); + content::WebContents* active_tab = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(UpdateSearchState(active_tab)); + ASSERT_TRUE(SearchProvider::ShouldPrefetch(*( + omnibox()->model()->result().default_match()))); + ASSERT_EQ("puppy", prefetch_query_value_); +} + +IN_PROC_BROWSER_TEST_F(InstantExtendedPrefetchTest, ClearPrefetchedResults) { + ASSERT_NO_FATAL_FAILURE(SetupInstant(browser())); + FocusOmniboxAndWaitForInstantNTPSupport(); + + content::WindowedNotificationObserver new_tab_observer( + content::NOTIFICATION_NAV_ENTRY_COMMITTED, + content::NotificationService::AllSources()); + ui_test_utils::NavigateToURLWithDisposition( + browser(), + GURL(chrome::kChromeUINewTabURL), + CURRENT_TAB, + ui_test_utils::BROWSER_TEST_NONE); + new_tab_observer.Wait(); + + omnibox()->model()->autocomplete_controller()->search_provider()-> + kMinimumTimeBetweenSuggestQueriesMs = 0; + + // Set the fake response for suggest request. Response has no prefetch + // details. Ensure that the page received a blank query to clear the + // prefetched results. + factory_->SetFakeResponse( + instant_url().spec() + "#q=dogs", + "[\"dogs\",[\"https://dogs.com\"],[],[]," + "{\"google:suggesttype\":[\"NAVIGATION\"]," + "\"google:suggestrelevance\":[2]}]", + true); + + SetOmniboxText("dogs"); + while (!omnibox()->model()->autocomplete_controller()->done()) { + content::WindowedNotificationObserver ready_observer( + chrome::NOTIFICATION_AUTOCOMPLETE_CONTROLLER_RESULT_READY, + content::Source<AutocompleteController>( + omnibox()->model()->autocomplete_controller())); + ready_observer.Wait(); + } + + ASSERT_EQ(2, CountSearchProviderSuggestions()); + ASSERT_FALSE(SearchProvider::ShouldPrefetch(*( + omnibox()->model()->result().default_match()))); + content::WebContents* active_tab = + browser()->tab_strip_model()->GetActiveWebContents(); + ASSERT_TRUE(UpdateSearchState(active_tab)); + ASSERT_EQ("", prefetch_query_value_); +} diff --git a/chrome/browser/ui/search/instant_ipc_sender.cc b/chrome/browser/ui/search/instant_ipc_sender.cc index 62387e6..3744743 100644 --- a/chrome/browser/ui/search/instant_ipc_sender.cc +++ b/chrome/browser/ui/search/instant_ipc_sender.cc @@ -35,6 +35,12 @@ class InstantIPCSenderImpl : public InstantIPCSender { routing_id(), is_app_launcher_enabled)); } + virtual void SetSuggestionToPrefetch( + const InstantSuggestion& suggestion) OVERRIDE { + Send(new ChromeViewMsg_SearchBoxSetSuggestionToPrefetch(routing_id(), + suggestion)); + } + virtual void SendThemeBackgroundInfo( const ThemeBackgroundInfo& theme_info) OVERRIDE { Send(new ChromeViewMsg_SearchBoxThemeChanged(routing_id(), theme_info)); diff --git a/chrome/browser/ui/search/instant_ipc_sender.h b/chrome/browser/ui/search/instant_ipc_sender.h index d9e4cd8..af43445 100644 --- a/chrome/browser/ui/search/instant_ipc_sender.h +++ b/chrome/browser/ui/search/instant_ipc_sender.h @@ -45,6 +45,9 @@ class InstantIPCSender : public content::WebContentsObserver { // Tells the page information it needs to display promos. virtual void SetPromoInformation(bool is_app_launcher_enabled) {} + // Tells the page the suggestion to be prefetched if any. + virtual void SetSuggestionToPrefetch(const InstantSuggestion& suggestion) {} + // Tells the page about the current theme background. virtual void SendThemeBackgroundInfo( const ThemeBackgroundInfo& theme_info) {} diff --git a/chrome/browser/ui/search/instant_test_utils.cc b/chrome/browser/ui/search/instant_test_utils.cc index 50eb25c..dfa5038 100644 --- a/chrome/browser/ui/search/instant_test_utils.cc +++ b/chrome/browser/ui/search/instant_test_utils.cc @@ -48,6 +48,8 @@ void InstantTestBase::SetupInstant(Browser* browser) { data.SetURL(instant_url_.spec() + "q={searchTerms}&is_search&{google:omniboxStartMarginParameter}"); data.instant_url = instant_url_.spec(); + if (init_suggestions_url_) + data.suggestions_url = instant_url_.spec() + "#q={searchTerms}"; data.alternate_urls.push_back(instant_url_.spec() + "#q={searchTerms}"); data.search_terms_replacement_key = "strk"; @@ -75,8 +77,9 @@ void InstantTestBase::SetInstantURL(const std::string& url) { service->SetDefaultSearchProvider(template_url); } -void InstantTestBase::Init(const GURL& instant_url) { +void InstantTestBase::Init(const GURL& instant_url, bool init_suggestions_url) { instant_url_ = instant_url; + init_suggestions_url_ = init_suggestions_url; } void InstantTestBase::FocusOmnibox() { diff --git a/chrome/browser/ui/search/instant_test_utils.h b/chrome/browser/ui/search/instant_test_utils.h index 47c273d..c232b7d 100644 --- a/chrome/browser/ui/search/instant_test_utils.h +++ b/chrome/browser/ui/search/instant_test_utils.h @@ -37,13 +37,14 @@ class InstantTestBase { : https_test_server_( net::SpawnedTestServer::TYPE_HTTPS, net::BaseTestServer::SSLOptions(), - base::FilePath(FILE_PATH_LITERAL("chrome/test/data"))) { + base::FilePath(FILE_PATH_LITERAL("chrome/test/data"))), + init_suggestions_url_(false) { } virtual ~InstantTestBase() {} protected: void SetupInstant(Browser* browser); - void Init(const GURL& instant_url); + void Init(const GURL& instant_url, bool init_suggestions_url); void SetInstantURL(const std::string& url); @@ -109,6 +110,9 @@ class InstantTestBase { // HTTPS Testing server, started on demand. net::SpawnedTestServer https_test_server_; + // Set to true to initialize suggestions URL in default search provider. + bool init_suggestions_url_; + DISALLOW_COPY_AND_ASSIGN(InstantTestBase); }; diff --git a/chrome/browser/ui/search/local_ntp_browsertest.cc b/chrome/browser/ui/search/local_ntp_browsertest.cc index da42ae6c..19cf564 100644 --- a/chrome/browser/ui/search/local_ntp_browsertest.cc +++ b/chrome/browser/ui/search/local_ntp_browsertest.cc @@ -26,7 +26,7 @@ class LocalNTPTest : public InProcessBrowserTest, ASSERT_TRUE(https_test_server().Start()); GURL instant_url = https_test_server().GetURL( "files/local_ntp_browsertest.html?strk=1&"); - InstantTestBase::Init(instant_url); + InstantTestBase::Init(instant_url, false); } }; diff --git a/chrome/browser/ui/search/search_tab_helper.cc b/chrome/browser/ui/search/search_tab_helper.cc index 077e7f6..7eb3791 100644 --- a/chrome/browser/ui/search/search_tab_helper.cc +++ b/chrome/browser/ui/search/search_tab_helper.cc @@ -152,6 +152,17 @@ void SearchTabHelper::Observe( if (!load_details->is_main_frame) return; + // TODO(kmadhusu): Set the page initial states (such as omnibox margin, etc) + // from here. Please refer to crbug.com/247517 for more details. + Profile* profile = + Profile::FromBrowserContext(web_contents_->GetBrowserContext()); + if (chrome::ShouldAssignURLToInstantRenderer(web_contents_->GetURL(), + profile)) { + Send(new ChromeViewMsg_SearchBoxSetDisplayInstantResults( + routing_id(), + chrome::ShouldPrefetchSearchResultsOnSRP())); + } + UpdateMode(true, false); content::NavigationEntry* entry = diff --git a/chrome/common/instant_types.cc b/chrome/common/instant_types.cc index 9fa7149..ea744018 100644 --- a/chrome/common/instant_types.cc +++ b/chrome/common/instant_types.cc @@ -4,35 +4,18 @@ #include "chrome/common/instant_types.h" -InstantSuggestion::InstantSuggestion() - : behavior(INSTANT_COMPLETE_NOW), - type(INSTANT_SUGGESTION_SEARCH), - autocomplete_match_index(kNoMatchIndex) { +InstantSuggestion::InstantSuggestion() { } InstantSuggestion::InstantSuggestion(const string16& in_text, - InstantCompleteBehavior in_behavior, - InstantSuggestionType in_type, - const string16& in_query, - size_t in_autocomplete_match_index) + const std::string& in_metadata) : text(in_text), - behavior(in_behavior), - type(in_type), - query(in_query), - autocomplete_match_index(in_autocomplete_match_index) { + metadata(in_metadata) { } InstantSuggestion::~InstantSuggestion() { } -InstantAutocompleteResult::InstantAutocompleteResult() - : transition(content::PAGE_TRANSITION_LINK), - relevance(0) { -} - -InstantAutocompleteResult::~InstantAutocompleteResult() { -} - RGBAColor::RGBAColor() : r(0), g(0), @@ -69,7 +52,6 @@ ThemeBackgroundInfo::ThemeBackgroundInfo() ThemeBackgroundInfo::~ThemeBackgroundInfo() { } - bool ThemeBackgroundInfo::operator==(const ThemeBackgroundInfo& rhs) const { return using_default_theme == rhs.using_default_theme && background_color == rhs.background_color && diff --git a/chrome/common/instant_types.h b/chrome/common/instant_types.h index c88e1a9..8ef078b 100644 --- a/chrome/common/instant_types.h +++ b/chrome/common/instant_types.h @@ -18,59 +18,19 @@ // Visited items) that the Instant page needs access to. typedef int InstantRestrictedID; -const size_t kNoMatchIndex = -1; - -// Ways that the Instant suggested text is autocompleted into the omnibox. -enum InstantCompleteBehavior { - // Autocomplete the suggestion immediately. - INSTANT_COMPLETE_NOW, - - // Do not autocomplete the suggestion. The suggestion may still be displayed - // in the omnibox, but not made a part of the omnibox text by default (e.g., - // by displaying the suggestion as non-highlighted, non-selected gray text). - INSTANT_COMPLETE_NEVER, - - // Treat the suggested text as the entire omnibox text, effectively replacing - // whatever the user has typed. - INSTANT_COMPLETE_REPLACE, -}; - -// The type of suggestion provided by Instant. For example, if Instant suggests -// "yahoo.com", should that be considered a search string or a URL? -enum InstantSuggestionType { - INSTANT_SUGGESTION_SEARCH, - INSTANT_SUGGESTION_URL, -}; - -// A wrapper to hold Instant suggested text and its metadata. +// A wrapper to hold Instant suggested text and its metadata. Used to tell the +// server what suggestion to prefetch. struct InstantSuggestion { InstantSuggestion(); - InstantSuggestion(const string16& text, - InstantCompleteBehavior behavior, - InstantSuggestionType type, - const string16& query, - size_t autocomplete_match_index); + InstantSuggestion(const string16& in_text, + const std::string& in_metadata); ~InstantSuggestion(); // Full suggested text. string16 text; - // Completion behavior for the suggestion. - InstantCompleteBehavior behavior; - - // Is this a search or a URL suggestion? - InstantSuggestionType type; - - // Query for which this suggestion was generated. May be set to empty string - // if unknown. - string16 query; - - // Index of the AutocompleteMatch in AutocompleteResult. Used to get the - // metadata details of the suggested text from AutocompleteResult. Set to a - // positive value if the suggestion is displayed on the Local NTP and - // set to kNoMatchIndex if the suggestion is displayed on the - // Instant NTP. - size_t autocomplete_match_index; + // JSON metadata from the server response which produced this suggestion. + std::string metadata; }; // Omnibox dropdown matches provided by the native autocomplete providers. diff --git a/chrome/common/render_messages.h b/chrome/common/render_messages.h index 41de013..4ddfca3 100644 --- a/chrome/common/render_messages.h +++ b/chrome/common/render_messages.h @@ -162,6 +162,11 @@ IPC_STRUCT_TRAITS_BEGIN(ContentSettingPatternSource) IPC_STRUCT_TRAITS_MEMBER(incognito) IPC_STRUCT_TRAITS_END() +IPC_STRUCT_TRAITS_BEGIN(InstantSuggestion) + IPC_STRUCT_TRAITS_MEMBER(text) + IPC_STRUCT_TRAITS_MEMBER(metadata) +IPC_STRUCT_TRAITS_END() + IPC_STRUCT_TRAITS_BEGIN(InstantMostVisitedItem) IPC_STRUCT_TRAITS_MEMBER(url) IPC_STRUCT_TRAITS_MEMBER(title) @@ -291,6 +296,9 @@ IPC_MESSAGE_ROUTED3(ChromeViewMsg_HandleMessageFromExternalHost, IPC_MESSAGE_ROUTED0(ChromeViewMsg_DetermineIfPageSupportsInstant) +IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxSetDisplayInstantResults, + bool /* display_instant_results */) + IPC_MESSAGE_ROUTED2(ChromeViewMsg_SearchBoxFocusChanged, OmniboxFocusState /* new_focus_state */, OmniboxFocusChangeReason /* reason */) @@ -312,6 +320,9 @@ IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxPromoInformation, IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxSetInputInProgress, bool /* input_in_progress */) +IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxSetSuggestionToPrefetch, + InstantSuggestion /* suggestion */) + IPC_MESSAGE_ROUTED1(ChromeViewMsg_SearchBoxSubmit, string16 /* value */) diff --git a/chrome/renderer/resources/extensions/searchbox_api.js b/chrome/renderer/resources/extensions/searchbox_api.js index 3aa3d53..ef5a8ddb 100644 --- a/chrome/renderer/resources/extensions/searchbox_api.js +++ b/chrome/renderer/resources/extensions/searchbox_api.js @@ -13,12 +13,14 @@ if (!chrome.embeddedSearch) { // ======================================================================= // Private functions // ======================================================================= + native function GetDisplayInstantResults(); native function GetFont(); native function GetFontSize(); native function GetMostVisitedItemData(); native function GetQuery(); native function GetRightToLeft(); native function GetStartMargin(); + native function GetSuggestionToPrefetch(); native function IsFocused(); native function IsKeyCaptureEnabled(); native function Paste(); @@ -29,12 +31,14 @@ if (!chrome.embeddedSearch) { // ======================================================================= // Exported functions // ======================================================================= + this.__defineGetter__('displayInstantResults', GetDisplayInstantResults); this.__defineGetter__('font', GetFont); this.__defineGetter__('fontSize', GetFontSize); this.__defineGetter__('isFocused', IsFocused); this.__defineGetter__('isKeyCaptureEnabled', IsKeyCaptureEnabled); this.__defineGetter__('rtl', GetRightToLeft); this.__defineGetter__('startMargin', GetStartMargin); + this.__defineGetter__('suggestion', GetSuggestionToPrefetch); this.__defineGetter__('value', GetQuery); // This method is restricted to chrome-search://most-visited pages by @@ -63,10 +67,10 @@ if (!chrome.embeddedSearch) { this.onkeycapturechange = null; this.onmarginchange = null; this.onsubmit = null; + this.onsuggestionchange = null; this.ontogglevoicesearch = null; - // TODO(jered): Remove these when google no longer requires them. - this.displayInstantResults = false; + //TODO(jered): Remove this empty method when google no longer requires it. this.setRestrictedValue = function() {}; }; diff --git a/chrome/renderer/searchbox/searchbox.cc b/chrome/renderer/searchbox/searchbox.cc index ad5d08b..8130cd0 100644 --- a/chrome/renderer/searchbox/searchbox.cc +++ b/chrome/renderer/searchbox/searchbox.cc @@ -147,6 +147,7 @@ SearchBox::SearchBox(content::RenderView* render_view) is_focused_(false), is_input_in_progress_(false), is_key_capture_enabled_(false), + display_instant_results_(false), most_visited_items_cache_(kMaxInstantMostVisitedItemCacheSize), omnibox_font_(), omnibox_font_size_(12), @@ -279,8 +280,12 @@ bool SearchBox::OnMessageReceived(const IPC::Message& message) { OnMostVisitedChanged) IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxPromoInformation, OnPromoInformationReceived) + IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetDisplayInstantResults, + OnSetDisplayInstantResults) IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetInputInProgress, OnSetInputInProgress) + IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSetSuggestionToPrefetch, + OnSetSuggestionToPrefetch) IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxSubmit, OnSubmit) IPC_MESSAGE_HANDLER(ChromeViewMsg_SearchBoxThemeChanged, OnThemeChanged) @@ -368,6 +373,10 @@ void SearchBox::OnPromoInformationReceived(bool is_app_launcher_enabled) { app_launcher_enabled_ = is_app_launcher_enabled; } +void SearchBox::OnSetDisplayInstantResults(bool display_instant_results) { + display_instant_results_ = display_instant_results; +} + void SearchBox::OnSetInputInProgress(bool is_input_in_progress) { if (is_input_in_progress_ != is_input_in_progress) { is_input_in_progress_ = is_input_in_progress; @@ -385,6 +394,15 @@ void SearchBox::OnSetInputInProgress(bool is_input_in_progress) { } } +void SearchBox::OnSetSuggestionToPrefetch(const InstantSuggestion& suggestion) { + suggestion_ = suggestion; + if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { + DVLOG(1) << render_view() << " OnSetSuggestionToPrefetch"; + extensions_v8::SearchBoxExtension::DispatchSuggestionChange( + render_view()->GetWebView()->mainFrame()); + } +} + void SearchBox::OnSubmit(const string16& query) { query_ = query; if (render_view()->GetWebView() && render_view()->GetWebView()->mainFrame()) { @@ -422,6 +440,7 @@ GURL SearchBox::GetURLForMostVisitedItem(InstantRestrictedID item_id) const { void SearchBox::Reset() { query_.clear(); + suggestion_ = InstantSuggestion(); start_margin_ = 0; width_ = 0; is_focused_ = false; diff --git a/chrome/renderer/searchbox/searchbox.h b/chrome/renderer/searchbox/searchbox.h index 267c190..92ad0a5 100644 --- a/chrome/renderer/searchbox/searchbox.h +++ b/chrome/renderer/searchbox/searchbox.h @@ -97,10 +97,12 @@ class SearchBox : public content::RenderViewObserver, bool is_focused() const { return is_focused_; } bool is_input_in_progress() const { return is_input_in_progress_; } bool is_key_capture_enabled() const { return is_key_capture_enabled_; } + bool display_instant_results() const { return display_instant_results_; } const string16& omnibox_font() const { return omnibox_font_; } + size_t omnibox_font_size() const { return omnibox_font_size_; } const string16& query() const { return query_; } int start_margin() const { return start_margin_; } - size_t omnibox_font_size() const { return omnibox_font_size_; } + const InstantSuggestion& suggestion() const { return suggestion_; } private: // Overridden from content::RenderViewObserver: @@ -115,7 +117,9 @@ class SearchBox : public content::RenderViewObserver, void OnMostVisitedChanged( const std::vector<InstantMostVisitedItem>& items); void OnPromoInformationReceived(bool is_app_launcher_enabled); + void OnSetDisplayInstantResults(bool display_instant_results); void OnSetInputInProgress(bool input_in_progress); + void OnSetSuggestionToPrefetch(const InstantSuggestion& suggestion); void OnSubmit(const string16& query); void OnThemeChanged(const ThemeBackgroundInfo& theme_info); void OnToggleVoiceSearch(); @@ -133,12 +137,14 @@ class SearchBox : public content::RenderViewObserver, bool is_focused_; bool is_input_in_progress_; bool is_key_capture_enabled_; + bool display_instant_results_; InstantRestrictedIDCache<InstantMostVisitedItem> most_visited_items_cache_; ThemeBackgroundInfo theme_info_; string16 omnibox_font_; size_t omnibox_font_size_; string16 query_; int start_margin_; + InstantSuggestion suggestion_; int width_; DISALLOW_COPY_AND_ASSIGN(SearchBox); diff --git a/chrome/renderer/searchbox/searchbox_extension.cc b/chrome/renderer/searchbox/searchbox_extension.cc index 39c780d..f0bc341 100644 --- a/chrome/renderer/searchbox/searchbox_extension.cc +++ b/chrome/renderer/searchbox/searchbox_extension.cc @@ -270,6 +270,17 @@ static const char kDispatchSubmitEventScript[] = " true;" "}"; +static const char kDispatchSuggestionChangeEventScript[] = + "if (window.chrome &&" + " window.chrome.embeddedSearch &&" + " window.chrome.embeddedSearch.searchBox &&" + " window.chrome.embeddedSearch.searchBox.onsuggestionchange &&" + " typeof window.chrome.embeddedSearch.searchBox.onsuggestionchange ==" + " 'function') {" + " window.chrome.embeddedSearch.searchBox.onsuggestionchange();" + " true;" + "}"; + static const char kDispatchThemeChangeEventScript[] = "if (window.chrome &&" " window.chrome.embeddedSearch &&" @@ -339,6 +350,10 @@ class SearchBoxExtensionWrapper : public v8::Extension { // Gets the start-edge margin to use with extended Instant. static void GetStartMargin(const v8::FunctionCallbackInfo<v8::Value>& args); + // Gets the current top suggestion to prefetch search results. + static void GetSuggestionToPrefetch( + const v8::FunctionCallbackInfo<v8::Value>& args); + // Gets the background info of the theme currently adopted by browser. // Call only when overlay is showing NTP page. static void GetThemeBackgroundInfo( @@ -386,6 +401,10 @@ class SearchBoxExtensionWrapper : public v8::Extension { static void UndoMostVisitedDeletion( const v8::FunctionCallbackInfo<v8::Value>& args); + // Indicates whether the page supports Instant. + static void GetDisplayInstantResults( + const v8::FunctionCallbackInfo<v8::Value>& args); + private: DISALLOW_COPY_AND_ASSIGN(SearchBoxExtensionWrapper); }; @@ -442,6 +461,11 @@ void SearchBoxExtension::DispatchSubmit(WebKit::WebFrame* frame) { } // static +void SearchBoxExtension::DispatchSuggestionChange(WebKit::WebFrame* frame) { + Dispatch(frame, kDispatchSuggestionChangeEventScript); +} + +// static void SearchBoxExtension::DispatchThemeChange(WebKit::WebFrame* frame) { Dispatch(frame, kDispatchThemeChangeEventScript); } @@ -477,6 +501,8 @@ v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction( return v8::FunctionTemplate::New(GetRightToLeft); if (name->Equals(v8::String::New("GetStartMargin"))) return v8::FunctionTemplate::New(GetStartMargin); + if (name->Equals(v8::String::New("GetSuggestionToPrefetch"))) + return v8::FunctionTemplate::New(GetSuggestionToPrefetch); if (name->Equals(v8::String::New("GetThemeBackgroundInfo"))) return v8::FunctionTemplate::New(GetThemeBackgroundInfo); if (name->Equals(v8::String::New("IsFocused"))) @@ -501,6 +527,8 @@ v8::Handle<v8::FunctionTemplate> SearchBoxExtensionWrapper::GetNativeFunction( return v8::FunctionTemplate::New(UndoAllMostVisitedDeletions); if (name->Equals(v8::String::New("UndoMostVisitedDeletion"))) return v8::FunctionTemplate::New(UndoMostVisitedDeletion); + if (name->Equals(v8::String::New("GetDisplayInstantResults"))) + return v8::FunctionTemplate::New(GetDisplayInstantResults); return v8::Handle<v8::FunctionTemplate>(); } @@ -625,6 +653,20 @@ void SearchBoxExtensionWrapper::GetStartMargin( } // static +void SearchBoxExtensionWrapper::GetSuggestionToPrefetch( + const v8::FunctionCallbackInfo<v8::Value>& args) { + content::RenderView* render_view = GetRenderView(); + if (!render_view) return; + + const InstantSuggestion& suggestion = + SearchBox::Get(render_view)->suggestion(); + v8::Handle<v8::Object> data = v8::Object::New(); + data->Set(v8::String::New("text"), UTF16ToV8String(suggestion.text)); + data->Set(v8::String::New("metadata"), UTF8ToV8String(suggestion.metadata)); + args.GetReturnValue().Set(data); +} + +// static void SearchBoxExtensionWrapper::GetThemeBackgroundInfo( const v8::FunctionCallbackInfo<v8::Value>& args) { content::RenderView* render_view = GetRenderView(); @@ -920,4 +962,17 @@ void SearchBoxExtensionWrapper::UndoMostVisitedDeletion( SearchBox::Get(render_view)->UndoMostVisitedDeletion(args[0]->IntegerValue()); } +// static +void SearchBoxExtensionWrapper::GetDisplayInstantResults( + const v8::FunctionCallbackInfo<v8::Value>& args) { + content::RenderView* render_view = GetRenderView(); + if (!render_view) return; + + bool display_instant_results = + SearchBox::Get(render_view)->display_instant_results(); + DVLOG(1) << render_view << " GetDisplayInstantResults" << + display_instant_results; + args.GetReturnValue().Set(display_instant_results); +} + } // namespace extensions_v8 diff --git a/chrome/renderer/searchbox/searchbox_extension.h b/chrome/renderer/searchbox/searchbox_extension.h index 8e04103..5135d64 100644 --- a/chrome/renderer/searchbox/searchbox_extension.h +++ b/chrome/renderer/searchbox/searchbox_extension.h @@ -37,6 +37,7 @@ class SearchBoxExtension { static void DispatchMarginChange(WebKit::WebFrame* frame); static void DispatchMostVisitedChanged(WebKit::WebFrame* frame); static void DispatchSubmit(WebKit::WebFrame* frame); + static void DispatchSuggestionChange(WebKit::WebFrame* frame); static void DispatchThemeChange(WebKit::WebFrame* frame); static void DispatchToggleVoiceSearch(WebKit::WebFrame* frame); diff --git a/chrome/test/data/instant_extended.html b/chrome/test/data/instant_extended.html index ecf5821..9eb83ca 100644 --- a/chrome/test/data/instant_extended.html +++ b/chrome/test/data/instant_extended.html @@ -19,6 +19,7 @@ var submitCount = 0; var onEscKeyPressedCalls = 0; var onFocusChangedCalls = 0; var onToggleVoiceSearchCalls = 0; +var prefetchQuery = ''; var isFocused = false; var onvisibilitycalls = 0; var onThemeChangedCalls = 0; @@ -118,6 +119,10 @@ function handleToggleVoiceSearch() { onToggleVoiceSearchCalls++; } +function handleSuggestionChange() { + prefetchQuery = getApiHandle().suggestion.text; +} + function handleThemeChange() { onThemeChangedCalls++; } @@ -134,6 +139,7 @@ function setUp() { apiHandle.onkeypress = handleKeyPress; apiHandle.onfocuschange = handleFocusChange; apiHandle.ontogglevoicesearch = handleToggleVoiceSearch; + apiHandle.onsuggestionchange = handleSuggestionChange; newTabPageHandle.onmostvisitedchange = handleMostVisitedChange; newTabPageHandle.onthemechange = handleThemeChange; if (apiHandle.value) { |