Commit f58dffe3 authored by Alex Newcomer's avatar Alex Newcomer Committed by Commit Bot

cros: Rendering HTML for ClipboardHistory initial CL

see go/render-html-multipaste for the design doc.

This is the first CL to render HTML in multipaste.

New objects:
ClipboardHistoryResourceManager
[stub]ClipboardHistoryImageModelFactory/Impl
 - The next CL will include implementation of FactoryImpl.

Changes:
 - ClipboardHistory now has observers which are notified on
   things added/removed from history.
 - ClipboardData is wrapped by ClipboardHistoryItem
   - This has an ID to identify different ClipboardHistoryItems
     quickly.
 - ClipboardHistoryHelper -> ClipboardHistoryResourceManager.
   - CHResourceManager serves as a cache for ImageModels which will
     be rendered by ClipboardImageModelFactory.

Bug: 1117230
Change-Id: Ia73d284eb3481525bc04bb04622801eda3c7a8d8
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2363023
Commit-Queue: Alex Newcomer <newcomer@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarDavid Black <dmblack@google.com>
Cr-Commit-Position: refs/heads/master@{#800337}
parent 9c284879
......@@ -276,10 +276,12 @@ component("ash") {
"clipboard/clipboard_history.h",
"clipboard/clipboard_history_controller.cc",
"clipboard/clipboard_history_controller.h",
"clipboard/clipboard_history_helper.cc",
"clipboard/clipboard_history_helper.h",
"clipboard/clipboard_history_item.cc",
"clipboard/clipboard_history_item.h",
"clipboard/clipboard_history_menu_model_adapter.cc",
"clipboard/clipboard_history_menu_model_adapter.h",
"clipboard/clipboard_history_resource_manager.cc",
"clipboard/clipboard_history_resource_manager.h",
"dbus/ash_dbus_services.cc",
"dbus/ash_dbus_services.h",
"dbus/display_service_provider.cc",
......@@ -1816,7 +1818,7 @@ test("ash_unittests") {
"autoclick/autoclick_drag_event_rewriter_unittest.cc",
"autoclick/autoclick_unittest.cc",
"capture_mode/capture_mode_unittests.cc",
"clipboard/clipboard_history_helper_unittest.cc",
"clipboard/clipboard_history_resource_manager_unittest.cc",
"clipboard/clipboard_history_unittest.cc",
"dbus/gesture_properties_service_provider_unittest.cc",
"dbus/url_handler_service_provider_unittest.cc",
......
......@@ -4,7 +4,6 @@
#include "ash/clipboard/clipboard_history.h"
#include "ash/clipboard/clipboard_history_controller.h"
#include "base/stl_util.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "ui/base/clipboard/clipboard_monitor.h"
......@@ -35,12 +34,22 @@ ClipboardHistory::~ClipboardHistory() {
ui::ClipboardMonitor::GetInstance()->RemoveObserver(this);
}
const std::list<ui::ClipboardData>& ClipboardHistory::GetItems() const {
void ClipboardHistory::AddObserver(Observer* observer) const {
observers_.AddObserver(observer);
}
void ClipboardHistory::RemoveObserver(Observer* observer) const {
observers_.RemoveObserver(observer);
}
const std::list<ClipboardHistoryItem>& ClipboardHistory::GetItems() const {
return history_list_;
}
void ClipboardHistory::Clear() {
history_list_ = std::list<ui::ClipboardData>();
history_list_ = std::list<ClipboardHistoryItem>();
for (auto& observer : observers_)
observer.OnClipboardHistoryCleared();
}
bool ClipboardHistory::IsEmpty() const {
......@@ -75,9 +84,16 @@ void ClipboardHistory::OnClipboardDataChanged() {
}
void ClipboardHistory::CommitData(ui::ClipboardData data) {
history_list_.push_front(std::move(data));
if (history_list_.size() > kMaxClipboardItemsShared)
history_list_.emplace_front(std::move(data));
for (auto& observer : observers_)
observer.OnClipboardHistoryItemAdded(history_list_.front());
if (history_list_.size() > kMaxClipboardItemsShared) {
auto removed = std::move(history_list_.back());
history_list_.pop_back();
for (auto& observer : observers_)
observer.OnClipboardHistoryItemRemoved(removed);
}
}
void ClipboardHistory::Pause() {
......
......@@ -9,20 +9,30 @@
#include <map>
#include "ash/ash_export.h"
#include "ash/clipboard/clipboard_history_item.h"
#include "base/component_export.h"
#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/clipboard/clipboard_observer.h"
namespace ui {
class ClipboardData;
} // namespace ui
namespace ash {
// Keeps track of the last few things saved in the clipboard.
class ASH_EXPORT ClipboardHistory : public ui::ClipboardObserver {
public:
class ASH_EXPORT Observer : public base::CheckedObserver {
public:
// Called when a ClipboardHistoryItem has been added.
virtual void OnClipboardHistoryItemAdded(const ClipboardHistoryItem& item) {
}
// Called when a ClipboardHistoryItem has been removed.
virtual void OnClipboardHistoryItemRemoved(
const ClipboardHistoryItem& item) {}
// Called when ClipboardHistory is Clear()-ed.
virtual void OnClipboardHistoryCleared() {}
};
// Prevents clipboard history from being recorded within its scope. If
// anything is copied within its scope, history will not be recorded.
class ASH_EXPORT ScopedPause {
......@@ -41,9 +51,12 @@ class ASH_EXPORT ClipboardHistory : public ui::ClipboardObserver {
ClipboardHistory& operator=(const ClipboardHistory&) = delete;
~ClipboardHistory() override;
void AddObserver(Observer* observer) const;
void RemoveObserver(Observer* observer) const;
// Returns the list of most recent items. The returned list is sorted by
// recency.
const std::list<ui::ClipboardData>& GetItems() const;
const std::list<ClipboardHistoryItem>& GetItems() const;
// Deletes clipboard history. Does not modify content stored in the clipboard.
void Clear();
......@@ -66,7 +79,11 @@ class ASH_EXPORT ClipboardHistory : public ui::ClipboardObserver {
// The history of data copied to the Clipboard. Items of the list are sorted
// by recency.
std::list<ui::ClipboardData> history_list_;
std::list<ClipboardHistoryItem> history_list_;
// Mutable to allow adding/removing from |observers_| through a const
// ClipboardHistory.
mutable base::ObserverList<Observer> observers_;
// Factory to create WeakPtrs used to debounce calls to CommitData().
base::WeakPtrFactory<ClipboardHistory> commit_data_weak_factory_{this};
......
......@@ -6,8 +6,8 @@
#include "ash/accelerators/accelerator_controller_impl.h"
#include "ash/clipboard/clipboard_history.h"
#include "ash/clipboard/clipboard_history_helper.h"
#include "ash/clipboard/clipboard_history_menu_model_adapter.h"
#include "ash/clipboard/clipboard_history_resource_manager.h"
#include "ash/public/cpp/window_tree_host_lookup.h"
#include "ash/resources/vector_icons/vector_icons.h"
#include "ash/shell.h"
......@@ -37,32 +37,6 @@ namespace ash {
namespace {
// TODO(dmblack): Move to clipboard_history_helper.
ui::ImageModel GetImageModelForClipboardData(const ui::ClipboardData& item) {
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kBitmap)) {
// TODO(newcomer): Show a smaller version of the bitmap.
return ui::ImageModel();
}
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kWeb))
return ui::ImageModel::FromVectorIcon(ash::kWebSmartPasteIcon);
if (item.format() &
static_cast<int>(ui::ClipboardInternalFormat::kBookmark)) {
return ui::ImageModel::FromVectorIcon(ash::kWebBookmarkIcon);
}
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kHtml))
return ui::ImageModel::FromVectorIcon(ash::kHtmlIcon);
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kRtf))
return ui::ImageModel::FromVectorIcon(ash::kRtfIcon);
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kText))
return ui::ImageModel::FromVectorIcon(ash::kTextIcon);
if (item.format() & static_cast<int>(ui::ClipboardInternalFormat::kCustom)) {
// TODO(crbug/1108901): Handle file manager case.
// TODO(crbug/1108902): Handle fallback case.
return ui::ImageModel();
}
return ui::ImageModel();
}
ui::ClipboardNonBacked* GetClipboard() {
auto* clipboard = ui::ClipboardNonBacked::GetForCurrentThread();
DCHECK(clipboard);
......@@ -133,6 +107,8 @@ class ClipboardHistoryController::MenuDelegate
ClipboardHistoryController::ClipboardHistoryController()
: clipboard_history_(std::make_unique<ClipboardHistory>()),
resource_manager_(std::make_unique<ClipboardHistoryResourceManager>(
clipboard_history_.get())),
accelerator_target_(std::make_unique<AcceleratorTarget>(this)),
menu_delegate_(std::make_unique<MenuDelegate>(this)) {}
......@@ -171,8 +147,8 @@ void ClipboardHistoryController::ShowMenu() {
return;
clipboard_items_ =
std::vector<ui::ClipboardData>(clipboard_history_->GetItems().begin(),
clipboard_history_->GetItems().end());
std::vector<ClipboardHistoryItem>(clipboard_history_->GetItems().begin(),
clipboard_history_->GetItems().end());
std::unique_ptr<ui::SimpleMenuModel> menu_model =
std::make_unique<ui::SimpleMenuModel>(menu_delegate_.get());
......@@ -181,8 +157,8 @@ void ClipboardHistoryController::ShowMenu() {
IDS_CLIPBOARD_MENU_CLIPBOARD));
int index = 0;
for (const auto& item : clipboard_items_) {
menu_model->AddItemWithIcon(index++, clipboard::helper::GetLabel(item),
GetImageModelForClipboardData(item));
menu_model->AddItemWithIcon(index++, resource_manager_->GetLabel(item),
resource_manager_->GetImageModel(item));
}
menu_model->AddSeparator(ui::MenuSeparatorType::NORMAL_SEPARATOR);
menu_model->AddItemWithIcon(
......@@ -213,14 +189,14 @@ void ClipboardHistoryController::MenuOptionSelected(int index,
// If necessary, replace the clipboard's |original_data| temporarily so that
// we can paste the selected history item.
const bool shift_key_pressed = event_flags & ui::EF_SHIFT_DOWN;
if (shift_key_pressed || *it != *clipboard->GetClipboardData()) {
if (shift_key_pressed || it->data() != *clipboard->GetClipboardData()) {
std::unique_ptr<ui::ClipboardData> temp_data;
if (shift_key_pressed) {
// When the shift key is pressed, we only paste plain text.
temp_data = std::make_unique<ui::ClipboardData>();
temp_data->set_text(it->text());
temp_data->set_text(it->data().text());
} else {
temp_data = std::make_unique<ui::ClipboardData>(*it);
temp_data = std::make_unique<ui::ClipboardData>(it->data());
}
// Pause clipboard history when manipulating the clipboard for a paste.
ClipboardHistory::ScopedPause scoped_pause(clipboard_history_.get());
......
......@@ -9,20 +9,18 @@
#include <vector>
#include "ash/ash_export.h"
#include "ash/clipboard/clipboard_history_item.h"
#include "base/memory/weak_ptr.h"
namespace gfx {
class Rect;
} // namespace gfx
namespace ui {
class ClipboardData;
} // namespace ui
namespace ash {
class ClipboardHistory;
class ClipboardHistoryMenuModelAdapter;
class ClipboardHistoryResourceManager;
// Shows a menu with the last few things saved in the clipboard when the
// keyboard shortcut is pressed.
......@@ -45,6 +43,12 @@ class ASH_EXPORT ClipboardHistoryController {
// Returns the history which tracks what is being copied to the clipboard.
const ClipboardHistory* history() const { return clipboard_history_.get(); }
// Returns the resource manager which gets labels and images for items copied
// to the clipboard.
const ClipboardHistoryResourceManager* resource_manager() const {
return resource_manager_.get();
}
private:
class AcceleratorTarget;
class MenuDelegate;
......@@ -60,12 +64,14 @@ class ASH_EXPORT ClipboardHistoryController {
std::unique_ptr<ClipboardHistoryMenuModelAdapter> context_menu_;
// Used to keep track of what is being copied to the clipboard.
std::unique_ptr<ClipboardHistory> clipboard_history_;
// Manages resources for clipboard history.
std::unique_ptr<ClipboardHistoryResourceManager> resource_manager_;
// Detects the search+v key combo.
std::unique_ptr<AcceleratorTarget> accelerator_target_;
// Handles events on the contextual menu.
std::unique_ptr<MenuDelegate> menu_delegate_;
// The items we show in the contextual menu. Saved so we can paste them later.
std::vector<ui::ClipboardData> clipboard_items_;
std::vector<ClipboardHistoryItem> clipboard_items_;
base::WeakPtrFactory<ClipboardHistoryController> weak_ptr_factory_{this};
};
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_helper.h"
#include <string>
#include <vector>
#include "base/notreached.h"
#include "base/strings/escape.h"
#include "base/strings/string_split.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/base/resource/resource_bundle.h"
#include "ui/strings/grit/ui_strings.h"
namespace ash {
namespace clipboard {
namespace helper {
namespace {
constexpr char kFileSystemSourcesType[] = "fs/sources";
// Private ---------------------------------------------------------------------
// Returns true if |data| contains the specified |format|.
bool ContainsFormat(const ui::ClipboardData& data,
ui::ClipboardInternalFormat format) {
return data.format() & static_cast<int>(format);
}
// Returns the localized string for the specified |resource_id|.
base::string16 GetLocalizedString(int resource_id) {
return ui::ResourceBundle::GetSharedInstance().GetLocalizedString(
resource_id);
}
// TODO(crbug/1108902): Handle fallback case.
// Returns the label to display for the custom data contained within |data|.
base::string16 GetLabelForCustomData(const ui::ClipboardData& data) {
DCHECK(ContainsFormat(data, ui::ClipboardInternalFormat::kCustom));
// Attempt to read file system sources in the custom data.
base::string16 sources;
ui::ReadCustomDataForType(
data.custom_data_data().c_str(), data.custom_data_data().size(),
base::UTF8ToUTF16(kFileSystemSourcesType), &sources);
if (sources.empty())
return base::UTF8ToUTF16("<Custom Data>");
// Split sources into a list.
std::vector<base::StringPiece16> source_list =
base::SplitStringPiece(sources, base::UTF8ToUTF16("\n"),
base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY);
// Strip path information, so all that's left are file names.
for (auto it = source_list.begin(); it != source_list.end(); ++it)
*it = it->substr(it->find_last_of(base::UTF8ToUTF16("/")) + 1);
// Join file names, unescaping encoded character sequences for display. This
// ensures that "My%20File.txt" will display as "My File.txt".
return base::UTF8ToUTF16(base::UnescapeURLComponent(
base::UTF16ToUTF8(base::JoinString(source_list, base::UTF8ToUTF16(", "))),
base::UnescapeRule::SPACES));
}
} // namespace
// Public ----------------------------------------------------------------------
base::string16 GetLabel(const ui::ClipboardData& data) {
if (ContainsFormat(data, ui::ClipboardInternalFormat::kBitmap))
return GetLocalizedString(IDS_CLIPBOARD_MENU_IMAGE);
if (ContainsFormat(data, ui::ClipboardInternalFormat::kText))
return base::UTF8ToUTF16(data.text());
if (ContainsFormat(data, ui::ClipboardInternalFormat::kHtml))
return base::UTF8ToUTF16(data.markup_data());
if (ContainsFormat(data, ui::ClipboardInternalFormat::kRtf))
return GetLocalizedString(IDS_CLIPBOARD_MENU_RTF_CONTENT);
if (ContainsFormat(data, ui::ClipboardInternalFormat::kBookmark))
return base::UTF8ToUTF16(data.bookmark_title());
if (ContainsFormat(data, ui::ClipboardInternalFormat::kWeb))
return GetLocalizedString(IDS_CLIPBOARD_MENU_WEB_SMART_PASTE);
if (ContainsFormat(data, ui::ClipboardInternalFormat::kCustom))
return GetLabelForCustomData(data);
NOTREACHED();
return base::string16();
}
} // namespace helper
} // namespace clipboard
} // namespace ash
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_CLIPBOARD_CLIPBOARD_HISTORY_HELPER_H_
#define ASH_CLIPBOARD_CLIPBOARD_HISTORY_HELPER_H_
#include "ash/ash_export.h"
#include "base/strings/string16.h"
namespace ui {
class ClipboardData;
} // namespace ui
namespace ash {
namespace clipboard {
namespace helper {
// Returns the label to display for the specified clipboard |data|.
ASH_EXPORT base::string16 GetLabel(const ui::ClipboardData& data);
} // namespace helper
} // namespace clipboard
} // namespace ash
#endif // ASH_CLIPBOARD_CLIPBOARD_HISTORY_HELPER_H_
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_helper.h"
#include <string>
#include <unordered_map>
#include "base/optional.h"
#include "base/pickle.h"
#include "base/strings/string_util.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/icu_test_util.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "third_party/skia/include/core/SkBitmap.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/clipboard/clipboard_format_type.h"
#include "ui/base/clipboard/custom_data_helper.h"
#include "ui/gfx/image/image_unittest_util.h"
namespace ash {
namespace clipboard {
namespace helper {
namespace {
// ClipboardDataBuilder --------------------------------------------------------
class ClipboardDataBuilder {
public:
ClipboardDataBuilder() = default;
ClipboardDataBuilder(const ClipboardDataBuilder&) = delete;
ClipboardDataBuilder& operator=(const ClipboardDataBuilder&) = delete;
~ClipboardDataBuilder() = default;
ui::ClipboardData Build() const {
ui::ClipboardData data;
if (text_.has_value())
data.set_text(text_.value());
if (markup_.has_value())
data.set_markup_data(markup_.value());
if (rtf_.has_value())
data.SetRTFData(rtf_.value());
if (bookmark_title_.has_value())
data.set_bookmark_title(bookmark_title_.value());
if (bitmap_.has_value())
data.SetBitmapData(bitmap_.value());
if (custom_format_.has_value() && custom_data_.has_value())
data.SetCustomData(custom_format_.value(), custom_data_.value());
if (web_smart_paste_.has_value())
data.set_web_smart_paste(web_smart_paste_.value());
return data;
}
ClipboardDataBuilder& SetText(const std::string& text) {
text_ = text;
return *this;
}
ClipboardDataBuilder& ClearText() {
text_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetMarkup(const std::string& markup) {
markup_ = markup;
return *this;
}
ClipboardDataBuilder& ClearMarkup() {
markup_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetRtf(const std::string& rtf) {
rtf_ = rtf;
return *this;
}
ClipboardDataBuilder& ClearRtf() {
rtf_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetBookmarkTitle(const std::string& bookmark_title) {
bookmark_title_ = bookmark_title;
return *this;
}
ClipboardDataBuilder& ClearBookmarkTitle() {
bookmark_title_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetBitmap(const SkBitmap& bitmap) {
bitmap_ = bitmap;
return *this;
}
ClipboardDataBuilder& ClearBitmap() {
bitmap_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetCustomData(const std::string& custom_format,
const std::string& custom_data) {
custom_format_ = custom_format;
custom_data_ = custom_data;
return *this;
}
ClipboardDataBuilder& ClearCustomData() {
custom_format_ = base::nullopt;
custom_data_ = base::nullopt;
return *this;
}
ClipboardDataBuilder& SetFileSystemData(
std::initializer_list<std::string>&& source_list) {
constexpr char kFileSystemSourcesType[] = "fs/sources";
base::Pickle custom_data;
ui::WriteCustomDataToPickle(
std::unordered_map<base::string16, base::string16>(
{{base::UTF8ToUTF16(kFileSystemSourcesType),
base::UTF8ToUTF16(base::JoinString(source_list, "\n"))}}),
&custom_data);
return SetCustomData(
ui::ClipboardFormatType::GetWebCustomDataType().GetName(),
std::string(static_cast<const char*>(custom_data.data()),
custom_data.size()));
}
ClipboardDataBuilder& SetWebSmartPaste(bool web_smart_paste) {
web_smart_paste_ = web_smart_paste;
return *this;
}
ClipboardDataBuilder& ClearWebSmartPaste() {
web_smart_paste_ = base::nullopt;
return *this;
}
private:
base::Optional<std::string> text_;
base::Optional<std::string> markup_;
base::Optional<std::string> rtf_;
base::Optional<std::string> bookmark_title_;
base::Optional<SkBitmap> bitmap_;
base::Optional<std::string> custom_format_;
base::Optional<std::string> custom_data_;
base::Optional<bool> web_smart_paste_;
};
} // namespace
// Tests -----------------------------------------------------------------------
using ClipboardHistoryHelperTest = testing::Test;
TEST_F(ClipboardHistoryHelperTest, GetLabel) {
base::test::ScopedRestoreICUDefaultLocale locale("en_US");
// Populate a builder with all the data formats that we expect to handle.
ClipboardDataBuilder builder;
builder.SetText("Text")
.SetMarkup("Markup")
.SetRtf("Rtf")
.SetBookmarkTitle("Bookmark Title")
.SetBitmap(gfx::test::CreateBitmap(10, 10))
.SetCustomData("Custom Format", "Custom Data")
.SetWebSmartPaste(true);
// Bitmap data always take precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("Image"));
builder.ClearBitmap();
// In the absence of bitmap data, text data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("Text"));
builder.ClearText();
// In the absence of text data, HTML data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("Markup"));
builder.ClearMarkup();
// In the absence of HTML data, RTF data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("RTF Content"));
builder.ClearRtf();
// In the absence of RTF data, bookmark data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("Bookmark Title"));
builder.ClearBookmarkTitle();
// In the absence of bookmark data, web smart paste data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()),
base::UTF8ToUTF16("Web Smart Paste Content"));
builder.ClearWebSmartPaste();
// In the absence of web smart paste data, custom data takes precedence.
EXPECT_EQ(GetLabel(builder.Build()), base::UTF8ToUTF16("<Custom Data>"));
builder.SetFileSystemData(
{"/path/to/My%20File.txt", "/path/to/My%20Other%20File.txt"});
// We specially treat custom file system data to show a list of file names.
EXPECT_EQ(GetLabel(builder.Build()),
base::UTF8ToUTF16("My File.txt, My Other File.txt"));
}
} // namespace helper
} // namespace clipboard
} // namespace ash
// Copyright 2020 The Chromium Authors. All rights reserved.
// 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_item.h"
namespace ash {
ClipboardHistoryItem::ClipboardHistoryItem(ui::ClipboardData data)
: id_(base::UnguessableToken::Create()), data_(std::move(data)) {}
ClipboardHistoryItem::ClipboardHistoryItem(const ClipboardHistoryItem&) =
default;
ClipboardHistoryItem::ClipboardHistoryItem(ClipboardHistoryItem&&) = default;
ClipboardHistoryItem::~ClipboardHistoryItem() = default;
} // namespace ash
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_CLIPBOARD_CLIPBOARD_HISTORY_ITEM_H_
#define ASH_CLIPBOARD_CLIPBOARD_HISTORY_ITEM_H_
#include "ash/ash_export.h"
#include "base/unguessable_token.h"
#include "ui/base/clipboard/clipboard_data.h"
namespace ash {
// Wraps ClipboardData with a unique identifier.
class ASH_EXPORT ClipboardHistoryItem {
public:
explicit ClipboardHistoryItem(ui::ClipboardData data);
ClipboardHistoryItem(const ClipboardHistoryItem&);
ClipboardHistoryItem(ClipboardHistoryItem&&);
// Copy/move assignment operators are deleted to be consistent with
// ui::ClipboardData and ui::ClipboardDataEndpoint.
ClipboardHistoryItem& operator=(const ClipboardHistoryItem&) = delete;
ClipboardHistoryItem& operator=(ClipboardHistoryItem&&) = delete;
~ClipboardHistoryItem();
const base::UnguessableToken& id() const { return id_; }
const ui::ClipboardData& data() const { return data_; }
private:
// Unique identifier.
base::UnguessableToken id_;
ui::ClipboardData data_;
};
} // namespace ash
#endif // ASH_CLIPBOARD_CLIPBOARD_HISTORY_ITEM_H_
This diff is collapsed.
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_CLIPBOARD_CLIPBOARD_HISTORY_RESOURCE_MANAGER_H_
#define ASH_CLIPBOARD_CLIPBOARD_HISTORY_RESOURCE_MANAGER_H_
#include <vector>
#include "ash/ash_export.h"
#include "ash/clipboard/clipboard_history.h"
#include "ash/clipboard/clipboard_history_item.h"
#include "base/strings/string16.h"
#include "base/unguessable_token.h"
#include "ui/base/models/image_model.h"
namespace ash {
class ASH_EXPORT ClipboardHistoryResourceManager
: public ClipboardHistory::Observer {
public:
explicit ClipboardHistoryResourceManager(
const ClipboardHistory* clipboard_history);
ClipboardHistoryResourceManager(const ClipboardHistoryResourceManager&) =
delete;
ClipboardHistoryResourceManager& operator=(
const ClipboardHistoryResourceManager&) = delete;
~ClipboardHistoryResourceManager() override;
// Returns the image to display for the specified clipboard history |item|.
ui::ImageModel GetImageModel(const ClipboardHistoryItem& item) const;
// Returns the label to display for the specified clipboard history |item|.
base::string16 GetLabel(const ClipboardHistoryItem& item) const;
private:
struct CachedImageModel {
CachedImageModel();
CachedImageModel(const CachedImageModel&);
CachedImageModel& operator=(const CachedImageModel&);
~CachedImageModel();
// Unique identifier.
base::UnguessableToken id;
// ImageModel that was created by ClipboardImageModelFactory.
ui::ImageModel image_model;
// ClipboardHistoryItem id's which utilize this CachedImageModel.
std::vector<base::UnguessableToken> clipboard_history_item_ids;
};
// Caches the specified |image_model| with the specified |id|.
void CacheImageModel(const base::UnguessableToken& id,
ui::ImageModel image_model);
// Finds the cached image model associated with the specified |id|.
std::vector<ClipboardHistoryResourceManager::CachedImageModel>::const_iterator
FindCachedImageModelForId(const base::UnguessableToken& id) const;
// Finds the cached image model associated with the specified |item|.
std::vector<ClipboardHistoryResourceManager::CachedImageModel>::const_iterator
FindCachedImageModelForItem(const ClipboardHistoryItem& item) const;
// Cancels all unfinished requests.
void CancelUnfinishedRequests();
// ClipboardHistory::Observer:
void OnClipboardHistoryItemAdded(const ClipboardHistoryItem& item) override;
void OnClipboardHistoryItemRemoved(const ClipboardHistoryItem& item) override;
void OnClipboardHistoryCleared() override;
// Owned by ClipboardHistoryController.
const ClipboardHistory* const clipboard_history_;
std::vector<CachedImageModel> cached_image_models_;
base::WeakPtrFactory<ClipboardHistoryResourceManager> weak_factory_{this};
};
} // namespace ash
#endif // ASH_CLIPBOARD_CLIPBOARD_HISTORY_RESOURCE_MANAGER_H_
This diff is collapsed.
......@@ -8,6 +8,7 @@
#include <unordered_map>
#include "ash/clipboard/clipboard_history_controller.h"
#include "ash/clipboard/clipboard_history_item.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/strings/utf_string_conversions.h"
......@@ -37,7 +38,7 @@ class ClipboardHistoryTest : public AshTestBase {
Shell::Get()->clipboard_history_controller()->history());
}
const std::list<ui::ClipboardData>& GetClipboardHistoryData() {
const std::list<ClipboardHistoryItem>& GetClipboardHistoryItems() {
return clipboard_history_->GetItems();
}
......@@ -62,13 +63,13 @@ class ClipboardHistoryTest : public AshTestBase {
}
void EnsureTextHistory(const std::vector<base::string16>& expected_strings) {
const std::list<ui::ClipboardData>& datas = GetClipboardHistoryData();
EXPECT_EQ(expected_strings.size(), datas.size());
const std::list<ClipboardHistoryItem>& items = GetClipboardHistoryItems();
EXPECT_EQ(expected_strings.size(), items.size());
int expected_strings_index = 0;
for (auto& data : datas) {
for (const auto& item : items) {
EXPECT_EQ(expected_strings[expected_strings_index++],
base::UTF8ToUTF16(data.text()));
base::UTF8ToUTF16(item.data().text()));
}
}
......@@ -84,13 +85,13 @@ class ClipboardHistoryTest : public AshTestBase {
}
base::RunLoop().RunUntilIdle();
}
const std::list<ui::ClipboardData>& datas = GetClipboardHistoryData();
EXPECT_EQ(expected_bitmaps.size(), datas.size());
const std::list<ClipboardHistoryItem>& items = GetClipboardHistoryItems();
EXPECT_EQ(expected_bitmaps.size(), items.size());
int expected_bitmaps_index = 0;
for (auto& data : datas) {
for (const auto& item : items) {
EXPECT_TRUE(gfx::BitmapsAreEqual(
expected_bitmaps[expected_bitmaps_index++], data.bitmap()));
expected_bitmaps[expected_bitmaps_index++], item.data().bitmap()));
}
}
......@@ -110,12 +111,12 @@ class ClipboardHistoryTest : public AshTestBase {
}
base::RunLoop().RunUntilIdle();
const std::list<ui::ClipboardData> datas = GetClipboardHistoryData();
EXPECT_EQ(1u, datas.size());
const std::list<ClipboardHistoryItem> items = GetClipboardHistoryItems();
EXPECT_EQ(1u, items.size());
std::unordered_map<base::string16, base::string16> actual_data;
ui::ReadCustomDataIntoMap(datas.front().custom_data_data().c_str(),
datas.front().custom_data_data().size(),
ui::ReadCustomDataIntoMap(items.front().data().custom_data_data().c_str(),
items.front().data().custom_data_data().size(),
&actual_data);
EXPECT_EQ(expected_data, actual_data);
......
......@@ -112,6 +112,8 @@ component("cpp") {
"caption_buttons/snap_controller.h",
"cast_config_controller.cc",
"cast_config_controller.h",
"clipboard_image_model_factory.cc",
"clipboard_image_model_factory.h",
"default_frame_header.cc",
"default_frame_header.h",
"default_scale_factor_retriever.cc",
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "ash/public/cpp/clipboard_image_model_factory.h"
namespace ash {
namespace {
ClipboardImageModelFactory* g_instance = nullptr;
} // namespace
ClipboardImageModelFactory::ClipboardImageModelFactory() {
DCHECK_EQ(nullptr, g_instance);
g_instance = this;
}
ClipboardImageModelFactory::~ClipboardImageModelFactory() {
DCHECK_EQ(g_instance, this);
g_instance = nullptr;
}
// static
ClipboardImageModelFactory* ClipboardImageModelFactory::Get() {
return g_instance;
}
} // namespace ash
\ No newline at end of file
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef ASH_PUBLIC_CPP_CLIPBOARD_IMAGE_MODEL_FACTORY_H_
#define ASH_PUBLIC_CPP_CLIPBOARD_IMAGE_MODEL_FACTORY_H_
#include <string>
#include "ash/public/cpp/ash_public_export.h"
#include "base/callback.h"
#include "base/unguessable_token.h"
#include "ui/base/models/image_model.h"
namespace ui {
class ImageModel;
} // namespace ui
namespace ash {
// A factory implemented in the Browser with the primary profile which is
// responsible for creating ui::ImageModels out of html strings from
// ClipboardHistory to work around dependency restrictions in ash. This will not
// create ImageModels until it is activated.
class ASH_PUBLIC_EXPORT ClipboardImageModelFactory {
public:
// Returns the singleton factory instance.
static ClipboardImageModelFactory* Get();
using ImageModelCallback = base::OnceCallback<void(ui::ImageModel)>;
// Asynchronously renders |html_markup|, identified by |id|. Later the
// rendered html will be returned via |callback|, which is called as long as
// the request is not canceled via CancelRequest. If the request times out, an
// empty ImageModel will be passed through |callback|.
virtual void Render(const base::UnguessableToken& id,
const std::string& html_markup,
ImageModelCallback callback) = 0;
// Called to stop rendering which was requested with |id|.
virtual void CancelRequest(const base::UnguessableToken& id) = 0;
// Until Activate() is called, ClipboardImageModelFactory is in an inactive
// state and all rendering requests will be queued until activated.
virtual void Activate() = 0;
// Called after Activate() to pause rendering requests.
virtual void Deactivate() = 0;
protected:
ClipboardImageModelFactory();
virtual ~ClipboardImageModelFactory();
};
} // namespace ash
#endif // ASH_PUBLIC_CPP_CLIPBOARD_IMAGE_MODEL_FACTORY_H_
......@@ -29,7 +29,9 @@
#include "chrome/browser/component_updater/sth_set_component_remover.h"
#include "chrome/browser/google/google_brand_chromeos.h"
#include "chrome/browser/net/nss_context.h"
#include "chrome/browser/ui/ash/clipboard_image_model_factory_impl.h"
#include "chrome/common/pref_names.h"
#include "chromeos/constants/chromeos_features.h"
#include "chromeos/network/network_cert_loader.h"
#include "chromeos/tpm/install_attributes.h"
#include "components/prefs/pref_service.h"
......@@ -200,6 +202,11 @@ void UserSessionInitializer::InitializePrimaryProfileServices(
if (crostini_manager)
crostini_manager->MaybeUpdateCrostini();
if (chromeos::features::IsClipboardHistoryEnabled()) {
clipboard_image_model_factory_impl_ =
std::make_unique<ClipboardImageModelFactoryImpl>(profile);
}
g_browser_process->platform_part()->InitializePrimaryProfileServices(profile);
}
......
......@@ -5,9 +5,12 @@
#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SESSION_USER_SESSION_INITIALIZER_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_SESSION_USER_SESSION_INITIALIZER_H_
#include <memory>
#include "components/session_manager/core/session_manager_observer.h"
#include "components/user_manager/user.h"
class ClipboardImageModelFactoryImpl;
class Profile;
namespace user_manager {
......@@ -72,6 +75,10 @@ class UserSessionInitializer : public session_manager::SessionManagerObserver {
base::OnceClosure init_rlz_impl_closure_for_testing_;
// Clipboard html image generator for the primary user.
std::unique_ptr<ClipboardImageModelFactoryImpl>
clipboard_image_model_factory_impl_;
base::WeakPtrFactory<UserSessionInitializer> weak_factory_{this};
};
......
......@@ -1862,6 +1862,8 @@ static_library("ui") {
"ash/chrome_screenshot_grabber_test_observer.h",
"ash/chrome_shell_delegate.cc",
"ash/chrome_shell_delegate.h",
"ash/clipboard_image_model_factory_impl.cc",
"ash/clipboard_image_model_factory_impl.h",
"ash/clipboard_util.cc",
"ash/clipboard_util.h",
"ash/holding_space/holding_space_keyed_service.cc",
......
......@@ -7,6 +7,7 @@
#include "ash/clipboard/clipboard_history.h"
#include "ash/clipboard/clipboard_history_controller.h"
#include "ash/clipboard/clipboard_history_item.h"
#include "ash/shell.h"
#include "base/test/bind_test_util.h"
#include "base/test/scoped_feature_list.h"
......@@ -75,7 +76,7 @@ ash::ClipboardHistoryController* GetClipboardHistoryController() {
return ash::Shell::Get()->clipboard_history_controller();
}
const std::list<ui::ClipboardData>& GetClipboardData() {
const std::list<ash::ClipboardHistoryItem>& GetClipboardItems() {
return GetClipboardHistoryController()->history()->GetItems();
}
......@@ -142,31 +143,31 @@ class ClipboardHistoryWithMultiProfileBrowserTest
IN_PROC_BROWSER_TEST_F(ClipboardHistoryWithMultiProfileBrowserTest,
VerifyClipboardHistoryAcrossMultiUser) {
LoginUser(account_id1_);
EXPECT_TRUE(GetClipboardData().empty());
EXPECT_TRUE(GetClipboardItems().empty());
// Store text when the user1 is active.
const std::string copypaste_data1("user1_text1");
SetClipboardText(copypaste_data1);
{
const std::list<ui::ClipboardData>& data = GetClipboardData();
EXPECT_EQ(1u, data.size());
EXPECT_EQ(copypaste_data1, data.front().text());
const std::list<ash::ClipboardHistoryItem>& items = GetClipboardItems();
EXPECT_EQ(1u, items.size());
EXPECT_EQ(copypaste_data1, items.front().data().text());
}
// Log in as the user2. The clipboard history should be non-empty.
chromeos::UserAddingScreen::Get()->Start();
AddUser(account_id2_);
EXPECT_FALSE(GetClipboardData().empty());
EXPECT_FALSE(GetClipboardItems().empty());
// Store text when the user2 is active.
const std::string copypaste_data2("user2_text1");
SetClipboardText(copypaste_data2);
{
const std::list<ui::ClipboardData>& data = GetClipboardData();
EXPECT_EQ(2u, data.size());
EXPECT_EQ(copypaste_data2, data.front().text());
const std::list<ash::ClipboardHistoryItem>& items = GetClipboardItems();
EXPECT_EQ(2u, items.size());
EXPECT_EQ(copypaste_data2, items.front().data().text());
}
// Switch to the user1.
......@@ -177,19 +178,19 @@ IN_PROC_BROWSER_TEST_F(ClipboardHistoryWithMultiProfileBrowserTest,
SetClipboardText(copypaste_data3);
{
const std::list<ui::ClipboardData>& data = GetClipboardData();
EXPECT_EQ(3u, data.size());
const std::list<ash::ClipboardHistoryItem>& items = GetClipboardItems();
EXPECT_EQ(3u, items.size());
// Note that items in |data| follow the time ordering. The most recent item
// is always the first one.
auto it = data.begin();
EXPECT_EQ(copypaste_data3, it->text());
auto it = items.begin();
EXPECT_EQ(copypaste_data3, it->data().text());
std::advance(it, 1u);
EXPECT_EQ(copypaste_data2, it->text());
EXPECT_EQ(copypaste_data2, it->data().text());
std::advance(it, 1u);
EXPECT_EQ(copypaste_data1, it->text());
EXPECT_EQ(copypaste_data1, it->data().text());
}
}
......@@ -203,7 +204,7 @@ IN_PROC_BROWSER_TEST_F(ClipboardHistoryWithMultiProfileBrowserTest,
CloseAllBrowsers();
// No clipboard data. So the clipboard history menu should not show.
ASSERT_TRUE(GetClipboardData().empty());
ASSERT_TRUE(GetClipboardItems().empty());
ShowContextMenuViaAccelerator();
EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
......
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "chrome/browser/ui/ash/clipboard_image_model_factory_impl.h"
#include "chrome/browser/profiles/profile.h"
ClipboardImageModelFactoryImpl::ClipboardImageModelFactoryImpl(
Profile* primary_profile)
: primary_profile_(primary_profile) {
DCHECK(primary_profile_);
}
ClipboardImageModelFactoryImpl::~ClipboardImageModelFactoryImpl() = default;
void ClipboardImageModelFactoryImpl::Render(const base::UnguessableToken& id,
const std::string& html_markup,
ImageModelCallback callback) {
std::move(callback).Run(ui::ImageModel());
}
void ClipboardImageModelFactoryImpl::CancelRequest(
const base::UnguessableToken& id) {}
void ClipboardImageModelFactoryImpl::Activate() {}
void ClipboardImageModelFactoryImpl::Deactivate() {}
// Copyright 2020 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef CHROME_BROWSER_UI_ASH_CLIPBOARD_IMAGE_MODEL_FACTORY_IMPL_H_
#define CHROME_BROWSER_UI_ASH_CLIPBOARD_IMAGE_MODEL_FACTORY_IMPL_H_
#include <string>
#include "ash/public/cpp/clipboard_image_model_factory.h"
#include "base/unguessable_token.h"
class Profile;
// Implements the singleton ClipboardImageModelFactory.
class ClipboardImageModelFactoryImpl : public ash::ClipboardImageModelFactory {
public:
explicit ClipboardImageModelFactoryImpl(Profile* primary_profile);
ClipboardImageModelFactoryImpl(ClipboardImageModelFactoryImpl&) = delete;
ClipboardImageModelFactoryImpl& operator=(ClipboardImageModelFactoryImpl&) =
delete;
~ClipboardImageModelFactoryImpl() override;
private:
// ash::ClipboardImageModelFactory:
void Render(const base::UnguessableToken& id,
const std::string& html_markup,
ImageModelCallback callback) override;
void CancelRequest(const base::UnguessableToken& id) override;
void Activate() override;
void Deactivate() override;
// The primary profile, used instead of the active profile to create the
// WebContents that renders html.
Profile* const primary_profile_;
};
#endif // CHROME_BROWSER_UI_ASH_CLIPBOARD_IMAGE_MODEL_FACTORY_IMPL_H_
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment