chromium/chrome/browser/lacros/launcher_search/search_controller_lacros.cc

// Copyright 2022 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "chrome/browser/lacros/launcher_search/search_controller_lacros.h"

#include <utility>

#include "chrome/browser/autocomplete/chrome_autocomplete_provider_client.h"
#include "chrome/browser/autocomplete/chrome_autocomplete_scheme_classifier.h"
#include "chrome/browser/bookmarks/bookmark_model_factory.h"
#include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/launcher_search/search_util.h"
#include "chrome/browser/favicon/favicon_service_factory.h"
#include "chrome/browser/history/history_service_factory.h"
#include "chrome/browser/profiles/profile_manager.h"
#include "chromeos/lacros/lacros_service.h"
#include "components/bookmarks/browser/bookmark_model.h"
#include "components/keyed_service/core/service_access_type.h"
#include "third_party/omnibox_proto/rich_answer_template.pb.h"

namespace crosapi {

SearchControllerLacros::SearchControllerLacros(int provider_types)
    : profile_(g_browser_process->profile_manager()->GetProfileByPath(
          ProfileManager::GetPrimaryUserProfilePath())) {
  if (!profile_) {
    // TODO(crbug.com/40189614): Log error metrics if the profile is
    // unavailable.
    return;
  }

  profile_observation_.Observe(profile_.get());

  autocomplete_controller_ = std::make_unique<AutocompleteController>(
      std::make_unique<ChromeAutocompleteProviderClient>(profile_),
      provider_types, /*is_cros_launcher=*/true);
  autocomplete_controller_->AddObserver(this);

  favicon_cache_ = std::make_unique<FaviconCache>(
      FaviconServiceFactory::GetForProfile(profile_,
                                           ServiceAccessType::EXPLICIT_ACCESS),
      HistoryServiceFactory::GetForProfile(profile_,
                                           ServiceAccessType::EXPLICIT_ACCESS));
}

SearchControllerLacros::~SearchControllerLacros() = default;

void SearchControllerLacros::OnProfileWillBeDestroyed(Profile* profile) {
  DCHECK_EQ(profile, profile_);
  DCHECK(profile_observation_.IsObservingSource(profile_.get()));

  // SearchControllerLacros must shut down before the Profile is destroyed,
  // otherwise there will be a use-after-free.
  weak_ptr_factory_.InvalidateWeakPtrs();
  autocomplete_controller_.reset();
  favicon_cache_.reset();
  profile_observation_.Reset();
  profile_ = nullptr;
}

void SearchControllerLacros::RegisterWithAsh() {
  chromeos::LacrosService* service = chromeos::LacrosService::Get();
  if (!service->IsAvailable<mojom::SearchControllerRegistry>()) {
    return;
  }
  service->GetRemote<mojom::SearchControllerRegistry>()
      ->RegisterSearchController(receiver_.BindNewPipeAndPassRemote());
}

void SearchControllerLacros::Search(const std::u16string& query,
                                    SearchCallback callback) {
  if (!autocomplete_controller_) {
    // TODO(crbug.com/40777889): Log error metrics if the autocomplete
    // controller is unavailable.
    if (publisher_.is_bound() && publisher_.is_connected()) {
      publisher_->OnSearchResultsReceived(
          mojom::SearchStatus::kBackendUnavailable, std::nullopt);
    }
    return;
  }

  autocomplete_controller_->Stop(false);
  // If there is an in-flight session, send a cancellation notification.
  if (publisher_.is_bound() && publisher_.is_connected()) {
    publisher_->OnSearchResultsReceived(mojom::SearchStatus::kCancelled,
                                        std::nullopt);
  }

  // Reset the remote and send a new pending receiver to ash.
  publisher_.reset();
  std::move(callback).Run(publisher_.BindNewEndpointAndPassReceiver());

  // Start the search. Results will be returned through the observer interface.
  AutocompleteInput input =
      AutocompleteInput(query, metrics::OmniboxEventProto::CHROMEOS_APP_LIST,
                        ChromeAutocompleteSchemeClassifier(profile_));
  if (input.text().empty())
    input.set_focus_type(metrics::OmniboxFocusType::INTERACTION_FOCUS);

  query_ = query;
  input_ = input;
  autocomplete_controller_->Start(input);
}

void SearchControllerLacros::OnResultChanged(AutocompleteController* controller,
                                             bool default_match_changed) {
  DCHECK_EQ(controller, autocomplete_controller_.get());

  std::vector<mojom::SearchResultPtr> results;
  for (AutocompleteMatch match : autocomplete_controller_->result()) {
    // Calculator results are honorary answer results.
    const bool is_answer =
        match.answer_type != omnibox::ANSWER_TYPE_UNSPECIFIED ||
        match.type == AutocompleteMatchType::CALCULATOR;
    auto result =
        is_answer
            ? CreateAnswerResult(match, autocomplete_controller_.get(), query_,
                                 input_)
            : CreateResult(
                  match, autocomplete_controller_.get(), favicon_cache_.get(),
                  BookmarkModelFactory::GetForBrowserContext(profile_), input_);

    results.push_back(std::move(result));
  }

  const auto status = autocomplete_controller_->done()
                          ? mojom::SearchStatus::kDone
                          : mojom::SearchStatus::kInProgress;
  publisher_->OnSearchResultsReceived(status, std::move(results));
}

}  // namespace crosapi