chromium/chrome/browser/ash/crosapi/clipboard_history_ash.cc

// Copyright 2021 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/clipboard_history_ash.h"

#include <utility>

#include "ash/public/cpp/clipboard_history_controller.h"
#include "ash/shell.h"
#include "base/scoped_observation.h"
#include "base/unguessable_token.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/crosapi/mojom/clipboard_history.mojom.h"
#include "chromeos/ui/clipboard_history/clipboard_history_util.h"

namespace crosapi {

namespace {
std::vector<crosapi::mojom::ClipboardHistoryItemDescriptorPtr>
GetCurrentDescriptors() {
  std::vector<crosapi::mojom::ClipboardHistoryItemDescriptorPtr>
      descriptor_ptrs;
  for (const auto& descriptor :
       chromeos::clipboard_history::QueryItemDescriptors()) {
    descriptor_ptrs.push_back(descriptor.Clone());
  }
  return descriptor_ptrs;
}
}  // namespace

// ClipboardHistoryAsh::ClientUpdater ------------------------------------------

// Updates the cached descriptors of the associated client. Used only when the
// clipboard history refresh feature is enabled.
class ClipboardHistoryAsh::ClientUpdater
    : public ash::ClipboardHistoryController::Observer {
 public:
  explicit ClientUpdater(mojom::ClipboardHistoryClient* client)
      : client_(client) {
    CHECK(chromeos::features::IsClipboardHistoryRefreshEnabled());
    observation_.Observe(ash::ClipboardHistoryController::Get());
  }
  ClientUpdater(const ClientUpdater&) = delete;
  ClientUpdater& operator=(const ClientUpdater&) = delete;
  ~ClientUpdater() override = default;

  // ash::ClipboardHistoryController::Observer:
  void OnClipboardHistoryItemsUpdated() override {
    client_->SetClipboardHistoryItemDescriptors(GetCurrentDescriptors());
  }

 private:
  // The associated client.
  const raw_ptr<mojom::ClipboardHistoryClient> client_;

  // The observation on the clipboard history controller.
  base::ScopedObservation<ash::ClipboardHistoryController,
                          ash::ClipboardHistoryController::Observer>
      observation_{this};
};

// ClipboardHistoryAsh ---------------------------------------------------------

ClipboardHistoryAsh::ClipboardHistoryAsh() {
  // `ash::Shell` may not exist in tests.
  if (ash::Shell::HasInstance()) {
    shell_observation_.Observe(ash::Shell::Get());
  }
}

ClipboardHistoryAsh::~ClipboardHistoryAsh() = default;

void ClipboardHistoryAsh::ShowClipboard(
    const gfx::Rect& anchor_point,
    ui::MenuSourceType menu_source_type,
    crosapi::mojom::ClipboardHistoryControllerShowSource show_source) {
  if (auto* clipboard_history_controller =
          ash::ClipboardHistoryController::Get()) {
    clipboard_history_controller->ShowMenu(anchor_point, menu_source_type,
                                           show_source);
  }
}

void ClipboardHistoryAsh::PasteClipboardItemById(
    const base::UnguessableToken& item_id,
    int event_flags,
    mojom::ClipboardHistoryControllerShowSource paste_source) {
  if (auto* clipboard_history_controller =
          ash::ClipboardHistoryController::Get()) {
    clipboard_history_controller->PasteClipboardItemById(
        item_id.ToString(), event_flags, paste_source);
  }
}

void ClipboardHistoryAsh::RegisterClient(
    mojo::PendingRemote<mojom::ClipboardHistoryClient> client) {
  CHECK(chromeos::features::IsClipboardHistoryRefreshEnabled());

  // In testing, `remote_client_` can be already bound when multiple Lacros
  // tests run in parallel. This does not happen on real devices.
  // TODO(http://b/294617428): Make `ClipboardHistoryAsh` support multiple
  // clients then remove this code.
  if (remote_client_.is_bound()) {
    return;
  }

  remote_client_.Bind(std::move(client));

  // `remote_client_` is a class member so it is safe to use `this` here.
  remote_client_.set_disconnect_handler(base::BindOnce(
      &ClipboardHistoryAsh::OnRemoteDisconnected, base::Unretained(this)));

  // If there are clipboard history item descriptors, send the descriptors to
  // `remote_client_` when connection is built.
  if (auto descriptors = GetCurrentDescriptors(); !descriptors.empty()) {
    remote_client_->SetClipboardHistoryItemDescriptors(std::move(descriptors));
  }

  client_updater_ = std::make_unique<ClientUpdater>(remote_client_.get());
}

void ClipboardHistoryAsh::UpdateRemoteDescriptorsForTesting() {
  CHECK(chromeos::features::IsClipboardHistoryRefreshEnabled());
  CHECK(remote_client_.is_bound());
  CHECK(client_updater_);

  client_updater_->OnClipboardHistoryItemsUpdated();
}

void ClipboardHistoryAsh::BindReceiver(
    mojo::PendingReceiver<mojom::ClipboardHistory> pending_receiver) {
  receivers_.Add(this, std::move(pending_receiver));
}

void ClipboardHistoryAsh::FlushForTesting() {
  remote_client_.FlushForTesting();  // IN-TEST
}

void ClipboardHistoryAsh::OnShellDestroying() {
  shell_observation_.Reset();
  ClearRemoteConnection();
}

void ClipboardHistoryAsh::OnRemoteDisconnected() {
  ClearRemoteConnection();
}

void ClipboardHistoryAsh::ClearRemoteConnection() {
  client_updater_.reset();
  remote_client_.reset();
}

}  // namespace crosapi