// Copyright 2024 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/ash/crosapi/search_controller_ash.h"
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include "base/check.h"
#include "base/functional/bind.h"
#include "base/logging.h"
#include "base/memory/weak_ptr.h"
#include "chromeos/crosapi/mojom/launcher_search.mojom-forward.h"
#include "chromeos/crosapi/mojom/launcher_search.mojom-shared.h"
#include "mojo/public/cpp/bindings/pending_associated_receiver.h"
#include "mojo/public/cpp/bindings/pending_remote.h"
namespace crosapi {
SearchControllerAsh::SearchControllerAsh(
mojo::PendingRemote<mojom::SearchController> search_controller)
: search_controller_(std::move(search_controller)) {
search_controller_.set_disconnect_handler(base::BindOnce(
&SearchControllerAsh::HandleDisconnect, weak_factory_.GetWeakPtr()));
}
SearchControllerAsh::~SearchControllerAsh() = default;
void SearchControllerAsh::Search(const std::u16string& query,
SearchResultsReceivedCallback callback) {
if (search_controller_.is_connected()) {
search_controller_->Search(
query, base::BindOnce(&SearchControllerAsh::BindPublisher,
weak_factory_.GetWeakPtr(), std::move(callback)));
}
}
void SearchControllerAsh::OnSearchResultsReceived(
mojom::SearchStatus status,
std::optional<std::vector<mojom::SearchResultPtr>> results) {
switch (status) {
case mojom::SearchStatus::kError: {
LOG(ERROR) << "Search failed.";
publisher_receivers_.Remove(publisher_receivers_.current_receiver());
return;
}
case mojom::SearchStatus::kDone: {
const auto& callback = publisher_receivers_.current_context();
if (results.has_value() && !callback.is_null()) {
callback.Run(std::move(results.value()));
}
return;
}
case mojom::SearchStatus::kInProgress:
case mojom::SearchStatus::kCancelled:
case mojom::SearchStatus::kBackendUnavailable: {
return;
}
}
}
bool SearchControllerAsh::IsConnected() const {
return search_controller_.is_connected();
}
void SearchControllerAsh::AddDisconnectHandler(DisconnectCallback handler) {
if (!IsConnected()) {
std::move(handler).Run(weak_factory_.GetWeakPtr());
// USE-AFTER-FREE SAFETY: As disconnect handlers may destroy `this`, we
// cannot refer to `this` (including using members or methods) after this
// point.
return;
}
disconnect_callbacks_.push_back(std::move(handler));
}
void SearchControllerAsh::HandleDisconnect() {
CHECK(!IsConnected());
base::WeakPtr<SearchControllerAsh> local_weak_this =
weak_factory_.GetWeakPtr();
// Move the disconnect callbacks into a local variable to ensure that they do
// not get destroyed if `this` is destroyed.
std::vector<DisconnectCallback> local_disconnect_callbacks =
std::move(disconnect_callbacks_);
// USE-AFTER-FREE SAFETY: As disconnect handlers may destroy `this`, we cannot
// refer to `this` (including using members or methods) after this point.
//
// Use verbose local variable names to ensure that members are not
// accidentally used instead.
for (DisconnectCallback& callback : local_disconnect_callbacks) {
std::move(callback).Run(local_weak_this);
}
}
void SearchControllerAsh::BindPublisher(
SearchResultsReceivedCallback callback,
mojo::PendingAssociatedReceiver<mojom::SearchResultsPublisher> publisher) {
publisher_receivers_.Add(this, std::move(publisher), std::move(callback));
}
} // namespace crosapi