| // Copyright 2020 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| #include "chromeos/components/quick_answers/result_loader.h" |
| |
| #include "base/functional/bind.h" |
| #include "chromeos/components/quick_answers/public/cpp/quick_answers_prefs.h" |
| #include "chromeos/components/quick_answers/public/cpp/quick_answers_state.h" |
| #include "chromeos/components/quick_answers/quick_answers_model.h" |
| #include "chromeos/components/quick_answers/search_result_loader.h" |
| #include "chromeos/components/quick_answers/translation_result_loader.h" |
| #include "chromeos/components/quick_answers/utils/quick_answers_metrics.h" |
| #include "services/network/public/cpp/resource_request.h" |
| #include "services/network/public/cpp/shared_url_loader_factory.h" |
| #include "services/network/public/cpp/simple_url_loader.h" |
| #include "services/network/public/mojom/url_response_head.mojom.h" |
| |
| namespace quick_answers { |
| namespace { |
| |
| using network::ResourceRequest; |
| using network::SharedURLLoaderFactory; |
| |
| constexpr net::NetworkTrafficAnnotationTag kNetworkTrafficAnnotationTag = |
| net::DefineNetworkTrafficAnnotation("quick_answers_loader", R"( |
| semantics: { |
| sender: "ChromeOS Quick Answers" |
| description: |
| "ChromeOS requests quick answers based on the currently selected " |
| "text to look up a translation, dictionary definition, " |
| "or unit conversion." |
| trigger: |
| "Right click to trigger context menu." |
| data: "Currently selected text is sent to Google API for " |
| "generating answers. Source language of the selected text " |
| "is sent to Google API only for translation and dictionary " |
| "definition. Device language is sent to Google API " |
| "only for translation." |
| destination: GOOGLE_OWNED_SERVICE |
| } |
| policy: { |
| cookies_allowed: YES |
| cookies_store: "system" |
| setting: |
| "Quick Answers can be enabled/disabled in Chrome Settings and is " |
| "subject to eligibility requirements. The user may also " |
| "separately opt out of sharing screen context with Assistant." |
| chrome_policy { |
| QuickAnswersEnabled { |
| QuickAnswersEnabled: false |
| } |
| QuickAnswersTranslationEnabled { |
| QuickAnswersTranslationEnabled: false |
| } |
| QuickAnswersDefinitionEnabled { |
| QuickAnswersDefinitionEnabled: false |
| } |
| QuickAnswersUnitConversionEnabled { |
| QuickAnswersUnitConversionEnabled: false |
| } |
| } |
| })"); |
| |
| } // namespace |
| |
| ResultLoader::ResultLoader( |
| scoped_refptr<SharedURLLoaderFactory> url_loader_factory, |
| ResultLoaderDelegate* delegate) |
| : url_loader_factory_(url_loader_factory), delegate_(delegate) {} |
| |
| ResultLoader::~ResultLoader() = default; |
| |
| // static |
| std::unique_ptr<ResultLoader> ResultLoader::Create( |
| IntentType intent_type, |
| scoped_refptr<SharedURLLoaderFactory> url_loader_factory, |
| ResultLoader::ResultLoaderDelegate* delegate) { |
| if (intent_type == IntentType::kTranslation) { |
| return std::make_unique<TranslationResultLoader>(url_loader_factory, |
| delegate); |
| } |
| return std::make_unique<SearchResultLoader>(url_loader_factory, delegate); |
| } |
| |
| void ResultLoader::Fetch(const PreprocessedOutput& preprocessed_output) { |
| DCHECK(url_loader_factory_); |
| DCHECK(!preprocessed_output.query.empty()); |
| |
| // Fail-safe for a fetch request if `consent_status` is not `kAccepted`. |
| CHECK(QuickAnswersState::Get()->consent_status() == |
| quick_answers::prefs::ConsentStatus::kAccepted); |
| |
| // Load the resource. |
| BuildRequest(preprocessed_output, |
| base::BindOnce(&ResultLoader::OnBuildRequestComplete, |
| weak_factory_.GetWeakPtr(), preprocessed_output)); |
| } |
| |
| void ResultLoader::OnBuildRequestComplete( |
| const PreprocessedOutput& preprocessed_output, |
| std::unique_ptr<network::ResourceRequest> resource_request, |
| const std::string& request_body) { |
| loader_ = network::SimpleURLLoader::Create(std::move(resource_request), |
| kNetworkTrafficAnnotationTag); |
| if (!request_body.empty()) { |
| loader_->AttachStringForUpload(request_body, "application/json"); |
| } |
| |
| loader_->SetRetryOptions( |
| /*max_retries=*/5, network::SimpleURLLoader::RetryMode::RETRY_ON_5XX | |
| network::SimpleURLLoader::RETRY_ON_NETWORK_CHANGE); |
| |
| fetch_start_time_ = base::TimeTicks::Now(); |
| loader_->DownloadToString( |
| url_loader_factory_.get(), |
| base::BindOnce(&ResultLoader::OnSimpleURLLoaderComplete, |
| weak_factory_.GetWeakPtr(), preprocessed_output), |
| network::SimpleURLLoader::kMaxBoundedStringDownloadSize); |
| } |
| |
| void ResultLoader::OnSimpleURLLoaderComplete( |
| const PreprocessedOutput& preprocessed_output, |
| std::unique_ptr<std::string> response_body) { |
| base::TimeDelta duration = base::TimeTicks::Now() - fetch_start_time_; |
| |
| if (!response_body || loader_->NetError() != net::OK || |
| !loader_->ResponseInfo() || !loader_->ResponseInfo()->headers) { |
| int response_code = -1; |
| if (loader_->ResponseInfo() && loader_->ResponseInfo()->headers) { |
| response_code = loader_->ResponseInfo()->headers->response_code(); |
| } |
| RecordLoadingStatus(LoadStatus::kNetworkError, duration); |
| RecordNetworkError(preprocessed_output.intent_info.intent_type, |
| response_code); |
| delegate_->OnNetworkError(); |
| return; |
| } |
| |
| ProcessResponse(preprocessed_output, std::move(response_body), |
| base::BindOnce(&ResultLoader::OnResultParserComplete, |
| weak_factory_.GetWeakPtr())); |
| } |
| |
| void ResultLoader::OnResultParserComplete( |
| std::unique_ptr<QuickAnswersSession> quick_answers_session) { |
| raw_ptr<QuickAnswer> quick_answer = |
| quick_answers_session ? quick_answers_session->quick_answer.get() |
| : nullptr; |
| // Record quick answer result. |
| base::TimeDelta duration = base::TimeTicks::Now() - fetch_start_time_; |
| RecordLoadingStatus( |
| quick_answer ? LoadStatus::kSuccess : LoadStatus::kNoResult, duration); |
| RecordResult(quick_answer ? quick_answer->result_type : ResultType::kNoResult, |
| duration); |
| |
| delegate_->OnQuickAnswerReceived(std::move(quick_answers_session)); |
| } |
| } // namespace quick_answers |