1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
ash / quick_insert / quick_insert_clipboard_insertion.cc [blame]
// Copyright 2024 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/quick_insert/quick_insert_clipboard_insertion.h"
#include <memory>
#include <optional>
#include <utility>
#include "ash/clipboard/clipboard_history_util.h"
#include "ash/public/cpp/clipboard_history_controller.h"
#include "ash/public/cpp/scoped_clipboard_history_pause.h"
#include "ash/root_window_controller.h"
#include "ash/shell.h"
#include "ash/wm/window_util.h"
#include "base/check.h"
#include "base/check_deref.h"
#include "base/functional/bind.h"
#include "base/functional/callback.h"
#include "base/location.h"
#include "base/system/sys_info.h"
#include "base/task/sequenced_task_runner.h"
#include "base/task/single_thread_task_runner.h"
#include "base/time/time.h"
#include "build/buildflag.h"
#include "ui/aura/window_tree_host.h"
#include "ui/base/clipboard/clipboard_data.h"
#include "ui/base/clipboard/clipboard_non_backed.h"
#include "ui/base/ime/text_input_client.h"
#include "ui/events/event_constants.h"
#include "ui/events/keycodes/keyboard_codes_posix.h"
#include "ui/events/ozone/layout/keyboard_layout_engine_manager.h"
#include "ui/events/ozone/layout/xkb/xkb_keyboard_layout_engine.h"
#include "ui/events/types/event_type.h"
#if BUILDFLAG(USE_XKBCOMMON)
#include "ui/events/keycodes/xkb_keysym.h"
#endif
namespace ash {
namespace {
// Fork of ash/clipboard/clipboard_history_controller_impl.cc.
#if BUILDFLAG(USE_XKBCOMMON)
// Looks up the DomCode assigned to the keysym. In some edge cases,
// such as Dvorak layout, the original DomCode may be different
// from US standard layout.
ui::DomCode LookUpXkbDomCode(int keysym) {
if (!base::SysInfo::IsRunningOnChromeOS()) {
// On linux-chromeos, stub layout engine is used.
return ui::DomCode::NONE;
}
ui::KeyboardLayoutEngine* layout_engine =
ui::KeyboardLayoutEngineManager::GetKeyboardLayoutEngine();
if (!layout_engine) {
return ui::DomCode::NONE;
}
return static_cast<ui::XkbKeyboardLayoutEngine*>(layout_engine)
->GetDomCodeByKeysym(keysym, /*modifiers=*/std::nullopt);
}
#endif
ui::KeyEvent SyntheticCtrlV(ui::EventType type) {
ui::DomCode dom_code = ui::DomCode::NONE;
#if BUILDFLAG(USE_XKBCOMMON)
dom_code = LookUpXkbDomCode(XKB_KEY_v);
#endif
return dom_code == ui::DomCode::NONE
? ui::KeyEvent(type, ui::VKEY_V, ui::EF_CONTROL_DOWN)
: ui::KeyEvent(type, ui::VKEY_V, dom_code, ui::EF_CONTROL_DOWN);
}
ui::KeyEvent SyntheticCtrl(ui::EventType type) {
int flags =
type == ui::EventType::kKeyPressed ? ui::EF_CONTROL_DOWN : ui::EF_NONE;
ui::DomCode dom_code = ui::DomCode::NONE;
#if BUILDFLAG(USE_XKBCOMMON)
dom_code = LookUpXkbDomCode(XKB_KEY_Control_L);
#endif
return dom_code == ui::DomCode::NONE
? ui::KeyEvent(type, ui::VKEY_CONTROL, flags)
: ui::KeyEvent(type, ui::VKEY_CONTROL, dom_code, flags);
}
std::unique_ptr<ui::ClipboardData> ReplaceClipboard(
std::unique_ptr<ui::ClipboardData> data) {
// Pause changes to clipboard history while manipulating the clipboard.
std::unique_ptr<ScopedClipboardHistoryPause> pause_history =
CHECK_DEREF(ClipboardHistoryController::Get()).CreateScopedPause();
return CHECK_DEREF(ui::ClipboardNonBacked::GetForCurrentThread())
.WriteClipboardData(std::move(data));
}
// `intended_window` must be non-null.
// Forked from `PasteClipboardHistoryItem`.
void InsertClipboardDataImpl(aura::Window* intended_window,
std::unique_ptr<ui::ClipboardData> data,
base::OnceClosure do_web_paste,
base::OnceCallback<void(bool)> done_callback) {
if (intended_window != window_util::GetActiveWindow()) {
std::move(done_callback).Run(false);
return;
}
std::unique_ptr<ui::ClipboardData> replaced_data =
ReplaceClipboard(std::move(data));
if (!do_web_paste.is_null()) {
std::move(do_web_paste).Run();
} else {
// "Synthetic paste" using key events.
aura::WindowTreeHost* host = intended_window->GetHost();
CHECK(host);
ui::KeyEvent ctrl_press = SyntheticCtrl(ui::EventType::kKeyPressed);
host->DeliverEventToSink(&ctrl_press);
ui::KeyEvent v_press = SyntheticCtrlV(ui::EventType::kKeyPressed);
host->DeliverEventToSink(&v_press);
ui::KeyEvent v_release = SyntheticCtrlV(ui::EventType::kKeyReleased);
host->DeliverEventToSink(&v_release);
ui::KeyEvent ctrl_release = SyntheticCtrl(ui::EventType::kKeyReleased);
host->DeliverEventToSink(&ctrl_release);
}
if (!replaced_data) {
// No was on the clipboard.
return;
}
// Restore the clipboard data asynchronously. Some apps take a long time to
// receive the paste event, and some apps will read from the clipboard
// multiple times per paste. Wait a bit before writing `replaced_data` back to
// the clipboard.
base::SingleThreadTaskRunner::GetCurrentDefault()->PostDelayedTask(
FROM_HERE,
base::BindOnce(
[](std::unique_ptr<ui::ClipboardData> data,
base::OnceCallback<void(bool)> done_callback) {
ReplaceClipboard(std::move(data));
std::move(done_callback).Run(true);
},
std::move(replaced_data), std::move(done_callback)),
base::Milliseconds(200));
}
} // namespace
// Forked from `ClipboardHistoryControllerImpl::MaybePastePostTask`.
void InsertClipboardData(std::unique_ptr<ui::ClipboardData> data,
base::OnceClosure do_web_paste,
base::OnceCallback<void(bool)> done_callback) {
aura::Window* active_window = window_util::GetActiveWindow();
if (active_window == nullptr) {
std::move(done_callback).Run(false);
return;
}
if (!do_web_paste.is_null()) {
// `do_web_paste` needs to be called synchronously. If it is non-null, we
// are guaranteed to not be pasting into an ARC window.
InsertClipboardDataImpl(active_window, std::move(data),
std::move(do_web_paste), std::move(done_callback));
return;
}
// Paste asynchronously to ensure ARC windows handle paste events correctly.
base::SequencedTaskRunner::GetCurrentDefault()->PostTask(
FROM_HERE,
base::BindOnce(&InsertClipboardDataImpl, active_window, std::move(data),
std::move(do_web_paste), std::move(done_callback)));
}
} // namespace ash