Index: chrome/browser/autocomplete/search_provider.cc |
diff --git a/chrome/browser/autocomplete/search_provider.cc b/chrome/browser/autocomplete/search_provider.cc |
index e4f35813feb91cacb699c4ff33e352afe0dd1845..93d0c18fa0950e3e23c22eee70105048cff06669 100644 |
--- a/chrome/browser/autocomplete/search_provider.cc |
+++ b/chrome/browser/autocomplete/search_provider.cc |
@@ -45,6 +45,7 @@ |
#include "chrome/common/net/url_fixer_upper.h" |
#include "chrome/common/pref_names.h" |
#include "chrome/common/url_constants.h" |
+#include "content/public/browser/user_metrics.h" |
#include "grit/generated_resources.h" |
#include "net/base/escape.h" |
#include "net/base/load_flags.h" |
@@ -175,6 +176,61 @@ void SetAndClassifyMatchContents(const string16& query_string, |
} // namespace |
+// SuggestionDeletionHandler ------------------------------------------------- |
+ |
+// This class handles making requests to the server in order to delete |
+// personalized suggestions. |
+class SuggestionDeletionHandler : public net::URLFetcherDelegate { |
+ public: |
+ typedef base::Callback<void(bool, SuggestionDeletionHandler*)> |
+ DeletionCompletedCallback; |
+ |
+ SuggestionDeletionHandler( |
+ const std::string& deletion_url, |
+ Profile* profile, |
+ const DeletionCompletedCallback& callback); |
+ |
+ virtual ~SuggestionDeletionHandler(); |
+ |
+ private: |
+ // net::URLFetcherDelegate: |
+ virtual void OnURLFetchComplete(const net::URLFetcher* source) OVERRIDE; |
+ |
+ scoped_ptr<net::URLFetcher> deletion_fetcher_; |
+ DeletionCompletedCallback callback_; |
+ |
+ DISALLOW_COPY_AND_ASSIGN(SuggestionDeletionHandler); |
+}; |
+ |
+ |
+SuggestionDeletionHandler::SuggestionDeletionHandler( |
+ const std::string& deletion_url, |
+ Profile* profile, |
+ const DeletionCompletedCallback& callback) : callback_(callback) { |
+ GURL url(deletion_url); |
+ DCHECK(url.is_valid()); |
+ |
+ deletion_fetcher_.reset(net::URLFetcher::Create( |
+ SearchProvider::kDeletionURLFetcherID, |
+ url, |
+ net::URLFetcher::GET, |
+ this)); |
+ deletion_fetcher_->SetRequestContext(profile->GetRequestContext()); |
+ deletion_fetcher_->Start(); |
+}; |
+ |
+SuggestionDeletionHandler::~SuggestionDeletionHandler() { |
+}; |
+ |
+void SuggestionDeletionHandler::OnURLFetchComplete( |
+ const net::URLFetcher* source) { |
+ DCHECK(source == deletion_fetcher_.get()); |
+ callback_.Run( |
+ source->GetStatus().is_success() && (source->GetResponseCode() == 200), |
+ this); |
+}; |
+ |
+ |
// SearchProvider::Providers -------------------------------------------------- |
SearchProvider::Providers::Providers(TemplateURLService* template_url_service) |
@@ -213,6 +269,7 @@ SearchProvider::SuggestResult::SuggestResult( |
const string16& match_contents, |
const string16& annotation, |
const std::string& suggest_query_params, |
+ const std::string& deletion_url, |
bool from_keyword_provider, |
int relevance, |
bool relevance_from_server, |
@@ -222,6 +279,7 @@ SearchProvider::SuggestResult::SuggestResult( |
match_contents_(match_contents), |
annotation_(annotation), |
suggest_query_params_(suggest_query_params), |
+ deletion_url_(deletion_url), |
should_prefetch_(should_prefetch) { |
} |
@@ -326,10 +384,12 @@ bool SearchProvider::Results::HasServerProvidedScores() const { |
// static |
const int SearchProvider::kDefaultProviderURLFetcherID = 1; |
const int SearchProvider::kKeywordProviderURLFetcherID = 2; |
+const int SearchProvider::kDeletionURLFetcherID = 3; |
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::kDeletionUrlKey[] = "deletion_url"; |
const char SearchProvider::kTrue[] = "true"; |
const char SearchProvider::kFalse[] = "false"; |
@@ -434,6 +494,20 @@ void SearchProvider::AddProviderInfo(ProvidersInfo* provider_info) const { |
} |
} |
+void SearchProvider::DeleteMatch(const AutocompleteMatch& match) { |
+ // TODO(mariakhomenko): Add support for deleting search history suggestions. |
+ DCHECK(match.deletable); |
+ |
+ deletion_handlers_.push_back(new SuggestionDeletionHandler( |
+ match.GetAdditionalInfo(SearchProvider::kDeletionUrlKey), |
+ profile_, |
+ base::Bind(&SearchProvider::OnDeletionComplete, base::Unretained(this)))); |
+ |
+ // Immediately update the list of matches to show the match was deleted, |
+ // regardless of whether the server request actually succeeds. |
+ DeleteMatchFromMatches(match); |
+} |
+ |
void SearchProvider::ResetSession() { |
field_trial_triggered_in_session_ = false; |
} |
@@ -638,6 +712,7 @@ void SearchProvider::OnURLFetchComplete(const net::URLFetcher* source) { |
source->GetResponseHeaders(); |
std::string json_data; |
source->GetResponseAsString(&json_data); |
+ |
// JSON is supposed to be UTF-8, but some suggest service providers send |
// JSON files in non-UTF-8 encodings. The actual encoding is usually |
// specified in the Content-Type header field. |
@@ -678,6 +753,41 @@ void SearchProvider::OnURLFetchComplete(const net::URLFetcher* source) { |
listener_->OnProviderUpdate(results_updated); |
} |
+void SearchProvider::OnDeletionComplete(bool success, |
+ SuggestionDeletionHandler* handler) { |
+ RecordDeletionResult(success); |
+ SuggestionDeletionHandlers::iterator it = std::find( |
+ deletion_handlers_.begin(), deletion_handlers_.end(), handler); |
+ DCHECK(it != deletion_handlers_.end()); |
+ deletion_handlers_.erase(it); |
+} |
+ |
+ |
+void SearchProvider::RecordDeletionResult(bool success) { |
+ if (success) { |
+ content::RecordAction( |
+ content::UserMetricsAction("Omnibox.ServerSuggestDelete.Success")); |
+ } else { |
+ content::RecordAction( |
+ content::UserMetricsAction("Omnibox.ServerSuggestDelete.Failure")); |
+ } |
+} |
+ |
+void SearchProvider::DeleteMatchFromMatches(const AutocompleteMatch& match) { |
+ for (ACMatches::iterator i(matches_.begin()); i != matches_.end(); ++i) { |
+ // Find the desired match to delete by checking the type and contents. |
+ // We can't check the destination URL, because the autocomplete controller |
+ // may have reformulated that. Not that while checking for matching |
+ // contents works for personalized suggestions, if more match types gain |
+ // deletion support, this algorithm may need to be re-examined. |
+ if (i->contents == match.contents && i->type == match.type) { |
+ matches_.erase(i); |
+ break; |
+ } |
+ } |
+ listener_->OnProviderUpdate(true); |
+} |
+ |
void SearchProvider::Run() { |
// Start a new request with the current input. |
suggest_results_pending_ = 0; |
@@ -935,69 +1045,6 @@ void SearchProvider::ApplyCalculatedNavigationRelevance( |
} |
} |
-bool SearchProvider::CanSendURL( |
- const GURL& current_page_url, |
- const GURL& suggest_url, |
- const TemplateURL* template_url, |
- AutocompleteInput::PageClassification page_classification, |
- Profile* profile) { |
- if (!current_page_url.is_valid()) |
- return false; |
- |
- // TODO(hfung): Show Most Visited on NTP with appropriate verbatim |
- // description when the user actively focuses on the omnibox as discussed in |
- // crbug/305366 if Most Visited (or something similar) will launch. |
- if ((page_classification == |
- AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS) || |
- (page_classification == |
- AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS)) |
- return false; |
- |
- // Only allow HTTP URLs or HTTPS URLs for the same domain as the search |
- // provider. |
- if ((current_page_url.scheme() != content::kHttpScheme) && |
- ((current_page_url.scheme() != content::kHttpsScheme) || |
- !net::registry_controlled_domains::SameDomainOrHost( |
- current_page_url, suggest_url, |
- net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES))) |
- return false; |
- |
- // Make sure we are sending the suggest request through HTTPS to prevent |
- // exposing the current page URL to networks before the search provider. |
- if (!suggest_url.SchemeIs(content::kHttpsScheme)) |
- return false; |
- |
- // Don't run if there's no profile or in incognito mode. |
- if (profile == NULL || profile->IsOffTheRecord()) |
- return false; |
- |
- // Don't run if we can't get preferences or search suggest is not enabled. |
- PrefService* prefs = profile->GetPrefs(); |
- if (!prefs->GetBoolean(prefs::kSearchSuggestEnabled)) |
- return false; |
- |
- // Only make the request if we know that the provider supports zero suggest |
- // (currently only the prepopulated Google provider). |
- if (template_url == NULL || !template_url->SupportsReplacement() || |
- TemplateURLPrepopulateData::GetEngineType(*template_url) != |
- SEARCH_ENGINE_GOOGLE) |
- return false; |
- |
- // Check field trials and settings allow sending the URL on suggest requests. |
- ProfileSyncService* service = |
- ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile); |
- browser_sync::SyncPrefs sync_prefs(prefs); |
- if (!OmniboxFieldTrial::InZeroSuggestFieldTrial() || |
- service == NULL || |
- !service->IsSyncEnabledAndLoggedIn() || |
- !sync_prefs.GetPreferredDataTypes(syncer::UserTypes()).Has( |
- syncer::PROXY_TABS) || |
- service->GetEncryptedDataTypes().Has(syncer::SESSIONS)) |
- return false; |
- |
- return true; |
-} |
- |
net::URLFetcher* SearchProvider::CreateSuggestFetcher( |
int id, |
const TemplateURL* template_url, |
@@ -1134,19 +1181,27 @@ bool SearchProvider::ParseSuggestResults(Value* root_val, bool is_keyword) { |
string16 disambiguating_query; |
Peter Kasting
2013/12/03 00:33:51
Nit: Declare this in the most local scope possible
Maria
2013/12/03 01:00:26
Done.
|
string16 annotation; |
std::string suggest_query_params; |
- if (suggestion_details && (type == "ENTITY") && |
- suggestion_details->GetDictionary(index, &suggestion_detail) && |
- suggestion_detail) { |
- suggestion_detail->GetString("a", &annotation); |
- if (suggestion_detail->GetString("dq", &disambiguating_query) && |
- !disambiguating_query.empty()) |
- suggestion = disambiguating_query; |
- suggestion_detail->GetString("q", &suggest_query_params); |
+ std::string deletion_url; |
+ |
+ if (suggestion_details) { |
+ suggestion_details->GetDictionary(index, &suggestion_detail); |
+ if (suggestion_detail) { |
+ suggestion_detail->GetString("du", &deletion_url); |
+ |
+ if (type == "ENTITY") { |
+ suggestion_detail->GetString("a", &annotation); |
+ if (suggestion_detail->GetString("dq", &disambiguating_query) && |
+ !disambiguating_query.empty()) |
+ suggestion = disambiguating_query; |
+ suggestion_detail->GetString("q", &suggest_query_params); |
+ } |
+ } |
} |
+ |
// TODO(kochi): Improve calculator suggestion presentation. |
results->suggest_results.push_back(SuggestResult( |
suggestion, match_contents, annotation, suggest_query_params, |
- is_keyword, relevance, true, should_prefetch)); |
+ deletion_url, is_keyword, relevance, true, should_prefetch)); |
} |
} |
@@ -1206,6 +1261,7 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { |
input_.text(), |
did_not_accept_default_suggestion, |
std::string(), |
+ std::string(), |
&map); |
} |
if (!keyword_input_.text().empty()) { |
@@ -1234,6 +1290,7 @@ void SearchProvider::ConvertResultsToAutocompleteMatches() { |
keyword_input_.text(), |
did_not_accept_keyword_suggestion, |
std::string(), |
+ std::string(), |
&map); |
} |
} |
@@ -1542,6 +1599,7 @@ void SearchProvider::AddHistoryResultsToMap(const HistoryResults& results, |
i->suggestion(), |
did_not_accept_suggestion, |
std::string(), |
+ std::string(), |
map); |
} |
UMA_HISTOGRAM_TIMES("Omnibox.SearchProvider.AddHistoryResultsTime", |
@@ -1592,7 +1650,7 @@ SearchProvider::SuggestResults SearchProvider::ScoreHistoryResults( |
prevent_search_history_inlining); |
scored_results.push_back( |
SuggestResult(i->term, string16(), string16(), std::string(), |
- is_keyword, relevance, false, false)); |
+ std::string(), is_keyword, relevance, false, false)); |
} |
// History returns results sorted for us. However, we may have docked some |
@@ -1631,6 +1689,7 @@ void SearchProvider::AddSuggestResultsToMap(const SuggestResults& results, |
results[i].suggestion(), |
i, |
results[i].suggest_query_params(), |
+ results[i].deletion_url(), |
map); |
} |
} |
@@ -1755,6 +1814,7 @@ void SearchProvider::AddMatchToMap(const string16& input_text, |
const string16& query_string, |
int accepted_suggestion, |
const std::string& suggest_query_params, |
+ const std::string& deletion_url, |
MatchMap* map) { |
// On non-mobile, ask the instant controller for the appropriate start margin. |
// On mobile the start margin is unused, so leave the value as default there. |
@@ -1787,6 +1847,11 @@ void SearchProvider::AddMatchToMap(const string16& input_text, |
match.RecordAdditionalInfo(kShouldPrefetchKey, |
should_prefetch ? kTrue : kFalse); |
+ if (!deletion_url.empty() && GURL(deletion_url).is_valid()) { |
+ match.RecordAdditionalInfo(kDeletionUrlKey, deletion_url); |
+ match.deletable = true; |
+ } |
+ |
// Metadata is needed only for prefetching queries. |
if (should_prefetch) |
match.RecordAdditionalInfo(kSuggestMetadataKey, metadata); |
@@ -1952,3 +2017,66 @@ void SearchProvider::UpdateDone() { |
// pending, and we're not waiting on Instant. |
done_ = !timer_.IsRunning() && (suggest_results_pending_ == 0); |
} |
+ |
+bool SearchProvider::CanSendURL( |
+ const GURL& current_page_url, |
+ const GURL& suggest_url, |
+ const TemplateURL* template_url, |
+ AutocompleteInput::PageClassification page_classification, |
+ Profile* profile) { |
+ if (!current_page_url.is_valid()) |
+ return false; |
+ |
+ // TODO(hfung): Show Most Visited on NTP with appropriate verbatim |
+ // description when the user actively focuses on the omnibox as discussed in |
+ // crbug/305366 if Most Visited (or something similar) will launch. |
+ if ((page_classification == |
+ AutocompleteInput::INSTANT_NTP_WITH_FAKEBOX_AS_STARTING_FOCUS) || |
+ (page_classification == |
+ AutocompleteInput::INSTANT_NTP_WITH_OMNIBOX_AS_STARTING_FOCUS)) |
+ return false; |
+ |
+ // Only allow HTTP URLs or HTTPS URLs for the same domain as the search |
+ // provider. |
+ if ((current_page_url.scheme() != content::kHttpScheme) && |
+ ((current_page_url.scheme() != content::kHttpsScheme) || |
+ !net::registry_controlled_domains::SameDomainOrHost( |
+ current_page_url, suggest_url, |
+ net::registry_controlled_domains::EXCLUDE_PRIVATE_REGISTRIES))) |
+ return false; |
+ |
+ // Make sure we are sending the suggest request through HTTPS to prevent |
+ // exposing the current page URL to networks before the search provider. |
+ if (!suggest_url.SchemeIs(content::kHttpsScheme)) |
+ return false; |
+ |
+ // Don't run if there's no profile or in incognito mode. |
+ if (profile == NULL || profile->IsOffTheRecord()) |
+ return false; |
+ |
+ // Don't run if we can't get preferences or search suggest is not enabled. |
+ PrefService* prefs = profile->GetPrefs(); |
+ if (!prefs->GetBoolean(prefs::kSearchSuggestEnabled)) |
+ return false; |
+ |
+ // Only make the request if we know that the provider supports zero suggest |
+ // (currently only the prepopulated Google provider). |
+ if (template_url == NULL || !template_url->SupportsReplacement() || |
+ TemplateURLPrepopulateData::GetEngineType(*template_url) != |
+ SEARCH_ENGINE_GOOGLE) |
+ return false; |
+ |
+ // Check field trials and settings allow sending the URL on suggest requests. |
+ ProfileSyncService* service = |
+ ProfileSyncServiceFactory::GetInstance()->GetForProfile(profile); |
+ browser_sync::SyncPrefs sync_prefs(prefs); |
+ if (!OmniboxFieldTrial::InZeroSuggestFieldTrial() || |
+ service == NULL || |
+ !service->IsSyncEnabledAndLoggedIn() || |
+ !sync_prefs.GetPreferredDataTypes(syncer::UserTypes()).Has( |
+ syncer::PROXY_TABS) || |
+ service->GetEncryptedDataTypes().Has(syncer::SESSIONS)) |
+ return false; |
+ |
+ return true; |
+} |