blob: ddac4462c25303c11cb0599ad420ddaa1c974af5 [file] [log] [blame] [edit]
// Copyright 2017 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef COMPONENTS_OMNIBOX_BROWSER_REMOTE_SUGGESTIONS_SERVICE_H_
#define COMPONENTS_OMNIBOX_BROWSER_REMOTE_SUGGESTIONS_SERVICE_H_
#include <map>
#include <memory>
#include <optional>
#include <string>
#include "base/functional/callback_forward.h"
#include "base/memory/scoped_refptr.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "base/observer_list_types.h"
#include "base/time/time.h"
#include "base/timer/elapsed_timer.h"
#include "base/unguessable_token.h"
#include "components/keyed_service/core/keyed_service.h"
#include "components/omnibox/browser/autocomplete_input.h"
#include "components/omnibox/browser/autocomplete_match.h"
#include "components/omnibox/browser/enterprise_search_aggregator_suggestions_service.h"
#include "components/search_engines/template_url.h"
#include "net/traffic_annotation/network_traffic_annotation.h"
#include "url/gurl.h"
class DocumentSuggestionsService;
class EnterpriseSearchAggregatorSuggestionsService;
using EnterpriseSearchAggregatorSuggestionType =
AutocompleteMatch::EnterpriseSearchAggregatorType;
namespace network {
class SharedURLLoaderFactory;
class SimpleURLLoader;
struct ResourceRequest;
} // namespace network
// The types of requests for remote suggestions.
// These values are written to logs. New enum values can be added, but existing
// enums must never be renumbered or deleted and reused.
// Must be kept in sync with RemoteRequestType enum and variant.
// LINT.IfChange(RemoteRequestType)
enum class RemoteRequestType {
// Search suggestion requests.
kSearch = 0,
// Search suggestion warm-up requests.
kSearchWarmup = 1,
// Search suggestion requests to obtain images.
kImages = 2,
// Zero-prefix suggestion requests.
kZeroSuggest = 3,
// Zero-prefix suggestion prefetching requests.
kZeroSuggestPrefetch = 4,
// Document suggestion requests.
kDocumentSuggest = 5,
// Suggestion deletion requests.
kDeletion = 6,
// Enterprise Search Aggregator suggestion requests.
kEnterpriseSearchAggregatorSuggest = 7,
kMaxValue = kEnterpriseSearchAggregatorSuggest,
};
// LINT.ThenChange(
// //tools/metrics/histograms/metadata/omnibox/enums.xml:RemoteRequestType,
// //tools/metrics/histograms/metadata/omnibox/histograms.xml:RemoteRequestType
// )
// The event types recorded by the providers for remote suggestions. Each event
// must be logged at most once from when the provider is started until it is
// stopped.
// These values are written to logs. New enum values can be added, but existing
// enums must never be renumbered or deleted and reused.
enum class RemoteRequestEvent {
// Cached response was synchronously converted to displayed matches.
// Recorded for non-prefetch requests only.
kCachedResponseConvertedToMatches = 0,
// Request was sent.
kRequestSent = 1,
// Request was invalidated.
kRequestInvalidated = 2,
// Response was received asynchronously.
kResponseReceived = 3,
// Response was cached.
kResponseCached = 4,
// Response ended up being converted to displayed matches. This may happen due
// to an empty displayed result set or an empty remote result set.
// Recorded for non-prefetch requests only.
kResponseConvertedToMatches = 5,
kMaxValue = kResponseConvertedToMatches,
};
// A service to fetch suggestions from a search provider's suggest endpoint.
// Used by ZeroSuggestProvider, SearchProvider, DocumentProvider, and
// ImageService.
//
// This service is always sent the user's authentication state, so the
// suggestions always can be personalized. This service is also sometimes sent
// the user's current URL, so the suggestions are sometimes also contextual.
class RemoteSuggestionsService : public KeyedService {
public:
class Observer : public base::CheckedObserver {
public:
// Called when the request has been created. `request_id` identifies the
// request. `request` is deleted after this call once the transfer starts.
virtual void OnRequestCreated(const base::UnguessableToken& request_id,
const network::ResourceRequest* request) {}
// Called when the transfer has started. `request_id` identifies the
// request. `request_body` is the HTTP POST upload body, if applicable.
virtual void OnRequestStarted(const base::UnguessableToken& request_id,
network::SimpleURLLoader* loader,
const std::string& request_body) {}
// Called when the transfer is done. `request_id` identifies the request.
// `response_code` is the response status code. A status code of 200
// indicates that the request has succeeded and `response_body` is
// populated.
virtual void OnRequestCompleted(
const base::UnguessableToken& request_id,
const int response_code,
const std::unique_ptr<std::string>& response_body) {}
};
// Called when the transfer has started asynchronously, e.g., after obtaining
// an OAuth token.
using StartCallback = base::OnceCallback<void(
std::unique_ptr<network::SimpleURLLoader> loader)>;
// Called when the transfer is done. `response_code` is the response status
// code. A status code of 200 indicates that the request has succeeded and
// `response_body` is populated.
using CompletionCallback =
base::OnceCallback<void(const network::SimpleURLLoader* source,
const int response_code,
std::unique_ptr<std::string> response_body)>;
// Same as `StartCallback` but for requests that are associated with a
// `request_index`.
using IndexedStartCallback = base::RepeatingCallback<void(
const int request_index,
std::unique_ptr<network::SimpleURLLoader> loader)>;
// Same as `CompletionCallback` but for requests that are associated with a
// `request_index`.
using IndexedCompletionCallback =
base::RepeatingCallback<void(const int request_index,
const network::SimpleURLLoader* source,
const int response_code,
std::unique_ptr<std::string> response_body)>;
class Delegate {
public:
Delegate();
virtual ~Delegate();
Delegate(const Delegate&) = delete;
Delegate& operator=(const Delegate&) = delete;
// Called when the transfer is done. Delegates invocation of
// `completion_callback`
virtual void OnRequestCompleted(const network::SimpleURLLoader* source,
const int response_code,
std::unique_ptr<std::string> response_body,
CompletionCallback completion_callback) = 0;
virtual void OnIndexedRequestCompleted(
const int request_index,
const network::SimpleURLLoader* source,
const int response_code,
std::unique_ptr<std::string> response_body,
IndexedCompletionCallback completion_callback) = 0;
protected:
base::WeakPtrFactory<Delegate> weak_ptr_factory_{this};
};
RemoteSuggestionsService(
DocumentSuggestionsService* document_suggestions_service,
EnterpriseSearchAggregatorSuggestionsService*
enterprise_search_aggregator_suggestions_service,
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
~RemoteSuggestionsService() override;
RemoteSuggestionsService(const RemoteSuggestionsService&) = delete;
RemoteSuggestionsService& operator=(const RemoteSuggestionsService&) = delete;
// Helper to set the time request of type `request_type` has started in
// `time_request_sent_`.
void SetTimeRequestSent(RemoteRequestType request_type, base::TimeTicks time);
// Returns the suggest endpoint URL for `template_url`.
// `search_terms_args` is used to build the endpoint URL.
// `search_terms_data` is used to build the endpoint URL.
static GURL EndpointUrl(
const TemplateURL& template_url,
const TemplateURLRef::SearchTermsArgs& search_terms_args,
const SearchTermsData& search_terms_data);
// Creates and returns a loader for remote suggestions for `template_url`.
// It uses a number of signals to create the loader, including field trial
// parameters.
//
// `template_url` must not be nullptr.
// `search_terms_args` is used to build the endpoint URL.
// `search_terms_data` is used to build the endpoint URL.
// `completion_callback` will be invoked when the transfer is done.
std::unique_ptr<network::SimpleURLLoader> StartSuggestionsRequest(
RemoteRequestType request_type,
bool is_off_the_record,
const TemplateURL* template_url,
TemplateURLRef::SearchTermsArgs search_terms_args,
const SearchTermsData& search_terms_data,
CompletionCallback completion_callback);
// Creates and returns a loader for remote zero-prefix suggestions for
// `template_url`. It uses a number of signals to create the loader, including
// field trial parameters.
//
// `template_url` must not be nullptr.
// `search_terms_args` is used to build the endpoint URL.
// `search_terms_data` is used to build the endpoint URL.
// `completion_callback` will be invoked when the transfer is done.
std::unique_ptr<network::SimpleURLLoader> StartZeroPrefixSuggestionsRequest(
RemoteRequestType request_type,
bool is_off_the_record,
const TemplateURL* template_url,
TemplateURLRef::SearchTermsArgs search_terms_args,
const SearchTermsData& search_terms_data,
CompletionCallback completion_callback);
// Creates and starts a document suggestion request for `query` asynchronously
// after obtaining an OAuth2 token for the signed-in users.
void CreateDocumentSuggestionsRequest(
const std::u16string& query,
bool is_off_the_record,
metrics::OmniboxEventProto::PageClassification page_classification,
StartCallback start_callback,
CompletionCallback completion_callback);
// Stops creating the request. Already created requests aren't affected.
void StopCreatingDocumentSuggestionsRequest();
// Creates and starts an enterprise search aggregator suggestion request using
// `suggest_url` and `response_body` asynchronously after obtaining an OAuth2
// token for signed-in enterprise users.
void CreateEnterpriseSearchAggregatorSuggestionsRequest(
const std::u16string& query,
const GURL& suggest_url,
metrics::OmniboxEventProto::PageClassification page_classification,
std::vector<int> callback_indexes,
std::vector<std::vector<int>> suggestion_types,
IndexedStartCallback start_callback,
IndexedCompletionCallback completion_callback);
// Stops creating the request. Already created requests aren't affected.
void StopCreatingEnterpriseSearchAggregatorSuggestionsRequest();
// Creates and returns a loader to delete personalized suggestions.
//
// `deletion_url` must be a valid URL.
// `completion_callback` will be invoked when the transfer is done.
std::unique_ptr<network::SimpleURLLoader> StartDeletionRequest(
const std::string& deletion_url,
bool is_off_the_record,
CompletionCallback completion_callback);
void AddObserver(Observer* observer);
void RemoveObserver(Observer* observer);
void SetDelegate(base::WeakPtr<Delegate> delegate);
// Exposed for testing.
void set_url_loader_factory_for_testing(
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory);
private:
// Called when the request has been created, before the transfer has started.
// Notifies `observers_`.
void OnRequestCreated(const base::UnguessableToken& request_id,
network::ResourceRequest* request);
// Called when the transfer has started. Notifies `observers_`.
void OnRequestStarted(
const base::UnguessableToken& request_id,
RemoteRequestType request_type,
metrics::OmniboxEventProto::PageClassification page_classification,
network::SimpleURLLoader* loader,
const std::string& request_body);
// Called when the transfer has started asynchronously, e.g., after obtaining
// an OAuth token. Notifies `observers_` and calls `start_callback` to
// transfer the ownership of `loader` to the caller.
void OnRequestStartedAsync(
const base::UnguessableToken& request_id,
RemoteRequestType request_type,
metrics::OmniboxEventProto::PageClassification page_classification,
StartCallback start_callback,
std::unique_ptr<network::SimpleURLLoader> loader,
const std::string& request_body);
void OnIndexedRequestStartedAsync(
const base::UnguessableToken& request_id,
RemoteRequestType request_type,
metrics::OmniboxEventProto::PageClassification page_classification,
IndexedStartCallback start_callback,
int request_index,
std::unique_ptr<network::SimpleURLLoader> loader,
const std::string& request_body);
// Called when the transfer is done. Notifies `observers_` and calls
// `completion_callback` passing the response to the caller.
void OnRequestCompleted(
const base::UnguessableToken& request_id,
RemoteRequestType request_type,
base::ElapsedTimer request_timer,
metrics::OmniboxEventProto::PageClassification page_classification,
CompletionCallback completion_callback,
const network::SimpleURLLoader* source,
std::unique_ptr<std::string> response_body);
void OnIndexedRequestCompleted(
const base::UnguessableToken& request_id,
RemoteRequestType request_type,
metrics::OmniboxEventProto::PageClassification page_classification,
base::TimeTicks start_time,
IndexedCompletionCallback completion_callback,
const network::SimpleURLLoader* source,
int request_index,
std::unique_ptr<std::string> response_body);
// May be nullptr in OTR profiles. Otherwise guaranteed to outlive this due to
// the factories' dependency.
raw_ptr<DocumentSuggestionsService> document_suggestions_service_;
// May be nullptr in OTR profiles. Otherwise guaranteed to outlive this due to
// the factories' dependency.
raw_ptr<EnterpriseSearchAggregatorSuggestionsService>
enterprise_search_aggregator_suggestions_service_;
scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory_;
// Time request sent for each RemoteRequestType. Used for histogram logging.
std::map<RemoteRequestType, base::TimeTicks> time_request_sent_;
// Observers being notified of request start and completion events.
base::ObserverList<Observer> observers_;
// Delegate to which invocation of completion callback is delegated.
base::WeakPtr<Delegate> delegate_;
// Used to bind `OnURLLoadComplete` to the network loader's callback as the
// loader is no longer owned by `this` once returned.
base::WeakPtrFactory<RemoteSuggestionsService> weak_ptr_factory_{this};
};
#endif // COMPONENTS_OMNIBOX_BROWSER_REMOTE_SUGGESTIONS_SERVICE_H_