blob: c39da7c7530568b7543e4006cfc487267f16db2b [file] [log] [blame]
[email protected]6ce7f612012-09-05 23:53:071// Copyright (c) 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
blundell2102f7c2015-07-09 10:00:535#include "components/omnibox/browser/zero_suggest_provider.h"
[email protected]6ce7f612012-09-05 23:53:076
avif57136c12015-12-25 23:27:457#include <stddef.h>
8
Kevin Bailey1e2a90e2017-10-27 21:02:059#include <string>
10#include <utility>
11
[email protected]6ce7f612012-09-05 23:53:0712#include "base/callback.h"
gcomanici8cabc77f2017-04-27 20:04:5413#include "base/feature_list.h"
[email protected]bb1fb2b2013-05-31 00:21:0114#include "base/i18n/case_conversion.h"
[email protected]6ce7f612012-09-05 23:53:0715#include "base/json/json_string_value_serializer.h"
asvitkine30330812016-08-30 04:01:0816#include "base/metrics/histogram_macros.h"
[email protected]f7f41c0e2014-08-11 04:22:2317#include "base/metrics/user_metrics.h"
[email protected]98570e12013-06-10 19:54:2218#include "base/strings/string16.h"
19#include "base/strings/string_util.h"
[email protected]135cb802013-06-09 16:44:2020#include "base/strings/utf_string_conversions.h"
[email protected]4dcb7972013-06-28 15:15:4121#include "base/time/time.h"
a-v-ydd768d54b2016-03-25 21:07:4622#include "base/trace_event/trace_event.h"
amohammadkhanf76ae112015-09-14 17:34:4323#include "components/data_use_measurement/core/data_use_user_data.h"
sdefresnebc766ef2014-09-25 09:28:1324#include "components/history/core/browser/history_types.h"
sdefresne0da3bc02015-01-29 18:26:3525#include "components/history/core/browser/top_sites.h"
blundell2102f7c2015-07-09 10:00:5326#include "components/omnibox/browser/autocomplete_classifier.h"
27#include "components/omnibox/browser/autocomplete_input.h"
28#include "components/omnibox/browser/autocomplete_match.h"
29#include "components/omnibox/browser/autocomplete_provider_listener.h"
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:1130#include "components/omnibox/browser/contextual_suggestions_service.h"
blundell2102f7c2015-07-09 10:00:5331#include "components/omnibox/browser/history_url_provider.h"
32#include "components/omnibox/browser/omnibox_field_trial.h"
33#include "components/omnibox/browser/omnibox_pref_names.h"
34#include "components/omnibox/browser/search_provider.h"
sdefresne70948d62015-08-11 10:46:3535#include "components/omnibox/browser/verbatim_match.h"
[email protected]f0c8c4992014-05-15 17:37:2636#include "components/pref_registry/pref_registry_syncable.h"
brettwf00b9b42016-02-01 22:11:3837#include "components/prefs/pref_service.h"
Gheorghe Comanici864725b2017-11-28 21:48:0138#include "components/search_engines/search_engine_type.h"
[email protected]bf5c532d2014-07-05 00:29:5339#include "components/search_engines/template_url_service.h"
rsleevi24f64dc22015-08-07 21:39:2140#include "components/url_formatter/url_formatter.h"
asvitkine9a279832015-12-18 02:35:5041#include "components/variations/net/variations_http_headers.h"
[email protected]bb1fb2b2013-05-31 00:21:0142#include "net/base/escape.h"
[email protected]6ce7f612012-09-05 23:53:0743#include "net/url_request/url_fetcher.h"
44#include "net/url_request/url_request_status.h"
Steven Holtef9d5ed62017-10-21 02:02:3045#include "third_party/metrics_proto/omnibox_event.pb.h"
46#include "third_party/metrics_proto/omnibox_input_type.pb.h"
[email protected]761fa4702013-07-02 15:25:1547#include "url/gurl.h"
[email protected]6ce7f612012-09-05 23:53:0748
49namespace {
[email protected]bb1fb2b2013-05-31 00:21:0150
mpearson3d89cdc2017-03-03 21:15:4551// Represents whether ZeroSuggestProvider is allowed to display contextual
52// suggestions on focus, and if not, why not.
53// These values are written to logs. New enum values can be added, but existing
54// enums must never be renumbered or deleted and reused.
55enum class ZeroSuggestEligibility {
56 ELIGIBLE = 0,
57 // URL_INELIGIBLE would be ELIGIBLE except some property of the current URL
58 // itself prevents ZeroSuggest from triggering.
59 URL_INELIGIBLE = 1,
60 GENERALLY_INELIGIBLE = 2,
61 ELIGIBLE_MAX_VALUE
62};
63
[email protected]bb1fb2b2013-05-31 00:21:0164// TODO(hfung): The histogram code was copied and modified from
65// search_provider.cc. Refactor and consolidate the code.
66// We keep track in a histogram how many suggest requests we send, how
67// many suggest requests we invalidate (e.g., due to a user typing
68// another character), and how many replies we receive.
mpearson3d89cdc2017-03-03 21:15:4569// These values are written to logs. New enum values can be added, but existing
70// enums must never be renumbered or deleted and reused.
[email protected]bb1fb2b2013-05-31 00:21:0171enum ZeroSuggestRequestsHistogramValue {
72 ZERO_SUGGEST_REQUEST_SENT = 1,
mpearson3d89cdc2017-03-03 21:15:4573 ZERO_SUGGEST_REQUEST_INVALIDATED = 2,
74 ZERO_SUGGEST_REPLY_RECEIVED = 3,
[email protected]bb1fb2b2013-05-31 00:21:0175 ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE
76};
77
78void LogOmniboxZeroSuggestRequest(
79 ZeroSuggestRequestsHistogramValue request_value) {
80 UMA_HISTOGRAM_ENUMERATION("Omnibox.ZeroSuggestRequests", request_value,
81 ZERO_SUGGEST_MAX_REQUEST_HISTOGRAM_VALUE);
82}
83
[email protected]bb1fb2b2013-05-31 00:21:0184// Relevance value to use if it was not set explicitly by the server.
85const int kDefaultZeroSuggestRelevance = 100;
86
mpearson3d89cdc2017-03-03 21:15:4587// Used for testing whether zero suggest is ever available.
mpearsonc90c24b2017-03-04 00:11:2688constexpr char kArbitraryInsecureUrlString[] = "http://www.google.com/";
mpearson3d89cdc2017-03-03 21:15:4589
Gheorghe Comanici9a364f572018-01-24 17:22:0090// If the user is not signed-in or the user does not have Google set up as their
91// default search engine, the personalized service is replaced with the most
92// visited service.
93bool PersonalizedServiceShouldFallBackToMostVisited(
Gheorghe Comanici864725b2017-11-28 21:48:0194 PrefService* prefs,
Gheorghe Comanici682504102017-11-30 17:47:2595 bool is_authenticated,
Gheorghe Comanici864725b2017-11-28 21:48:0196 const TemplateURLService* template_url_service) {
Gheorghe Comanici9a364f572018-01-24 17:22:0097 if (!is_authenticated)
Gheorghe Comanici864725b2017-11-28 21:48:0198 return true;
99
Gheorghe Comanici9a364f572018-01-24 17:22:00100 if (template_url_service == nullptr)
101 return false;
Gheorghe Comanici682504102017-11-30 17:47:25102
Gheorghe Comanici9a364f572018-01-24 17:22:00103 const TemplateURL* default_provider =
104 template_url_service->GetDefaultSearchProvider();
105 return default_provider == nullptr ||
106 default_provider->GetEngineType(
107 template_url_service->search_terms_data()) != SEARCH_ENGINE_GOOGLE;
Gheorghe Comanici864725b2017-11-28 21:48:01108}
109
[email protected]6ce7f612012-09-05 23:53:07110} // namespace
111
[email protected]a00008d42012-09-15 05:07:58112// static
113ZeroSuggestProvider* ZeroSuggestProvider::Create(
blundell55e35e82015-06-16 08:46:18114 AutocompleteProviderClient* client,
mpearson931028c2016-07-01 18:55:11115 HistoryURLProvider* history_url_provider,
blundelld130d592015-06-21 19:29:13116 AutocompleteProviderListener* listener) {
mpearson931028c2016-07-01 18:55:11117 return new ZeroSuggestProvider(client, history_url_provider, listener);
[email protected]6ce7f612012-09-05 23:53:07118}
119
[email protected]855ebff2014-05-09 07:14:38120// static
121void ZeroSuggestProvider::RegisterProfilePrefs(
122 user_prefs::PrefRegistrySyncable* registry) {
blundelld130d592015-06-21 19:29:13123 registry->RegisterStringPref(omnibox::kZeroSuggestCachedResults,
124 std::string());
[email protected]855ebff2014-05-09 07:14:38125}
126
[email protected]6ce7f612012-09-05 23:53:07127void ZeroSuggestProvider::Start(const AutocompleteInput& input,
jifcf322cd2015-06-17 11:01:18128 bool minimal_changes) {
a-v-ydd768d54b2016-03-25 21:07:46129 TRACE_EVENT0("omnibox", "ZeroSuggestProvider::Start");
[email protected]f030c4d2014-03-25 01:05:54130 matches_.clear();
mpearson6eaf7fc2017-04-11 04:09:57131 if (!input.from_omnibox_focus() || client()->IsOffTheRecord() ||
mariakhomenko3ef531d72015-01-10 00:03:43132 input.type() == metrics::OmniboxInputType::INVALID)
[email protected]f030c4d2014-03-25 01:05:54133 return;
[email protected]bb1fb2b2013-05-31 00:21:01134
mpearson8a37c382015-03-07 05:58:57135 Stop(true, false);
Gheorghe Comanici9a364f572018-01-24 17:22:00136 result_type_running_ = ResultType::NONE;
blundelld130d592015-06-21 19:29:13137 set_field_trial_triggered(false);
138 set_field_trial_triggered_in_session(false);
[email protected]f030c4d2014-03-25 01:05:54139 permanent_text_ = input.text();
140 current_query_ = input.current_url().spec();
gcomanici8cabc77f2017-04-27 20:04:54141 current_title_ = input.current_title();
[email protected]f030c4d2014-03-25 01:05:54142 current_page_classification_ = input.current_page_classification();
[email protected]9b9fa672013-11-07 06:04:52143 current_url_match_ = MatchForCurrentURL();
144
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11145 GURL suggest_url = ContextualSuggestionsService::ContextualSuggestionsUrl(
146 /*current_url=*/"", client()->GetTemplateURLService());
[email protected]162c8d9fa2014-03-18 20:25:41147 if (!suggest_url.is_valid())
[email protected]6ce7f612012-09-05 23:53:07148 return;
[email protected]162c8d9fa2014-03-18 20:25:41149
Gheorghe Comanici9a364f572018-01-24 17:22:00150 result_type_running_ = TypeOfResultToRun(input.current_url(), suggest_url);
151 if (result_type_running_ == ZeroSuggestProvider::NONE)
[email protected]162c8d9fa2014-03-18 20:25:41152 return;
[email protected]162c8d9fa2014-03-18 20:25:41153
[email protected]6ce7f612012-09-05 23:53:07154 done_ = false;
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11155
[email protected]6ce7f612012-09-05 23:53:07156 // TODO(jered): Consider adding locally-sourced zero-suggestions here too.
157 // These may be useful on the NTP or more relevant to the user than server
158 // suggestions, if based on local browsing history.
[email protected]855ebff2014-05-09 07:14:38159 MaybeUseCachedSuggestions();
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11160
Gheorghe Comanici9a364f572018-01-24 17:22:00161 if (result_type_running_ == ZeroSuggestProvider::MOST_VISITED) {
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11162 most_visited_urls_.clear();
163 scoped_refptr<history::TopSites> ts = client()->GetTopSites();
Gheorghe Comanici9a364f572018-01-24 17:22:00164 if (!ts) {
165 done_ = true;
166 result_type_running_ = ResultType::NONE;
167 return;
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11168 }
Gheorghe Comanici9a364f572018-01-24 17:22:00169
170 ts->GetMostVisitedURLs(
171 base::Bind(&ZeroSuggestProvider::OnMostVisitedUrlsAvailable,
172 weak_ptr_factory_.GetWeakPtr()),
173 false);
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11174 return;
175 }
176
Gheorghe Comanici9a364f572018-01-24 17:22:00177 const std::string current_url =
178 result_type_running_ == ZeroSuggestProvider::DEFAULT_SERP_FOR_URL
179 ? current_query_
180 : std::string();
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11181 // Create a request for suggestions with |this| as the fetcher delegate.
182 client()
Gheorghe Comanici86bbdf62017-08-28 17:20:33183 ->GetContextualSuggestionsService(/*create_if_necessary=*/true)
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11184 ->CreateContextualSuggestionsRequest(
Gheorghe Comanici034cff62018-01-27 03:34:00185 current_url, client()->GetCurrentVisitTimestamp(),
186 client()->GetTemplateURLService(),
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11187 /*fetcher_delegate=*/this,
188 base::BindOnce(
189 &ZeroSuggestProvider::OnContextualSuggestionsFetcherAvailable,
190 weak_ptr_factory_.GetWeakPtr()));
[email protected]6ce7f612012-09-05 23:53:07191}
192
mpearson8a37c382015-03-07 05:58:57193void ZeroSuggestProvider::Stop(bool clear_cached_results,
194 bool due_to_user_inactivity) {
[email protected]ec3f679b2014-08-18 07:45:13195 if (fetcher_)
196 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_INVALIDATED);
197 fetcher_.reset();
Gheorghe Comanici86bbdf62017-08-28 17:20:33198 auto* contextual_suggestions_service =
199 client()->GetContextualSuggestionsService(/*create_if_necessary=*/false);
200 // contextual_suggestions_service can be null if in incognito mode.
201 if (contextual_suggestions_service != nullptr) {
202 contextual_suggestions_service->StopCreatingContextualSuggestionsRequest();
203 }
[email protected]ec3f679b2014-08-18 07:45:13204 done_ = true;
205
206 if (clear_cached_results) {
207 // We do not call Clear() on |results_| to retain |verbatim_relevance|
208 // value in the |results_| object. |verbatim_relevance| is used at the
jifcf322cd2015-06-17 11:01:18209 // beginning of the next call to Start() to determine the current url
210 // match relevance.
[email protected]ec3f679b2014-08-18 07:45:13211 results_.suggest_results.clear();
212 results_.navigation_results.clear();
213 current_query_.clear();
gcomanici8cabc77f2017-04-27 20:04:54214 current_title_.clear();
mariakhomenko1535e6a2015-03-20 07:48:45215 most_visited_urls_.clear();
[email protected]ec3f679b2014-08-18 07:45:13216 }
Gheorghe Comanici9a364f572018-01-24 17:22:00217
218 result_type_running_ = ZeroSuggestProvider::NONE;
[email protected]ec3f679b2014-08-18 07:45:13219}
220
[email protected]855ebff2014-05-09 07:14:38221void ZeroSuggestProvider::DeleteMatch(const AutocompleteMatch& match) {
Theresa3db4ae22018-03-05 18:47:40222 if (OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial()) {
[email protected]855ebff2014-05-09 07:14:38223 // Remove the deleted match from the cache, so it is not shown to the user
224 // again. Since we cannot remove just one result, blow away the cache.
blundelld130d592015-06-21 19:29:13225 client()->GetPrefs()->SetString(omnibox::kZeroSuggestCachedResults,
[email protected]855ebff2014-05-09 07:14:38226 std::string());
227 }
228 BaseSearchProvider::DeleteMatch(match);
229}
230
[email protected]ec3f679b2014-08-18 07:45:13231void ZeroSuggestProvider::AddProviderInfo(ProvidersInfo* provider_info) const {
232 BaseSearchProvider::AddProviderInfo(provider_info);
mariakhomenko1535e6a2015-03-20 07:48:45233 if (!results_.suggest_results.empty() ||
234 !results_.navigation_results.empty() ||
235 !most_visited_urls_.empty())
[email protected]ec3f679b2014-08-18 07:45:13236 provider_info->back().set_times_returned_results_in_session(1);
237}
238
[email protected]f030c4d2014-03-25 01:05:54239void ZeroSuggestProvider::ResetSession() {
240 // The user has started editing in the omnibox, so leave
blundelld130d592015-06-21 19:29:13241 // |field_trial_triggered_in_session| unchanged and set
242 // |field_trial_triggered| to false since zero suggest is inactive now.
243 set_field_trial_triggered(false);
[email protected]f030c4d2014-03-25 01:05:54244}
245
mpearson931028c2016-07-01 18:55:11246ZeroSuggestProvider::ZeroSuggestProvider(
247 AutocompleteProviderClient* client,
248 HistoryURLProvider* history_url_provider,
249 AutocompleteProviderListener* listener)
blundelld130d592015-06-21 19:29:13250 : BaseSearchProvider(AutocompleteProvider::TYPE_ZERO_SUGGEST, client),
mpearson931028c2016-07-01 18:55:11251 history_url_provider_(history_url_provider),
[email protected]776ee5902014-08-11 09:15:19252 listener_(listener),
Gheorghe Comanici9a364f572018-01-24 17:22:00253 result_type_running_(ResultType::NONE),
[email protected]8f064e52013-09-18 01:17:14254 weak_ptr_factory_(this) {
mpearson3d89cdc2017-03-03 21:15:45255 // Record whether contextual zero suggest is possible for this user / profile.
256 const TemplateURLService* template_url_service =
257 client->GetTemplateURLService();
258 // Template URL service can be null in tests.
259 if (template_url_service != nullptr) {
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11260 GURL suggest_url = ContextualSuggestionsService::ContextualSuggestionsUrl(
261 /*current_url=*/"", template_url_service);
mpearson3d89cdc2017-03-03 21:15:45262 // To check whether this is allowed, use an arbitrary insecure (http) URL
263 // as the URL we'd want suggestions for. The value of OTHER as the current
264 // page classification is to correspond with that URL.
265 UMA_HISTOGRAM_BOOLEAN(
266 "Omnibox.ZeroSuggest.Eligible.OnProfileOpen",
267 suggest_url.is_valid() &&
268 CanSendURL(GURL(kArbitraryInsecureUrlString), suggest_url,
269 template_url_service->GetDefaultSearchProvider(),
270 metrics::OmniboxEventProto::OTHER,
271 template_url_service->search_terms_data(), client));
272 }
[email protected]6ce7f612012-09-05 23:53:07273}
274
275ZeroSuggestProvider::~ZeroSuggestProvider() {
276}
277
[email protected]776ee5902014-08-11 09:15:19278const TemplateURL* ZeroSuggestProvider::GetTemplateURL(bool is_keyword) const {
279 // Zero suggest provider should not receive keyword results.
280 DCHECK(!is_keyword);
blundelld130d592015-06-21 19:29:13281 return client()->GetTemplateURLService()->GetDefaultSearchProvider();
[email protected]776ee5902014-08-11 09:15:19282}
283
284const AutocompleteInput ZeroSuggestProvider::GetInput(bool is_keyword) const {
jifcf322cd2015-06-17 11:01:18285 // The callers of this method won't look at the AutocompleteInput's
286 // |from_omnibox_focus| member, so we can set its value to false.
Kevin Baileybcc319e2017-10-01 21:53:02287 AutocompleteInput input(base::string16(), current_page_classification_,
288 client()->GetSchemeClassifier());
289 input.set_current_url(GURL(current_query_));
290 input.set_current_title(current_title_);
291 input.set_prevent_inline_autocomplete(true);
292 input.set_allow_exact_keyword_match(false);
293 return input;
[email protected]776ee5902014-08-11 09:15:19294}
295
296bool ZeroSuggestProvider::ShouldAppendExtraParams(
297 const SearchSuggestionParser::SuggestResult& result) const {
298 // We always use the default provider for search, so append the params.
299 return true;
300}
301
[email protected]776ee5902014-08-11 09:15:19302void ZeroSuggestProvider::RecordDeletionResult(bool success) {
303 if (success) {
304 base::RecordAction(
305 base::UserMetricsAction("Omnibox.ZeroSuggestDelete.Success"));
306 } else {
307 base::RecordAction(
308 base::UserMetricsAction("Omnibox.ZeroSuggestDelete.Failure"));
309 }
310}
311
312void ZeroSuggestProvider::OnURLFetchComplete(const net::URLFetcher* source) {
313 DCHECK(!done_);
314 DCHECK_EQ(fetcher_.get(), source);
315
316 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REPLY_RECEIVED);
317
Gheorghe Comanici9a364f572018-01-24 17:22:00318 const bool results_updated =
319 source->GetStatus().is_success() && source->GetResponseCode() == 200 &&
320 UpdateResults(SearchSuggestionParser::ExtractJsonData(source));
[email protected]776ee5902014-08-11 09:15:19321 fetcher_.reset();
322 done_ = true;
Gheorghe Comanici9a364f572018-01-24 17:22:00323 result_type_running_ = ZeroSuggestProvider::NONE;
[email protected]776ee5902014-08-11 09:15:19324 listener_->OnProviderUpdate(results_updated);
325}
326
Gheorghe Comanici9a364f572018-01-24 17:22:00327bool ZeroSuggestProvider::UpdateResults(const std::string& json_data) {
328 std::unique_ptr<base::Value> data(
329 SearchSuggestionParser::DeserializeJsonData(json_data));
330 if (!data)
[email protected]855ebff2014-05-09 07:14:38331 return false;
332
Gheorghe Comanici9a364f572018-01-24 17:22:00333 // When running the personalized service, we want to store suggestion
334 // responses if non-empty.
335 if (result_type_running_ == ResultType::DEFAULT_SERP && !json_data.empty()) {
336 client()->GetPrefs()->SetString(omnibox::kZeroSuggestCachedResults,
337 json_data);
[email protected]855ebff2014-05-09 07:14:38338
Gheorghe Comanici9a364f572018-01-24 17:22:00339 // If we received an empty result list, we should update the display, as it
340 // may be showing cached results that should not be shown.
341 const base::ListValue* root_list = nullptr;
342 const base::ListValue* results_list = nullptr;
343 const bool non_empty_parsed_list = data->GetAsList(&root_list) &&
344 root_list->GetList(1, &results_list) &&
345 !results_list->empty();
346 const bool non_empty_cache = !results_.suggest_results.empty() ||
347 !results_.navigation_results.empty();
348 if (non_empty_parsed_list && non_empty_cache)
349 return false;
350 }
351 const bool results_updated = ParseSuggestResults(
352 *data, kDefaultZeroSuggestRelevance, false, &results_);
353 ConvertResultsToAutocompleteMatches();
354 return results_updated;
[email protected]855ebff2014-05-09 07:14:38355}
356
[email protected]bb1fb2b2013-05-31 00:21:01357void ZeroSuggestProvider::AddSuggestResultsToMap(
[email protected]0b9575f2014-07-30 11:58:37358 const SearchSuggestionParser::SuggestResults& results,
[email protected]02346202014-02-05 05:18:30359 MatchMap* map) {
[email protected]d4a94b92014-03-04 01:35:22360 for (size_t i = 0; i < results.size(); ++i)
[email protected]7bc5e162014-08-15 19:41:11361 AddMatchToMap(results[i], std::string(), i, false, false, map);
[email protected]bb1fb2b2013-05-31 00:21:01362}
363
[email protected]bb1fb2b2013-05-31 00:21:01364AutocompleteMatch ZeroSuggestProvider::NavigationToMatch(
[email protected]0b9575f2014-07-30 11:58:37365 const SearchSuggestionParser::NavigationResult& navigation) {
[email protected]bb1fb2b2013-05-31 00:21:01366 AutocompleteMatch match(this, navigation.relevance(), false,
[email protected]78981d8c2014-05-09 15:05:47367 navigation.type());
[email protected]bb1fb2b2013-05-31 00:21:01368 match.destination_url = navigation.url();
369
[email protected]23db6492014-01-16 02:35:30370 // Zero suggest results should always omit protocols and never appear bold.
Tommy C. Lia46e6382017-08-01 23:26:27371 auto format_types = AutocompleteMatch::GetFormatTypes(false, false, false);
tommycli72014f62017-06-29 21:42:16372 match.contents = url_formatter::FormatUrl(navigation.url(), format_types,
373 net::UnescapeRule::SPACES, nullptr,
374 nullptr, nullptr);
[email protected]bb1fb2b2013-05-31 00:21:01375 match.fill_into_edit +=
blundelld130d592015-06-21 19:29:13376 AutocompleteInput::FormattedStringWithEquivalentMeaning(
Tommy C. Li0beb8152017-08-25 18:30:26377 navigation.url(), url_formatter::FormatUrl(navigation.url()),
378 client()->GetSchemeClassifier());
[email protected]bb1fb2b2013-05-31 00:21:01379
[email protected]b959d7d42013-12-13 17:26:37380 AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0,
[email protected]bb1fb2b2013-05-31 00:21:01381 match.contents.length(), ACMatchClassification::URL,
382 &match.contents_class);
[email protected]9c97f89c2013-06-25 03:12:16383
384 match.description =
385 AutocompleteMatch::SanitizeString(navigation.description());
[email protected]b959d7d42013-12-13 17:26:37386 AutocompleteMatch::ClassifyLocationInString(base::string16::npos, 0,
[email protected]9c97f89c2013-06-25 03:12:16387 match.description.length(), ACMatchClassification::NONE,
388 &match.description_class);
gcomanici67d53ac2017-04-01 17:07:19389 match.subtype_identifier = navigation.subtype_identifier();
[email protected]bb1fb2b2013-05-31 00:21:01390 return match;
391}
392
[email protected]8f064e52013-09-18 01:17:14393void ZeroSuggestProvider::OnMostVisitedUrlsAvailable(
394 const history::MostVisitedURLList& urls) {
Gheorghe Comanici9a364f572018-01-24 17:22:00395 if (result_type_running_ != ResultType::MOST_VISITED)
396 return;
[email protected]8f064e52013-09-18 01:17:14397 most_visited_urls_ = urls;
mariakhomenkobfc3a2a2014-10-24 00:48:22398 done_ = true;
399 ConvertResultsToAutocompleteMatches();
Gheorghe Comanici9a364f572018-01-24 17:22:00400 result_type_running_ = ResultType::NONE;
mariakhomenkobfc3a2a2014-10-24 00:48:22401 listener_->OnProviderUpdate(true);
[email protected]8f064e52013-09-18 01:17:14402}
403
Daniel Kenji Toyamaf1e4b572017-08-03 16:31:11404void ZeroSuggestProvider::OnContextualSuggestionsFetcherAvailable(
405 std::unique_ptr<net::URLFetcher> fetcher) {
406 fetcher_ = std::move(fetcher);
407 fetcher_->Start();
408 LogOmniboxZeroSuggestRequest(ZERO_SUGGEST_REQUEST_SENT);
409}
410
[email protected]9c97f89c2013-06-25 03:12:16411void ZeroSuggestProvider::ConvertResultsToAutocompleteMatches() {
[email protected]bb1fb2b2013-05-31 00:21:01412 matches_.clear();
413
blundelld130d592015-06-21 19:29:13414 TemplateURLService* template_url_service = client()->GetTemplateURLService();
Kevin Bailey9bdd15d2018-02-28 04:07:49415 DCHECK(template_url_service);
[email protected]bb1fb2b2013-05-31 00:21:01416 const TemplateURL* default_provider =
blundelld130d592015-06-21 19:29:13417 template_url_service->GetDefaultSearchProvider();
[email protected]6ce7f612012-09-05 23:53:07418 // Fail if we can't set the clickthrough URL for query suggestions.
Ivan Kotenkov75b1c3a2017-10-24 14:47:24419 if (default_provider == nullptr ||
blundelld130d592015-06-21 19:29:13420 !default_provider->SupportsReplacement(
421 template_url_service->search_terms_data()))
[email protected]6ce7f612012-09-05 23:53:07422 return;
[email protected]6ce7f612012-09-05 23:53:07423
[email protected]00404742014-02-20 13:09:05424 MatchMap map;
425 AddSuggestResultsToMap(results_.suggest_results, &map);
426
427 const int num_query_results = map.size();
428 const int num_nav_results = results_.navigation_results.size();
[email protected]bb1fb2b2013-05-31 00:21:01429 const int num_results = num_query_results + num_nav_results;
[email protected]9c97f89c2013-06-25 03:12:16430 UMA_HISTOGRAM_COUNTS("ZeroSuggest.QueryResults", num_query_results);
[email protected]78981d8c2014-05-09 15:05:47431 UMA_HISTOGRAM_COUNTS("ZeroSuggest.URLResults", num_nav_results);
[email protected]9c97f89c2013-06-25 03:12:16432 UMA_HISTOGRAM_COUNTS("ZeroSuggest.AllResults", num_results);
[email protected]bb1fb2b2013-05-31 00:21:01433
[email protected]8f064e52013-09-18 01:17:14434 // Show Most Visited results after ZeroSuggest response is received.
Gheorghe Comanici9a364f572018-01-24 17:22:00435 if (result_type_running_ == ResultType::MOST_VISITED) {
[email protected]3feb8b002013-10-14 23:50:13436 if (!current_url_match_.destination_url.is_valid())
437 return;
[email protected]8f064e52013-09-18 01:17:14438 matches_.push_back(current_url_match_);
439 int relevance = 600;
440 if (num_results > 0) {
441 UMA_HISTOGRAM_COUNTS(
442 "Omnibox.ZeroSuggest.MostVisitedResultsCounterfactual",
443 most_visited_urls_.size());
444 }
[email protected]23db6492014-01-16 02:35:30445 const base::string16 current_query_string16(
446 base::ASCIIToUTF16(current_query_));
[email protected]8f064e52013-09-18 01:17:14447 for (size_t i = 0; i < most_visited_urls_.size(); i++) {
448 const history::MostVisitedURL& url = most_visited_urls_[i];
[email protected]0b9575f2014-07-30 11:58:37449 SearchSuggestionParser::NavigationResult nav(
blundelld130d592015-06-21 19:29:13450 client()->GetSchemeClassifier(), url.url,
gcomanici67d53ac2017-04-01 17:07:19451 AutocompleteMatchType::NAVSUGGEST, 0, url.title, std::string(), false,
jshin1fb76462016-04-05 22:13:03452 relevance, true, current_query_string16);
[email protected]8f064e52013-09-18 01:17:14453 matches_.push_back(NavigationToMatch(nav));
454 --relevance;
455 }
456 return;
457 }
458
[email protected]9c97f89c2013-06-25 03:12:16459 if (num_results == 0)
[email protected]bb1fb2b2013-05-31 00:21:01460 return;
461
462 // TODO(jered): Rip this out once the first match is decoupled from the
463 // current typing in the omnibox.
[email protected]bb1fb2b2013-05-31 00:21:01464 matches_.push_back(current_url_match_);
465
[email protected]00404742014-02-20 13:09:05466 for (MatchMap::const_iterator it(map.begin()); it != map.end(); ++it)
[email protected]bb1fb2b2013-05-31 00:21:01467 matches_.push_back(it->second);
[email protected]bb1fb2b2013-05-31 00:21:01468
[email protected]0b9575f2014-07-30 11:58:37469 const SearchSuggestionParser::NavigationResults& nav_results(
470 results_.navigation_results);
471 for (SearchSuggestionParser::NavigationResults::const_iterator it(
gcomanici67d53ac2017-04-01 17:07:19472 nav_results.begin());
473 it != nav_results.end(); ++it) {
[email protected]bb1fb2b2013-05-31 00:21:01474 matches_.push_back(NavigationToMatch(*it));
gcomanici67d53ac2017-04-01 17:07:19475 }
[email protected]6ce7f612012-09-05 23:53:07476}
477
[email protected]bb1fb2b2013-05-31 00:21:01478AutocompleteMatch ZeroSuggestProvider::MatchForCurrentURL() {
[email protected]bb1fb2b2013-05-31 00:21:01479 // The placeholder suggestion for the current URL has high relevance so
480 // that it is in the first suggestion slot and inline autocompleted. It
481 // gets dropped as soon as the user types something.
mpearson931028c2016-07-01 18:55:11482 AutocompleteInput tmp(GetInput(false));
483 tmp.UpdateText(permanent_text_, base::string16::npos, tmp.parts());
gcomanici8cabc77f2017-04-27 20:04:54484 const base::string16 description =
485 (base::FeatureList::IsEnabled(omnibox::kDisplayTitleForCurrentUrl))
486 ? current_title_
487 : base::string16();
488 return VerbatimMatchForURL(client(), tmp, GURL(current_query_), description,
mpearson931028c2016-07-01 18:55:11489 history_url_provider_,
sdefresne70948d62015-08-11 10:46:35490 results_.verbatim_relevance);
[email protected]00404742014-02-20 13:09:05491}
[email protected]162c8d9fa2014-03-18 20:25:41492
Gheorghe Comanici9a364f572018-01-24 17:22:00493bool ZeroSuggestProvider::AllowZeroSuggestSuggestions(
[email protected]162c8d9fa2014-03-18 20:25:41494 const GURL& current_page_url) const {
Peter Kastingfb1a8ea2017-11-28 02:26:50495 // Don't show zero suggest on the NTP.
496 // TODO(hfung): Experiment with showing MostVisited zero suggest on NTP
497 // under the conditions described in crbug.com/305366.
498 if (IsNTPPage(current_page_classification_))
499 return false;
500
501 // Don't run if in incognito mode.
502 if (client()->IsOffTheRecord())
[email protected]162c8d9fa2014-03-18 20:25:41503 return false;
504
[email protected]162c8d9fa2014-03-18 20:25:41505 // Only show zero suggest for HTTP[S] pages.
506 // TODO(mariakhomenko): We may be able to expand this set to include pages
507 // with other schemes (e.g. chrome://). That may require improvements to
508 // the formatting of the verbatim result returned by MatchForCurrentURL().
509 if (!current_page_url.is_valid() ||
[email protected]e8ca69c2014-05-07 15:31:19510 ((current_page_url.scheme() != url::kHttpScheme) &&
511 (current_page_url.scheme() != url::kHttpsScheme)))
[email protected]162c8d9fa2014-03-18 20:25:41512 return false;
513
[email protected]162c8d9fa2014-03-18 20:25:41514 return true;
515}
[email protected]855ebff2014-05-09 07:14:38516
517void ZeroSuggestProvider::MaybeUseCachedSuggestions() {
Gheorghe Comanici9a364f572018-01-24 17:22:00518 if (result_type_running_ != ZeroSuggestProvider::DEFAULT_SERP)
[email protected]855ebff2014-05-09 07:14:38519 return;
520
blundelld130d592015-06-21 19:29:13521 std::string json_data =
522 client()->GetPrefs()->GetString(omnibox::kZeroSuggestCachedResults);
[email protected]855ebff2014-05-09 07:14:38523 if (!json_data.empty()) {
dcheng259570c2016-04-22 00:45:57524 std::unique_ptr<base::Value> data(
[email protected]2c802d12014-07-31 12:57:14525 SearchSuggestionParser::DeserializeJsonData(json_data));
Gheorghe Comanici9a364f572018-01-24 17:22:00526 if (data && ParseSuggestResults(*data, kDefaultZeroSuggestRelevance, false,
527 &results_))
[email protected]855ebff2014-05-09 07:14:38528 ConvertResultsToAutocompleteMatches();
[email protected]855ebff2014-05-09 07:14:38529 }
530}
Gheorghe Comanici9a364f572018-01-24 17:22:00531
532ZeroSuggestProvider::ResultType ZeroSuggestProvider::TypeOfResultToRun(
533 const GURL& current_url,
534 const GURL& suggest_url) {
535 // TODO(jered): Consider adding locally-sourced zero-suggestions here too.
536 // These may be useful on the NTP or more relevant to the user than server
537 // suggestions, if based on local browsing history.
538
539 // Check if the URL can be sent in any suggest request.
540 const TemplateURLService* template_url_service =
541 client()->GetTemplateURLService();
Kevin Bailey9bdd15d2018-02-28 04:07:49542 DCHECK(template_url_service);
Gheorghe Comanici9a364f572018-01-24 17:22:00543 const TemplateURL* default_provider =
544 template_url_service->GetDefaultSearchProvider();
545 const bool can_send_current_url = CanSendURL(
546 current_url, suggest_url, default_provider, current_page_classification_,
547 template_url_service->search_terms_data(), client());
548
549 // Collect metrics on eligibility.
550 GURL arbitrary_insecure_url(kArbitraryInsecureUrlString);
551 ZeroSuggestEligibility eligibility = ZeroSuggestEligibility::ELIGIBLE;
552 if (!can_send_current_url) {
553 const bool can_send_ordinary_url =
554 CanSendURL(arbitrary_insecure_url, suggest_url, default_provider,
555 current_page_classification_,
556 template_url_service->search_terms_data(), client());
557 eligibility = can_send_ordinary_url
558 ? ZeroSuggestEligibility::URL_INELIGIBLE
559 : ZeroSuggestEligibility::GENERALLY_INELIGIBLE;
560 }
561 UMA_HISTOGRAM_ENUMERATION(
562 "Omnibox.ZeroSuggest.Eligible.OnFocus", static_cast<int>(eligibility),
563 static_cast<int>(ZeroSuggestEligibility::ELIGIBLE_MAX_VALUE));
564
565 // Check if zero suggestions are allowed in the current context.
566 if (!AllowZeroSuggestSuggestions(current_url))
567 return ResultType::NONE;
568
Theresa3db4ae22018-03-05 18:47:40569 if (OmniboxFieldTrial::InZeroSuggestPersonalizedFieldTrial())
Gheorghe Comanici9a364f572018-01-24 17:22:00570 return PersonalizedServiceShouldFallBackToMostVisited(
571 client()->GetPrefs(), client()->IsAuthenticated(),
572 template_url_service)
573 ? ResultType::MOST_VISITED
574 : ResultType::DEFAULT_SERP;
575
Theresa3db4ae22018-03-05 18:47:40576 if (OmniboxFieldTrial::InZeroSuggestMostVisitedWithoutSerpFieldTrial() &&
Gheorghe Comanici9a364f572018-01-24 17:22:00577 client()
578 ->GetTemplateURLService()
579 ->IsSearchResultsPageFromDefaultSearchProvider(current_url))
580 return ResultType::NONE;
581
Theresa3db4ae22018-03-05 18:47:40582 if (OmniboxFieldTrial::InZeroSuggestMostVisitedFieldTrial())
Gheorghe Comanici9a364f572018-01-24 17:22:00583 return ResultType::MOST_VISITED;
584
585 return can_send_current_url ? ResultType::DEFAULT_SERP_FOR_URL
586 : ResultType::NONE;
587}