blob: 08e9690bb0383cb48659871d40d274b6934052b1 [file] [log] [blame]
// 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_controller_impl.h"
#include <memory>
#include "ash/app_list/app_list_controller_impl.h"
#include "ash/clipboard/clipboard_history.h"
#include "ash/constants/ash_features.h"
#include "ash/public/cpp/clipboard_image_model_factory.h"
#include "ash/public/cpp/session/session_types.h"
#include "ash/session/session_controller_impl.h"
#include "ash/shell.h"
#include "ash/test/ash_test_base.h"
#include "base/location.h"
#include "base/run_loop.h"
#include "base/strings/utf_string_conversions.h"
#include "base/test/metrics/histogram_tester.h"
#include "base/test/scoped_feature_list.h"
#include "base/threading/sequenced_task_runner_handle.h"
#include "base/unguessable_token.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/base/clipboard/clipboard_buffer.h"
#include "ui/base/clipboard/scoped_clipboard_writer.h"
#include "ui/base/models/image_model.h"
#include "ui/events/test/event_generator.h"
namespace ash {
namespace {
void FlushMessageLoop() {
base::RunLoop run_loop;
base::SequencedTaskRunnerHandle::Get()->PostTask(FROM_HERE,
run_loop.QuitClosure());
run_loop.Run();
}
void WriteToClipboard(const std::string& str) {
{
ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
scw.WriteText(base::UTF8ToUTF16(str));
}
FlushMessageLoop();
}
class MockClipboardImageModelFactory : public ClipboardImageModelFactory {
public:
MockClipboardImageModelFactory() = default;
MockClipboardImageModelFactory(const MockClipboardImageModelFactory&) =
delete;
MockClipboardImageModelFactory& operator=(
const MockClipboardImageModelFactory&) = delete;
~MockClipboardImageModelFactory() override = default;
// ClipboardImageModelFactory:
void Render(const base::UnguessableToken& clipboard_history_item_id,
const std::string& markup,
const gfx::Size& bounding_box_size,
ImageModelCallback callback) override {
std::move(callback).Run(ui::ImageModel());
}
void CancelRequest(const base::UnguessableToken& request_id) override {}
void Activate() override {}
void Deactivate() override {}
void RenderCurrentPendingRequests() override {}
void OnShutdown() override {}
};
} // namespace
class ClipboardHistoryControllerTest : public AshTestBase {
public:
ClipboardHistoryControllerTest() = default;
ClipboardHistoryControllerTest(const ClipboardHistoryControllerTest&) =
delete;
ClipboardHistoryControllerTest& operator=(
const ClipboardHistoryControllerTest&) = delete;
~ClipboardHistoryControllerTest() override = default;
// AshTestBase:
void SetUp() override {
scoped_feature_list_.InitAndEnableFeature(
chromeos::features::kClipboardHistory);
AshTestBase::SetUp();
mock_image_factory_ = std::make_unique<MockClipboardImageModelFactory>();
}
ClipboardHistoryControllerImpl* GetClipboardHistoryController() {
return Shell::Get()->clipboard_history_controller();
}
void ShowMenu() {
GetEventGenerator()->PressKey(ui::VKEY_V, ui::EF_COMMAND_DOWN);
GetEventGenerator()->ReleaseKey(ui::VKEY_V, ui::EF_COMMAND_DOWN);
}
private:
base::test::ScopedFeatureList scoped_feature_list_;
std::unique_ptr<MockClipboardImageModelFactory> mock_image_factory_;
};
// Tests that search + v with no history fails to show a menu.
TEST_F(ClipboardHistoryControllerTest, NoHistoryNoMenu) {
base::HistogramTester histogram_tester;
ShowMenu();
EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 0);
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 0);
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 0);
}
// Tests that search + v shows a menu when there is something to show.
TEST_F(ClipboardHistoryControllerTest, MultiShowMenu) {
base::HistogramTester histogram_tester;
// Copy something to enable the clipboard history menu.
WriteToClipboard("test");
ShowMenu();
EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
histogram_tester.ExpectBucketCount(
"Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 1);
// No UserJourneyTime should be recorded as the menu is still showing.
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 0);
histogram_tester.ExpectBucketCount(
"Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 1);
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 1);
// Hide the menu.
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, /*flags=*/0);
GetEventGenerator()->ReleaseKey(ui::VKEY_ESCAPE, /*flags=*/0);
EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
histogram_tester.ExpectBucketCount(
"Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 1);
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 1);
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 1);
// Reshow the menu.
ShowMenu();
EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
histogram_tester.ExpectBucketCount(
"Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 2);
// No new UserJourneyTime histogram should be recorded as the menu is
// still showing.
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 1);
histogram_tester.ExpectBucketCount(
"Ash.ClipboardHistory.ContextMenu.NumberOfItemsShown", 1, 2);
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.DisplayFormatShown", 2);
// Hide the menu.
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, /*flags=*/0);
GetEventGenerator()->ReleaseKey(ui::VKEY_ESCAPE, /*flags=*/0);
EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
histogram_tester.ExpectTotalCount(
"Ash.ClipboardHistory.ContextMenu.UserJourneyTime", 2);
}
// Verifies that the clipboard history is disabled in some user modes, which
// means that the clipboard history should not be recorded and meanwhile the
// menu view should not show (https://crbug.com/1100739).
TEST_F(ClipboardHistoryControllerTest, VerifyAvailabilityInUserModes) {
// Write one item into the clipboard history.
WriteToClipboard("text");
constexpr struct {
user_manager::UserType user_type;
bool is_enabled;
} kTestCases[] = {{user_manager::USER_TYPE_REGULAR, true},
{user_manager::USER_TYPE_GUEST, true},
{user_manager::USER_TYPE_PUBLIC_ACCOUNT, false},
{user_manager::USER_TYPE_KIOSK_APP, false},
{user_manager::USER_TYPE_CHILD, true},
{user_manager::USER_TYPE_ARC_KIOSK_APP, false},
{user_manager::USER_TYPE_WEB_KIOSK_APP, false}};
UserSession session;
session.session_id = 1u;
session.user_info.account_id = AccountId::FromUserEmail("user1@test.com");
session.user_info.display_name = "User 1";
session.user_info.display_email = "user1@test.com";
for (const auto& test_case : kTestCases) {
// Switch to the target user mode.
session.user_info.type = test_case.user_type;
Shell::Get()->session_controller()->UpdateUserSession(session);
// Write a new item into the clipboard buffer.
WriteToClipboard("test");
const std::list<ClipboardHistoryItem>& items =
Shell::Get()->clipboard_history_controller()->history()->GetItems();
if (test_case.is_enabled) {
// Verify that the new item should be included in the clipboard history
// and the menu should be able to show.
EXPECT_EQ(2u, items.size());
ShowMenu();
EXPECT_TRUE(
Shell::Get()->clipboard_history_controller()->IsMenuShowing());
GetEventGenerator()->PressKey(ui::VKEY_ESCAPE, /*flags=*/0);
GetEventGenerator()->ReleaseKey(ui::VKEY_ESCAPE, /*flags=*/0);
EXPECT_FALSE(
Shell::Get()->clipboard_history_controller()->IsMenuShowing());
// Restore the clipboard history by removing the new item.
ClipboardHistory* clipboard_history = const_cast<ClipboardHistory*>(
Shell::Get()->clipboard_history_controller()->history());
clipboard_history->RemoveItemForId(items.begin()->id());
} else {
// Verify that the new item should not be written into the clipboard
// history. The menu cannot show although the clipboard history is
// non-empty.
EXPECT_EQ(1u, items.size());
ShowMenu();
EXPECT_FALSE(
Shell::Get()->clipboard_history_controller()->IsMenuShowing());
}
}
}
// Verifies that the clipboard history menu is disabled when the screen for
// user adding shows.
TEST_F(ClipboardHistoryControllerTest, DisableInUserAddingScreen) {
WriteToClipboard("text");
// Emulate that the user adding screen displays.
Shell::Get()->session_controller()->ShowMultiProfileLogin();
// Try to show the clipboard history menu; verify that the menu does not show.
ShowMenu();
EXPECT_FALSE(Shell::Get()->clipboard_history_controller()->IsMenuShowing());
}
// Tests that pressing and holding VKEY_V, then the search key (EF_COMMAND_DOWN)
// does not show the AppList.
TEST_F(ClipboardHistoryControllerTest, VThenSearchDoesNotShowLauncher) {
GetEventGenerator()->PressKey(ui::VKEY_V, /*event_flags=*/0);
GetEventGenerator()->PressKey(ui::VKEY_LWIN, /*event_flags=*/0);
// Release VKEY_V, which could trigger a key released accelerator.
GetEventGenerator()->ReleaseKey(ui::VKEY_V, /*event_flags=*/0);
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible(
/*display_id=*/base::nullopt));
// Release VKEY_LWIN(search/launcher), which could trigger the app list.
GetEventGenerator()->ReleaseKey(ui::VKEY_LWIN, /*event_flags=*/0);
EXPECT_FALSE(Shell::Get()->app_list_controller()->IsVisible(
/*display_id=*/base::nullopt));
}
// Tests that clearing the clipboard clears ClipboardHistory
TEST_F(ClipboardHistoryControllerTest, ClearClipboardClearsHistory) {
// Write a single item to ClipboardHistory.
WriteToClipboard("test");
// Clear the clipboard.
ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
FlushMessageLoop();
// History should also be cleared.
const std::list<ClipboardHistoryItem>& items =
Shell::Get()->clipboard_history_controller()->history()->GetItems();
EXPECT_EQ(0u, items.size());
ShowMenu();
EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
}
// Tests that clearing the clipboard closes the ClipboardHistory menu.
TEST_F(ClipboardHistoryControllerTest,
ClearingClipboardClosesClipboardHistory) {
// Write a single item to ClipboardHistory.
WriteToClipboard("test");
ASSERT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
ShowMenu();
EXPECT_TRUE(GetClipboardHistoryController()->IsMenuShowing());
// The cursor is visible after showing the clipboard history menu through
// the accelerator.
EXPECT_TRUE(Shell::Get()->cursor_manager()->IsCursorVisible());
ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
FlushMessageLoop();
EXPECT_FALSE(GetClipboardHistoryController()->IsMenuShowing());
}
} // namespace ash