123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518 |
- // 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.h"
- #include <list>
- #include <memory>
- #include <unordered_map>
- #include "ash/clipboard/clipboard_history_controller_impl.h"
- #include "ash/clipboard/clipboard_history_item.h"
- #include "ash/clipboard/clipboard_history_util.h"
- #include "ash/clipboard/scoped_clipboard_history_pause_impl.h"
- #include "ash/shell.h"
- #include "ash/test/ash_test_base.h"
- #include "base/pickle.h"
- #include "base/strings/utf_string_conversions.h"
- #include "base/test/metrics/histogram_tester.h"
- #include "base/test/repeating_test_future.h"
- #include "third_party/skia/include/core/SkBitmap.h"
- #include "ui/base/clipboard/clipboard.h"
- #include "ui/base/clipboard/clipboard_buffer.h"
- #include "ui/base/clipboard/custom_data_helper.h"
- #include "ui/base/clipboard/scoped_clipboard_writer.h"
- #include "ui/events/event_constants.h"
- #include "ui/events/test/event_generator.h"
- #include "ui/gfx/image/image_unittest_util.h"
- #include "ui/gfx/skia_util.h"
- namespace ash {
- class ClipboardHistoryTest : public AshTestBase {
- public:
- ClipboardHistoryTest() = default;
- ClipboardHistoryTest(const ClipboardHistoryTest&) = delete;
- ClipboardHistoryTest& operator=(const ClipboardHistoryTest&) = delete;
- ~ClipboardHistoryTest() override = default;
- // AshTestBase:
- void SetUp() override {
- AshTestBase::SetUp();
- clipboard_history_ = const_cast<ClipboardHistory*>(
- Shell::Get()->clipboard_history_controller()->history());
- event_generator_ = std::make_unique<ui::test::EventGenerator>(
- ash::Shell::GetPrimaryRootWindow());
- }
- const std::list<ClipboardHistoryItem>& GetClipboardHistoryItems() {
- return clipboard_history_->GetItems();
- }
- ui::test::EventGenerator* GetEventGenerator() {
- return event_generator_.get();
- }
- // Writes |input_strings| to the clipboard buffer and ensures that
- // |expected_strings| are retained in history. If |in_same_sequence| is true,
- // writes to the buffer will be performed in the same task sequence.
- void WriteAndEnsureTextHistory(
- const std::vector<std::u16string>& input_strings,
- const std::vector<std::u16string>& expected_strings,
- bool in_same_sequence = false) {
- for (const auto& input_string : input_strings) {
- {
- ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
- scw.WriteText(input_string);
- }
- if (!in_same_sequence)
- base::RunLoop().RunUntilIdle();
- }
- if (in_same_sequence)
- base::RunLoop().RunUntilIdle();
- EnsureTextHistory(expected_strings);
- }
- void EnsureTextHistory(const std::vector<std::u16string>& expected_strings) {
- const std::list<ClipboardHistoryItem>& items = GetClipboardHistoryItems();
- EXPECT_EQ(expected_strings.size(), items.size());
- int expected_strings_index = 0;
- for (const auto& item : items) {
- EXPECT_EQ(expected_strings[expected_strings_index++],
- base::UTF8ToUTF16(item.data().text()));
- }
- }
- // Writes |input_bitmaps| to the clipboard buffer and ensures that
- // |expected_bitmaps| are retained in history. Writes to the buffer are
- // performed in different task sequences to simulate real world behavior.
- void WriteAndEnsureBitmapHistory(std::vector<SkBitmap>& input_bitmaps,
- std::vector<SkBitmap>& expected_bitmaps) {
- for (const auto& input_bitmap : input_bitmaps) {
- {
- ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
- scw.WriteImage(input_bitmap);
- }
- base::RunLoop().RunUntilIdle();
- }
- const std::list<ClipboardHistoryItem>& items = GetClipboardHistoryItems();
- EXPECT_EQ(expected_bitmaps.size(), items.size());
- int expected_bitmaps_index = 0;
- for (const auto& item : items) {
- // The PNG should not have yet been encoded.
- const auto& maybe_png = item.data().maybe_png();
- EXPECT_FALSE(maybe_png.has_value());
- auto maybe_bitmap = item.data().GetBitmapIfPngNotEncoded();
- EXPECT_TRUE(maybe_bitmap.has_value());
- EXPECT_TRUE(gfx::BitmapsAreEqual(
- expected_bitmaps[expected_bitmaps_index++], maybe_bitmap.value()));
- }
- }
- // Writes |input_data| to the clipboard buffer and ensures that
- // |expected_data| is retained in history. After writing to the buffer, the
- // current task sequence is run to idle to simulate real world behavior.
- void WriteAndEnsureCustomDataHistory(
- const std::unordered_map<std::u16string, std::u16string>& input_data,
- const std::unordered_map<std::u16string, std::u16string>& expected_data) {
- base::Pickle input_data_pickle;
- ui::WriteCustomDataToPickle(input_data, &input_data_pickle);
- {
- ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
- scw.WritePickledData(input_data_pickle,
- ui::ClipboardFormatType::WebCustomDataType());
- }
- base::RunLoop().RunUntilIdle();
- const std::list<ClipboardHistoryItem> items = GetClipboardHistoryItems();
- EXPECT_EQ(expected_data.empty() ? 0u : 1u, items.size());
- if (expected_data.empty())
- return;
- std::unordered_map<std::u16string, std::u16string> actual_data;
- 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);
- }
- ClipboardHistory* clipboard_history() { return clipboard_history_; }
- private:
- std::unique_ptr<ui::test::EventGenerator> event_generator_;
- // Owned by ClipboardHistoryControllerImpl.
- ClipboardHistory* clipboard_history_ = nullptr;
- };
- // Tests that with nothing copied, nothing is shown.
- TEST_F(ClipboardHistoryTest, NothingCopiedNothingSaved) {
- // When nothing is copied, nothing should be saved.
- WriteAndEnsureTextHistory(/*input_strings=*/{},
- /*expected_strings=*/{});
- }
- // Tests that if one thing is copied, one thing is saved.
- TEST_F(ClipboardHistoryTest, OneThingCopiedOneThingSaved) {
- std::vector<std::u16string> input_strings{u"test"};
- std::vector<std::u16string> expected_strings = input_strings;
- // Test that only one string is in history.
- WriteAndEnsureTextHistory(input_strings, expected_strings);
- }
- // Tests that if the same (non bitmap) thing is copied, only one of the
- // duplicates is in the list.
- TEST_F(ClipboardHistoryTest, DuplicateBasic) {
- std::vector<std::u16string> input_strings{u"test", u"test"};
- std::vector<std::u16string> expected_strings{u"test"};
- // Test that both things are saved.
- WriteAndEnsureTextHistory(input_strings, expected_strings);
- }
- // Tests that if multiple things are copied in the same task sequence, only the
- // most recent thing is saved.
- TEST_F(ClipboardHistoryTest, InSameSequenceBasic) {
- std::vector<std::u16string> input_strings{u"test1", u"test2", u"test3"};
- // Because |input_strings| will be copied in the same task sequence, history
- // should only retain the most recent thing.
- std::vector<std::u16string> expected_strings{u"test3"};
- // Test that only the most recent thing is saved.
- WriteAndEnsureTextHistory(input_strings, expected_strings,
- /*in_same_sequence=*/true);
- }
- // Tests the ordering of history is in reverse chronological order.
- TEST_F(ClipboardHistoryTest, HistoryIsReverseChronological) {
- std::vector<std::u16string> input_strings{u"test1", u"test2", u"test3",
- u"test4"};
- std::vector<std::u16string> expected_strings = input_strings;
- // Reverse the vector, history should match this ordering.
- std::reverse(std::begin(expected_strings), std::end(expected_strings));
- WriteAndEnsureTextHistory(input_strings, expected_strings);
- }
- // Tests that when a duplicate is copied, the existing duplicate item moves up
- // to the front of the clipboard history.
- TEST_F(ClipboardHistoryTest, DuplicatePrecedesPreviousRecord) {
- // Input holds a unique string sandwiched by a copy.
- std::vector<std::u16string> input_strings{u"test1", u"test2", u"test1",
- u"test3"};
- // The result should be a reversal of the copied elements. When a duplicate
- // is copied, history will have that item moved to the front instead of adding
- // a new item.
- std::vector<std::u16string> expected_strings{u"test3", u"test1", u"test2"};
- WriteAndEnsureTextHistory(input_strings, expected_strings);
- }
- // Tests that nothing is saved after history is cleared.
- TEST_F(ClipboardHistoryTest, ClearHistoryBasic) {
- // Input holds a unique string sandwhiched by a copy.
- std::vector<std::u16string> input_strings{u"test1", u"test2", u"test1"};
- // The result should be a reversal of the last two elements. When a duplicate
- // is copied, history will show the most recent version of that duplicate.
- std::vector<std::u16string> expected_strings{};
- for (const auto& input_string : input_strings) {
- ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
- scw.WriteText(input_string);
- }
- clipboard_history()->Clear();
- EnsureTextHistory(expected_strings);
- }
- // Tests that there is no crash when an empty clipboard is cleared with empty
- // clipboard history.
- TEST_F(ClipboardHistoryTest, ClearHistoryFromClipboardNoHistory) {
- ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
- }
- // Tests that clipboard history is cleared when the clipboard is cleared.
- TEST_F(ClipboardHistoryTest, ClearHistoryFromClipboardWithHistory) {
- std::vector<std::u16string> input_strings{u"test1", u"test2"};
- std::vector<std::u16string> expected_strings_before_clear{u"test2", u"test1"};
- std::vector<std::u16string> expected_strings_after_clear{};
- for (const auto& input_string : input_strings) {
- {
- ui::ScopedClipboardWriter scw(ui::ClipboardBuffer::kCopyPaste);
- scw.WriteText(input_string);
- }
- base::RunLoop().RunUntilIdle();
- }
- EnsureTextHistory(expected_strings_before_clear);
- ui::Clipboard::GetForCurrentThread()->Clear(ui::ClipboardBuffer::kCopyPaste);
- EnsureTextHistory(expected_strings_after_clear);
- }
- // Tests that the limit of clipboard history is respected.
- TEST_F(ClipboardHistoryTest, HistoryLimit) {
- std::vector<std::u16string> input_strings{u"test1", u"test2", u"test3",
- u"test4", u"test5", u"test6"};
- // The result should be a reversal of the last five elements.
- std::vector<std::u16string> expected_strings{input_strings.begin() + 1,
- input_strings.end()};
- std::reverse(expected_strings.begin(), expected_strings.end());
- WriteAndEnsureTextHistory(input_strings, expected_strings);
- }
- // Tests that pausing clipboard history results in no history collected.
- TEST_F(ClipboardHistoryTest, PauseHistoryBasic) {
- std::vector<std::u16string> input_strings{u"test1", u"test2", u"test1"};
- // Because history is paused, there should be nothing stored.
- std::vector<std::u16string> expected_strings{};
- ScopedClipboardHistoryPauseImpl scoped_pause(clipboard_history());
- WriteAndEnsureTextHistory(input_strings, expected_strings);
- }
- // Tests that pausing clipboard history with the `kAllowReorderOnPaste` pause
- // behavior allows clipboard history to be modified, but clipboard data changes
- // received during the pause are not recorded as copy operations.
- TEST_F(ClipboardHistoryTest, PauseHistoryAllowReorders) {
- std::vector<std::u16string> input_strings{u"test1", u"test2"};
- std::vector<std::u16string> input_string1{u"test1"};
- std::vector<std::u16string> expected_strings_initial{u"test2", u"test1"};
- std::vector<std::u16string> expected_strings_reordered = input_strings;
- // Populate clipboard history to simulate paste-based reorders.
- WriteAndEnsureTextHistory(input_strings, expected_strings_initial);
- base::HistogramTester histogram_tester;
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- ScopedClipboardHistoryPauseImpl scoped_pause(
- clipboard_history(),
- ClipboardHistoryUtil::PauseBehavior::kAllowReorderOnPaste);
- WriteAndEnsureTextHistory(input_string1, expected_strings_reordered);
- // Clipboard history modifications made during a reorder-on-paste operation
- // should not count as copies (or pastes). A reorder is not a user action.
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- }
- // Tests that when already-paused clipboard history is paused again with a
- // different behavior, the newest behavior overrides all others for the duration
- // of the pause's lifetime.
- TEST_F(ClipboardHistoryTest, PauseHistoryNested) {
- std::vector<std::u16string> input_strings{u"test1", u"test2"};
- std::vector<std::u16string> input_string1{u"test1"};
- std::vector<std::u16string> input_string2{u"test2"};
- std::vector<std::u16string> input_string3{u"test3"};
- std::vector<std::u16string> expected_strings_initial{u"test2", u"test1"};
- std::vector<std::u16string> expected_strings_reordered1 = input_strings;
- std::vector<std::u16string> expected_strings_reordered2 =
- expected_strings_initial;
- // Populate clipboard history to simulate paste-based reorders.
- WriteAndEnsureTextHistory(input_strings, expected_strings_initial);
- base::HistogramTester histogram_tester;
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- // By default, pausing prevents clipboard history modifications.
- ScopedClipboardHistoryPauseImpl scoped_pause_default_1(
- clipboard_history(), ClipboardHistoryUtil::PauseBehavior::kDefault);
- WriteAndEnsureTextHistory(input_string3, expected_strings_initial);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- {
- // When allowing paste-based reorders, clipboard history should be modified.
- // Ensure that nesting pauses causes earlier pause behavior to be
- // overridden.
- ScopedClipboardHistoryPauseImpl scoped_pause_allow_reorders(
- clipboard_history(),
- ClipboardHistoryUtil::PauseBehavior::kAllowReorderOnPaste);
- WriteAndEnsureTextHistory(input_string1, expected_strings_reordered1);
- // Clipboard history modifications made during a reorder-on-paste operation
- // should not count as copies (or pastes). A reorder is not a user action.
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- {
- // Test that the newest behavior always applies, regardless of what order
- // behaviors were overridden.
- ScopedClipboardHistoryPauseImpl scoped_pause_default_2(
- clipboard_history(), ClipboardHistoryUtil::PauseBehavior::kDefault);
- WriteAndEnsureTextHistory(input_string3, expected_strings_reordered1);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- }
- // Test that the previous behavior is restored when the newest pause goes
- // out of scope.
- WriteAndEnsureTextHistory(input_string2, expected_strings_reordered2);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- }
- // Test that the previous behavior is restored when the newest pause goes out
- // of scope, regardless of what order behaviors were overridden.
- WriteAndEnsureTextHistory(input_string3, expected_strings_reordered2);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- }
- // Tests that clipboard history pauses do not have to be destroyed in LIFO
- // order.
- TEST_F(ClipboardHistoryTest, PauseHistoryResumeOutOfOrder) {
- std::vector<std::u16string> input_strings{u"test1", u"test2"};
- std::vector<std::u16string> input_string1{u"test1"};
- std::vector<std::u16string> input_string2{u"test2"};
- std::vector<std::u16string> input_string3{u"test3"};
- std::vector<std::u16string> expected_strings_initial{u"test2", u"test1"};
- std::vector<std::u16string> expected_strings_reordered1 = input_strings;
- std::vector<std::u16string> expected_strings_reordered2 =
- expected_strings_initial;
- std::vector<std::u16string> expected_strings_new_item = {u"test3", u"test2",
- u"test1"};
- // Populate clipboard history to simulate paste-based reorders.
- WriteAndEnsureTextHistory(input_strings, expected_strings_initial);
- base::HistogramTester histogram_tester;
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- auto scoped_pause_default = std::make_unique<ScopedClipboardHistoryPauseImpl>(
- clipboard_history(), ClipboardHistoryUtil::PauseBehavior::kDefault);
- WriteAndEnsureTextHistory(input_string3, expected_strings_initial);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- auto scoped_pause_allow_reorders =
- std::make_unique<ScopedClipboardHistoryPauseImpl>(
- clipboard_history(),
- ClipboardHistoryUtil::PauseBehavior::kAllowReorderOnPaste);
- WriteAndEnsureTextHistory(input_string1, expected_strings_reordered1);
- // Clipboard history modifications made during a reorder-on-paste operation
- // should not count as copies (or pastes). A reorder is not a user action.
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- // Verify that pauses can be destroyed in non-LIFO order without changing the
- // current pause behavior.
- scoped_pause_default.reset();
- WriteAndEnsureTextHistory(input_string2, expected_strings_reordered2);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 0u);
- // Verify that when all pauses are destroyed, clipboard history is modified as
- // usual.
- base::test::RepeatingTestFuture<bool> operation_confirmed_future;
- Shell::Get()
- ->clipboard_history_controller()
- ->set_confirmed_operation_callback_for_test(
- operation_confirmed_future.GetCallback());
- scoped_pause_allow_reorders.reset();
- WriteAndEnsureTextHistory(input_string3, expected_strings_new_item);
- // Since clipboard history is not paused in any way, data being written to the
- // clipboard is interpreted as a copy operation.
- EXPECT_EQ(operation_confirmed_future.Take(), true);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.Operation", 1u);
- }
- // Tests that bitmaps are recorded in clipboard history.
- TEST_F(ClipboardHistoryTest, BasicBitmap) {
- SkBitmap test_bitmap = gfx::test::CreateBitmap(3, 2);
- std::vector<SkBitmap> input_bitmaps{test_bitmap};
- std::vector<SkBitmap> expected_bitmaps{test_bitmap};
- WriteAndEnsureBitmapHistory(input_bitmaps, expected_bitmaps);
- }
- // Tests that duplicate bitmaps show up in history as one item placed in
- // most-recent order.
- TEST_F(ClipboardHistoryTest, DuplicateBitmap) {
- SkBitmap test_bitmap_1 = gfx::test::CreateBitmap(3, 2);
- SkBitmap test_bitmap_2 = gfx::test::CreateBitmap(4, 3);
- std::vector<SkBitmap> input_bitmaps{test_bitmap_1, test_bitmap_2,
- test_bitmap_1};
- std::vector<SkBitmap> expected_bitmaps{test_bitmap_1, test_bitmap_2};
- WriteAndEnsureBitmapHistory(input_bitmaps, expected_bitmaps);
- }
- // Tests that unrecognized custom data is omitted from clipboard history.
- TEST_F(ClipboardHistoryTest, BasicCustomData) {
- const std::unordered_map<std::u16string, std::u16string> input_data = {
- {u"custom-format-1", u"custom-data-1"},
- {u"custom-format-2", u"custom-data-2"}};
- // Custom data which is not recognized is omitted from history.
- WriteAndEnsureCustomDataHistory(input_data, /*expected_data=*/{});
- }
- // Tests that file system data is recorded in clipboard history.
- TEST_F(ClipboardHistoryTest, BasicFileSystemData) {
- const std::unordered_map<std::u16string, std::u16string> input_data = {
- {u"fs/sources", u"/path/to/My%20File.txt"}};
- const std::unordered_map<std::u16string, std::u16string> expected_data =
- input_data;
- WriteAndEnsureCustomDataHistory(input_data, expected_data);
- }
- // Tests that the ClipboardHistoryDisplayFormat for HTML with no <img or <table
- // tags is text.
- TEST_F(ClipboardHistoryTest, DisplayFormatForPlainHTML) {
- ui::ClipboardData data;
- data.set_markup_data("plain html with no img or table tags");
- EXPECT_EQ(ClipboardHistoryUtil::ClipboardHistoryDisplayFormat::kText,
- ClipboardHistoryUtil::CalculateDisplayFormat(data));
- data.set_markup_data("<img> </img>");
- EXPECT_EQ(ClipboardHistoryUtil::ClipboardHistoryDisplayFormat::kHtml,
- ClipboardHistoryUtil::CalculateDisplayFormat(data));
- }
- // Tests that Ash.ClipboardHistory.ControlToVDelay is only recorded if
- // ui::VKEY_V is pressed with only ui::VKEY_CONTROL pressed.
- TEST_F(ClipboardHistoryTest, RecordControlV) {
- base::HistogramTester histogram_tester;
- auto* event_generator = GetEventGenerator();
- // Press Ctrl + V, a histogram should be emitted.
- event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE);
- PressAndReleaseKey(ui::VKEY_V, ui::EF_CONTROL_DOWN);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 1u);
- // Press and release V again, no additional histograms should be emitted.
- PressAndReleaseKey(ui::VKEY_V, ui::EF_CONTROL_DOWN);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 1u);
- // Release Control to return to no keys pressed.
- event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_NONE);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 1u);
- // Hold shift while pressing ctrl + V, no histogram should be recorded.
- event_generator->PressKey(ui::VKEY_SHIFT, ui::EF_NONE);
- event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_SHIFT_DOWN);
- PressAndReleaseKey(ui::VKEY_V, ui::EF_CONTROL_DOWN | ui::EF_SHIFT_DOWN);
- event_generator->ReleaseKey(ui::VKEY_CONTROL, ui::EF_SHIFT_DOWN);
- event_generator->ReleaseKey(ui::VKEY_SHIFT, ui::EF_NONE);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 1u);
- // Press Ctrl, then press and release a random key, then press V. A histogram
- // should be recorded.
- event_generator->PressKey(ui::VKEY_CONTROL, ui::EF_NONE);
- PressAndReleaseKey(ui::VKEY_X, ui::EF_CONTROL_DOWN);
- PressAndReleaseKey(ui::VKEY_V, ui::EF_CONTROL_DOWN);
- histogram_tester.ExpectTotalCount("Ash.ClipboardHistory.ControlToVDelay", 2u);
- }
- } // namespace ash
|