chromium/chromeos/components/quick_answers/quick_answers_client.cc

// Copyright 2019 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/quick_answers_client.h"

#include <utility>

#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/utils/quick_answers_metrics.h"
#include "chromeos/components/quick_answers/utils/quick_answers_utils.h"
#include "chromeos/components/quick_answers/utils/spell_checker.h"
#include "services/network/public/cpp/shared_url_loader_factory.h"

namespace quick_answers {
namespace {

QuickAnswersClient::ResultLoaderFactoryCallback*
    g_testing_result_factory_callback = nullptr;

QuickAnswersClient::IntentGeneratorFactoryCallback*
    g_testing_intent_generator_factory_callback = nullptr;

}  // namespace

// static
void QuickAnswersClient::SetResultLoaderFactoryForTesting(
    ResultLoaderFactoryCallback* factory) {
  g_testing_result_factory_callback = factory;
}

void QuickAnswersClient::SetIntentGeneratorFactoryForTesting(
    IntentGeneratorFactoryCallback* factory) {
  g_testing_intent_generator_factory_callback = factory;
}

QuickAnswersClient::QuickAnswersClient(
    scoped_refptr<network::SharedURLLoaderFactory> url_loader_factory,
    QuickAnswersDelegate* delegate)
    : url_loader_factory_(url_loader_factory),
      delegate_(delegate),
      spell_checker_(std::make_unique<SpellChecker>(url_loader_factory)) {}

QuickAnswersClient::~QuickAnswersClient() = default;

void QuickAnswersClient::SendRequestForPreprocessing(
    const QuickAnswersRequest& quick_answers_request) {
  SendRequestInternal(quick_answers_request, /*skip_fetch=*/true);
}

void QuickAnswersClient::FetchQuickAnswers(
    const QuickAnswersRequest& preprocessed_request) {
  DCHECK(!preprocessed_request.preprocessed_output.query.empty());

  result_loader_ = CreateResultLoader(
      preprocessed_request.preprocessed_output.intent_info.intent_type);
  // Load and parse search result.
  result_loader_->Fetch(preprocessed_request.preprocessed_output);
}

void QuickAnswersClient::SendRequest(
    const QuickAnswersRequest& quick_answers_request) {
  SendRequestInternal(quick_answers_request, /*skip_fetch=*/false);
}

void QuickAnswersClient::OnQuickAnswerClick(ResultType result_type) {
  RecordClick(result_type, GetImpressionDuration());
}

void QuickAnswersClient::OnQuickAnswersDismissed(ResultType result_type,
                                                 bool is_active) {
  if (is_active)
    RecordActiveImpression(result_type, GetImpressionDuration());
}

std::unique_ptr<ResultLoader> QuickAnswersClient::CreateResultLoader(
    IntentType intent_type) {
  if (g_testing_result_factory_callback)
    return g_testing_result_factory_callback->Run();
  return ResultLoader::Create(intent_type, url_loader_factory_, this);
}

std::unique_ptr<IntentGenerator> QuickAnswersClient::CreateIntentGenerator(
    const QuickAnswersRequest& request,
    bool skip_fetch) {
  if (g_testing_intent_generator_factory_callback)
    return g_testing_intent_generator_factory_callback->Run();
  return std::make_unique<IntentGenerator>(
      spell_checker_ ? spell_checker_->GetWeakPtr() : nullptr,
      base::BindOnce(&QuickAnswersClient::IntentGeneratorCallback,
                     weak_factory_.GetWeakPtr(), request, skip_fetch));
}

void QuickAnswersClient::OnNetworkError() {
  DCHECK(delegate_);
  delegate_->OnNetworkError();
}

void QuickAnswersClient::OnQuickAnswerReceived(
    std::unique_ptr<QuickAnswersSession> quick_answers_session) {
  DCHECK(delegate_);
  quick_answer_received_time_ = base::TimeTicks::Now();
  delegate_->OnQuickAnswerReceived(std::move(quick_answers_session));
}

void QuickAnswersClient::SendRequestInternal(
    const QuickAnswersRequest& quick_answers_request,
    bool skip_fetch) {
  RecordSelectedTextLength(quick_answers_request.selected_text.length());

  // Generate intent from |quick_answers_request|.
  intent_generator_ = CreateIntentGenerator(quick_answers_request, skip_fetch);
  intent_generator_->GenerateIntent(quick_answers_request);
}

void QuickAnswersClient::IntentGeneratorCallback(
    const QuickAnswersRequest& quick_answers_request,
    bool skip_fetch,
    const IntentInfo& intent_info) {
  DCHECK(delegate_);

  // Preprocess the request.
  QuickAnswersRequest processed_request = quick_answers_request;
  processed_request.preprocessed_output = PreprocessRequest(intent_info);

  delegate_->OnRequestPreprocessFinished(processed_request);

  if (QuickAnswersState::Get()->ShouldUseQuickAnswersTextAnnotator()) {
    RecordIntentType(intent_info.intent_type);
    if (intent_info.intent_type == IntentType::kUnknown) {
      // Don't fetch answer if no intent is generated.
      return;
    }
  }

  RecordRequestTextLength(intent_info.intent_type,
                          quick_answers_request.selected_text.length());

  if (!skip_fetch)
    FetchQuickAnswers(processed_request);
}

base::TimeDelta QuickAnswersClient::GetImpressionDuration() const {
  // Use default 0 duration.
  base::TimeDelta duration;
  if (!quick_answer_received_time_.is_null()) {
    // Fetch finish, set the duration to be between fetch finish and now.
    duration = base::TimeTicks::Now() - quick_answer_received_time_;
  }
  return duration;
}

}  // namespace quick_answers