[go: nahoru, domu]

blob: 66aedd6cb6dfc573847e60c91ffb0302bdcbb841 [file] [log] [blame]
// 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 "ash/clipboard/clipboard_history_resource_manager.h"
#include <string>
#include "ash/clipboard/clipboard_history_item.h"
#include "ash/display/display_util.h"
#include "ash/public/cpp/clipboard_image_model_factory.h"
#include "ash/public/cpp/window_tree_host_lookup.h"
#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/ranges/algorithm.h"
#include "chromeos/crosapi/mojom/clipboard_history.mojom.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/ime/input_method.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/display/screen.h"
namespace ash {
// ClipboardHistoryResourceManager ---------------------------------------------
ClipboardHistoryResourceManager::ClipboardHistoryResourceManager(
ClipboardHistory* clipboard_history)
: clipboard_history_(clipboard_history) {
clipboard_history_->AddObserver(this);
}
ClipboardHistoryResourceManager::~ClipboardHistoryResourceManager() {
clipboard_history_->RemoveObserver(this);
if (ClipboardImageModelFactory::Get())
ClipboardImageModelFactory::Get()->OnShutdown();
}
void ClipboardHistoryResourceManager::AddObserver(Observer* observer) const {
observers_.AddObserver(observer);
}
void ClipboardHistoryResourceManager::RemoveObserver(Observer* observer) const {
observers_.RemoveObserver(observer);
}
ClipboardHistoryResourceManager::ImageModelRequest::ImageModelRequest() =
default;
ClipboardHistoryResourceManager::ImageModelRequest::ImageModelRequest(
const ImageModelRequest& other) = default;
ClipboardHistoryResourceManager::ImageModelRequest&
ClipboardHistoryResourceManager::ImageModelRequest::operator=(
const ImageModelRequest&) = default;
ClipboardHistoryResourceManager::ImageModelRequest::~ImageModelRequest() =
default;
void ClipboardHistoryResourceManager::OnImageModelRendered(
const base::UnguessableToken& id,
ui::ImageModel image_model) {
auto image_model_request = base::ranges::find(
image_model_requests_, id,
&ClipboardHistoryResourceManager::ImageModelRequest::id);
if (image_model_request == image_model_requests_.end()) {
return;
}
// Set the HTML preview for each item attached to `id`'s request.
for (auto& item : clipboard_history_->GetItems()) {
if (!base::Contains(image_model_request->clipboard_history_item_ids,
item.id())) {
continue;
}
DCHECK(item.display_image().has_value());
if (item.display_image().value() != image_model) {
item.SetDisplayImage(image_model);
}
}
for (auto& observer : observers_) {
observer.OnCachedImageModelUpdated(
image_model_request->clipboard_history_item_ids);
}
image_model_requests_.erase(image_model_request);
}
void ClipboardHistoryResourceManager::CancelUnfinishedRequests() {
for (const auto& image_model_request : image_model_requests_) {
ClipboardImageModelFactory::Get()->CancelRequest(image_model_request.id);
}
}
std::vector<ClipboardHistoryResourceManager::ImageModelRequest>::iterator
ClipboardHistoryResourceManager::GetImageModelRequestForItem(
const ClipboardHistoryItem& item) {
return base::ranges::find_if(
image_model_requests_, [&](const auto& image_model_request) {
return base::Contains(image_model_request.clipboard_history_item_ids,
item.id());
});
}
void ClipboardHistoryResourceManager::OnClipboardHistoryItemAdded(
const ClipboardHistoryItem& item,
bool is_duplicate) {
// If this item is a duplicate then there is no new item to render.
if (is_duplicate)
return;
// For items that will be represented by their rendered HTML, we need to do
// some prep work to pre-render and cache an image model.
if (item.display_format() !=
crosapi::mojom::ClipboardHistoryDisplayFormat::kHtml) {
return;
}
auto& items = clipboard_history_->GetItems();
// See if we have an `existing` item that will render the same as `item`.
auto it = base::ranges::find_if(items, [&](const auto& existing) {
return &existing != &item &&
existing.display_format() ==
crosapi::mojom::ClipboardHistoryDisplayFormat::kHtml &&
existing.data().markup_data() == item.data().markup_data();
});
// If no existing item will render the same as `item`, create a new request to
// render an HTML preview for `item`. Note that the image model factory may
// or may not start rendering immediately depending on its activation status.
if (it == items.end()) {
base::UnguessableToken id = base::UnguessableToken::Create();
ImageModelRequest image_model_request;
image_model_request.id = id;
image_model_request.clipboard_history_item_ids.push_back(item.id());
image_model_requests_.push_back(std::move(image_model_request));
// `image_model_factory` can be nullptr in tests.
auto* image_model_factory = ClipboardImageModelFactory::Get();
if (!image_model_factory) {
return;
}
// `text_input_client` can be nullptr in tests.
const auto* text_input_client =
ash::GetWindowTreeHostForDisplay(
display::Screen::GetScreen()->GetPrimaryDisplay().id())
->GetInputMethod()
->GetTextInputClient();
const gfx::Rect bounding_box =
text_input_client ? text_input_client->GetSelectionBoundingBox()
: gfx::Rect();
image_model_factory->Render(
id, item.data().markup_data(),
IsRectContainedByAnyDisplay(bounding_box) ? bounding_box.size()
: gfx::Size(),
base::BindOnce(&ClipboardHistoryResourceManager::OnImageModelRendered,
weak_factory_.GetWeakPtr(), id));
return;
}
// If there is an existing item that will render the same as `item`, check
// whether the existing item's preview has rendered.
auto image_model_request = GetImageModelRequestForItem(*it);
if (image_model_request != image_model_requests_.end()) {
// If rendering is still in progress, just note that `item` will need to
// hear about the result as well.
image_model_request->clipboard_history_item_ids.push_back(item.id());
} else {
// If rendering has finished, set `item` to have the same preview.
auto mutable_item =
base::ranges::find(items, item.id(), &ClipboardHistoryItem::id);
DCHECK(mutable_item != items.end());
const auto& existing_preview = it->display_image();
DCHECK(existing_preview.has_value());
mutable_item->SetDisplayImage(existing_preview.value());
}
}
void ClipboardHistoryResourceManager::OnClipboardHistoryItemRemoved(
const ClipboardHistoryItem& item) {
// For items that will not be represented by their rendered HTML, do nothing.
if (item.display_format() !=
crosapi::mojom::ClipboardHistoryDisplayFormat::kHtml) {
return;
}
// If the item's image model request has already finished, there is nothing
// more to do.
auto image_model_request = GetImageModelRequestForItem(item);
if (image_model_request == image_model_requests_.end()) {
return;
}
// If `item` was attached to a pending request, make sure it is not updated
// when rendering finishes.
base::Erase(image_model_request->clipboard_history_item_ids, item.id());
if (image_model_request->clipboard_history_item_ids.empty()) {
// If no more items are waiting on the image model, cancel the request.
ClipboardImageModelFactory::Get()->CancelRequest(image_model_request->id);
image_model_requests_.erase(image_model_request);
}
}
void ClipboardHistoryResourceManager::OnClipboardHistoryCleared() {
CancelUnfinishedRequests();
image_model_requests_.clear();
}
} // namespace ash