seat.cc 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439
  1. // Copyright 2017 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "components/exo/seat.h"
  5. #include <memory>
  6. #include "ash/wm/window_util.h"
  7. #include "base/auto_reset.h"
  8. #include "base/barrier_closure.h"
  9. #include "base/bind.h"
  10. #include "base/callback_helpers.h"
  11. #include "base/memory/weak_ptr.h"
  12. #include "base/pickle.h"
  13. #include "base/strings/utf_string_conversions.h"
  14. #include "build/chromeos_buildflags.h"
  15. #include "components/exo/data_exchange_delegate.h"
  16. #include "components/exo/data_source.h"
  17. #include "components/exo/drag_drop_operation.h"
  18. #include "components/exo/mime_utils.h"
  19. #include "components/exo/seat_observer.h"
  20. #include "components/exo/shell_surface_base.h"
  21. #include "components/exo/shell_surface_util.h"
  22. #include "components/exo/surface.h"
  23. #include "components/exo/wm_helper.h"
  24. #include "components/exo/xkb_tracker.h"
  25. #include "services/data_decoder/public/cpp/decode_image.h"
  26. #include "ui/aura/client/focus_client.h"
  27. #include "ui/base/clipboard/clipboard_monitor.h"
  28. #include "ui/base/clipboard/scoped_clipboard_writer.h"
  29. #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
  30. #include "ui/base/data_transfer_policy/data_transfer_endpoint_serializer.h"
  31. #include "ui/display/screen.h"
  32. #include "ui/events/event_utils.h"
  33. #include "ui/events/platform/platform_event_source.h"
  34. #include "ui/gfx/geometry/point_f.h"
  35. #if BUILDFLAG(IS_CHROMEOS_ASH)
  36. #include "ash/shell.h"
  37. #include "ui/aura/env.h"
  38. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  39. namespace exo {
  40. Seat::Seat(std::unique_ptr<DataExchangeDelegate> delegate)
  41. : changing_clipboard_data_to_selection_source_(false),
  42. data_exchange_delegate_(std::move(delegate)) {
  43. WMHelper::GetInstance()->AddFocusObserver(this);
  44. // Prepend handler as it's critical that we see all events.
  45. WMHelper::GetInstance()->PrependPreTargetHandler(this);
  46. ui::ClipboardMonitor::GetInstance()->AddObserver(this);
  47. // TODO(reveman): Need to handle the mus case where PlatformEventSource is
  48. // null. https://crbug.com/856230
  49. if (ui::PlatformEventSource::GetInstance())
  50. ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this);
  51. #if BUILDFLAG(IS_CHROMEOS_ASH)
  52. ui_lock_controller_ = std::make_unique<UILockController>(this);
  53. // Seat needs to be registered as observers before any Keyboard,
  54. // because Keyboard expects that the XkbTracker is up-to-date when its
  55. // observer method is called.
  56. xkb_tracker_ = std::make_unique<XkbTracker>();
  57. ash::ImeControllerImpl* ime_controller = ash::Shell::Get()->ime_controller();
  58. xkb_tracker_->UpdateKeyboardLayout(ime_controller->keyboard_layout_name());
  59. ime_controller->AddObserver(this);
  60. #endif
  61. }
  62. Seat::Seat() : Seat(nullptr) {}
  63. Seat::~Seat() {
  64. Shutdown();
  65. }
  66. void Seat::Shutdown() {
  67. if (was_shutdown_)
  68. return;
  69. was_shutdown_ = true;
  70. DCHECK(!selection_source_) << "DataSource must be released before Seat";
  71. #if BUILDFLAG(IS_CHROMEOS_ASH)
  72. ash::Shell::Get()->ime_controller()->RemoveObserver(this);
  73. #endif
  74. WMHelper::GetInstance()->RemoveFocusObserver(this);
  75. WMHelper::GetInstance()->RemovePreTargetHandler(this);
  76. ui::ClipboardMonitor::GetInstance()->RemoveObserver(this);
  77. if (ui::PlatformEventSource::GetInstance())
  78. ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this);
  79. }
  80. void Seat::AddObserver(SeatObserver* observer, int priority) {
  81. for (const auto& observer_list : priority_observer_list_)
  82. if (observer_list.HasObserver(observer))
  83. return;
  84. DCHECK(IsValidObserverPriority(priority));
  85. priority_observer_list_[priority].AddObserver(observer);
  86. }
  87. void Seat::RemoveObserver(SeatObserver* observer) {
  88. // We assume that the number of priority variations is small enough.
  89. for (auto& observer_list : priority_observer_list_)
  90. observer_list.RemoveObserver(observer);
  91. }
  92. void Seat::NotifyPointerCaptureEnabled(Pointer* pointer,
  93. aura::Window* capture_window) {
  94. for (auto& observer_list : priority_observer_list_) {
  95. for (auto& observer : observer_list)
  96. observer.OnPointerCaptureEnabled(pointer, capture_window);
  97. }
  98. }
  99. void Seat::NotifyPointerCaptureDisabled(Pointer* pointer,
  100. aura::Window* capture_window) {
  101. for (auto& observer_list : priority_observer_list_) {
  102. for (auto& observer : observer_list)
  103. observer.OnPointerCaptureDisabled(pointer, capture_window);
  104. }
  105. }
  106. Surface* Seat::GetFocusedSurface() {
  107. return GetTargetSurfaceForKeyboardFocus(
  108. WMHelper::GetInstance()->GetFocusedWindow());
  109. }
  110. void Seat::StartDrag(DataSource* source,
  111. Surface* origin,
  112. Surface* icon,
  113. ui::mojom::DragEventSource event_source) {
  114. gfx::Point cursor_location = aura::Env::GetInstance()->GetLastPointerPoint(
  115. event_source, origin->window(), /*fallback=*/absl::nullopt);
  116. // DragDropOperation manages its own lifetime.
  117. drag_drop_operation_ = DragDropOperation::Create(
  118. data_exchange_delegate_.get(), source, origin, icon,
  119. gfx::PointF(cursor_location), event_source);
  120. }
  121. void Seat::AbortPendingDragOperation() {
  122. if (drag_drop_operation_)
  123. drag_drop_operation_->AbortIfPending();
  124. }
  125. void Seat::SetSelection(DataSource* source) {
  126. Surface* focused_surface = GetFocusedSurface();
  127. if (!source || !focused_surface ||
  128. !source->CanBeDataSourceForCopy(focused_surface)) {
  129. if (source)
  130. source->Cancelled();
  131. return;
  132. }
  133. if (selection_source_) {
  134. if (selection_source_->get() == source)
  135. return;
  136. selection_source_->get()->Cancelled();
  137. }
  138. selection_source_ = std::make_unique<ScopedDataSource>(source, this);
  139. ui::EndpointType endpoint_type =
  140. data_exchange_delegate_->GetDataTransferEndpointType(
  141. focused_surface->window());
  142. scoped_refptr<RefCountedScopedClipboardWriter> writer =
  143. base::MakeRefCounted<RefCountedScopedClipboardWriter>(endpoint_type);
  144. size_t num_data_read_callbacks = DataSource::kMaxDataTypes;
  145. #if BUILDFLAG(IS_CHROMEOS_ASH)
  146. // Lacros sends additional metadata, in a custom MIME type, to sync clipboard
  147. // source metadata,
  148. if (endpoint_type == ui::EndpointType::kLacros)
  149. ++num_data_read_callbacks;
  150. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  151. base::RepeatingClosure data_read_callback = base::BarrierClosure(
  152. num_data_read_callbacks,
  153. base::BindOnce(&Seat::OnAllReadsFinished, weak_ptr_factory_.GetWeakPtr(),
  154. writer));
  155. #if BUILDFLAG(IS_CHROMEOS_ASH)
  156. if (endpoint_type == ui::EndpointType::kLacros) {
  157. source->ReadDataTransferEndpoint(
  158. base::BindOnce(&Seat::OnDataTransferEndpointRead,
  159. weak_ptr_factory_.GetWeakPtr(), writer,
  160. data_read_callback),
  161. data_read_callback);
  162. }
  163. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  164. source->GetDataForPreferredMimeTypes(
  165. base::BindOnce(&Seat::OnTextRead, weak_ptr_factory_.GetWeakPtr(), writer,
  166. data_read_callback),
  167. base::BindOnce(&Seat::OnRTFRead, weak_ptr_factory_.GetWeakPtr(), writer,
  168. data_read_callback),
  169. base::BindOnce(&Seat::OnHTMLRead, weak_ptr_factory_.GetWeakPtr(), writer,
  170. data_read_callback),
  171. base::BindOnce(&Seat::OnImageRead, weak_ptr_factory_.GetWeakPtr(), writer,
  172. data_read_callback),
  173. base::BindOnce(&Seat::OnFilenamesRead, weak_ptr_factory_.GetWeakPtr(),
  174. endpoint_type, writer, data_read_callback),
  175. DataSource::ReadFileContentsDataCallback(),
  176. base::BindOnce(&Seat::OnWebCustomDataRead, weak_ptr_factory_.GetWeakPtr(),
  177. writer, data_read_callback),
  178. data_read_callback);
  179. }
  180. class Seat::RefCountedScopedClipboardWriter
  181. : public ui::ScopedClipboardWriter,
  182. public base::RefCounted<RefCountedScopedClipboardWriter> {
  183. public:
  184. explicit RefCountedScopedClipboardWriter(ui::EndpointType type)
  185. : ScopedClipboardWriter(
  186. ui::ClipboardBuffer::kCopyPaste,
  187. std::make_unique<ui::DataTransferEndpoint>(type)) {}
  188. private:
  189. friend class base::RefCounted<RefCountedScopedClipboardWriter>;
  190. virtual ~RefCountedScopedClipboardWriter() = default;
  191. };
  192. #if BUILDFLAG(IS_CHROMEOS_ASH)
  193. void Seat::OnDataTransferEndpointRead(
  194. scoped_refptr<RefCountedScopedClipboardWriter> writer,
  195. base::OnceClosure callback,
  196. const std::string& mime_type,
  197. std::u16string data) {
  198. std::string utf8_json = base::UTF16ToUTF8(data);
  199. auto clipboard_source = ui::ConvertJsonToDataTransferEndpoint(utf8_json);
  200. writer->SetDataSource(std::move(clipboard_source));
  201. std::move(callback).Run();
  202. }
  203. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  204. void Seat::OnTextRead(scoped_refptr<RefCountedScopedClipboardWriter> writer,
  205. base::OnceClosure callback,
  206. const std::string& mime_type,
  207. std::u16string data) {
  208. writer->WriteText(std::move(data));
  209. std::move(callback).Run();
  210. }
  211. void Seat::OnRTFRead(scoped_refptr<RefCountedScopedClipboardWriter> writer,
  212. base::OnceClosure callback,
  213. const std::string& mime_type,
  214. const std::vector<uint8_t>& data) {
  215. writer->WriteRTF(
  216. std::string(reinterpret_cast<const char*>(data.data()), data.size()));
  217. std::move(callback).Run();
  218. }
  219. void Seat::OnHTMLRead(scoped_refptr<RefCountedScopedClipboardWriter> writer,
  220. base::OnceClosure callback,
  221. const std::string& mime_type,
  222. std::u16string data) {
  223. writer->WriteHTML(std::move(data), std::string());
  224. std::move(callback).Run();
  225. }
  226. void Seat::OnImageRead(scoped_refptr<RefCountedScopedClipboardWriter> writer,
  227. base::OnceClosure callback,
  228. const std::string& mime_type,
  229. const std::vector<uint8_t>& data) {
  230. #if BUILDFLAG(IS_CHROMEOS_ASH)
  231. data_decoder::DecodeImageIsolated(
  232. data, data_decoder::mojom::ImageCodec::kDefault, false,
  233. std::numeric_limits<int64_t>::max(), gfx::Size(),
  234. base::BindOnce(&Seat::OnImageDecoded, weak_ptr_factory_.GetWeakPtr(),
  235. std::move(callback), writer));
  236. #else
  237. std::move(callback).Run();
  238. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  239. }
  240. #if BUILDFLAG(IS_CHROMEOS_ASH)
  241. void Seat::OnImageDecoded(base::OnceClosure callback,
  242. scoped_refptr<RefCountedScopedClipboardWriter> writer,
  243. const SkBitmap& bitmap) {
  244. if (!bitmap.isNull() && !bitmap.empty())
  245. writer->WriteImage(bitmap);
  246. std::move(callback).Run();
  247. }
  248. #endif // BUILDFLAG(IS_CHROMEOS_ASH)
  249. void Seat::OnFilenamesRead(
  250. ui::EndpointType source,
  251. scoped_refptr<RefCountedScopedClipboardWriter> writer,
  252. base::OnceClosure callback,
  253. const std::string& mime_type,
  254. const std::vector<uint8_t>& data) {
  255. std::vector<ui::FileInfo> filenames =
  256. data_exchange_delegate_->GetFilenames(source, data);
  257. writer->WriteFilenames(ui::FileInfosToURIList(filenames));
  258. std::move(callback).Run();
  259. }
  260. void Seat::OnWebCustomDataRead(
  261. scoped_refptr<RefCountedScopedClipboardWriter> writer,
  262. base::OnceClosure callback,
  263. const std::string& mime_type,
  264. const std::vector<uint8_t>& data) {
  265. base::Pickle pickle(reinterpret_cast<const char*>(data.data()), data.size());
  266. writer->WritePickledData(pickle,
  267. ui::ClipboardFormatType::WebCustomDataType());
  268. std::move(callback).Run();
  269. }
  270. void Seat::OnAllReadsFinished(
  271. scoped_refptr<RefCountedScopedClipboardWriter> writer) {
  272. // We need to destroy the ScopedClipboardWriter in this call, before
  273. // |auto_reset| is destroyed, so if there are outstanding references that
  274. // would prevent that, reschedule this task.
  275. if (!writer->HasOneRef()) {
  276. base::ThreadTaskRunnerHandle::Get()->PostTask(
  277. FROM_HERE,
  278. base::BindOnce(&Seat::OnAllReadsFinished,
  279. weak_ptr_factory_.GetWeakPtr(), std::move(writer)));
  280. return;
  281. }
  282. base::AutoReset<bool> auto_reset(
  283. &changing_clipboard_data_to_selection_source_, true);
  284. writer.reset();
  285. }
  286. ////////////////////////////////////////////////////////////////////////////////
  287. // aura::client::FocusChangeObserver overrides:
  288. void Seat::OnWindowFocused(aura::Window* gained_focus,
  289. aura::Window* lost_focus) {
  290. Surface* const gaining_focus_surface =
  291. GetTargetSurfaceForKeyboardFocus(gained_focus);
  292. Surface* const lost_focus_surface =
  293. GetTargetSurfaceForKeyboardFocus(lost_focus);
  294. for (auto& observer_list : priority_observer_list_) {
  295. for (auto& observer : observer_list)
  296. observer.OnSurfaceFocused(gaining_focus_surface, lost_focus_surface,
  297. !!gained_focus);
  298. }
  299. }
  300. ////////////////////////////////////////////////////////////////////////////////
  301. // ui::PlatformEventObserver overrides:
  302. void Seat::WillProcessEvent(const ui::PlatformEvent& event) {
  303. switch (ui::EventTypeFromNative(event)) {
  304. case ui::ET_KEY_PRESSED:
  305. case ui::ET_KEY_RELEASED:
  306. physical_code_for_currently_processing_event_ = ui::CodeFromNative(event);
  307. break;
  308. default:
  309. break;
  310. }
  311. }
  312. void Seat::DidProcessEvent(const ui::PlatformEvent& event) {
  313. switch (ui::EventTypeFromNative(event)) {
  314. case ui::ET_KEY_PRESSED:
  315. physical_code_for_currently_processing_event_ = ui::DomCode::NONE;
  316. break;
  317. case ui::ET_KEY_RELEASED:
  318. // Remove this from the pressed key map because when IME is active we can
  319. // end up getting the DidProcessEvent call before we get the OnKeyEvent
  320. // callback and then the key will end up being stuck pressed.
  321. if (physical_code_for_currently_processing_event_ != ui::DomCode::NONE) {
  322. pressed_keys_.erase(physical_code_for_currently_processing_event_);
  323. physical_code_for_currently_processing_event_ = ui::DomCode::NONE;
  324. }
  325. break;
  326. default:
  327. break;
  328. }
  329. }
  330. ////////////////////////////////////////////////////////////////////////////////
  331. // ui::EventHandler overrides:
  332. void Seat::OnKeyEvent(ui::KeyEvent* event) {
  333. // Ignore synthetic key repeat events.
  334. if (event->is_repeat())
  335. return;
  336. if (physical_code_for_currently_processing_event_ != ui::DomCode::NONE) {
  337. switch (event->type()) {
  338. case ui::ET_KEY_PRESSED:
  339. pressed_keys_.emplace(
  340. physical_code_for_currently_processing_event_,
  341. KeyState{event->code(), /*consumed_by_ime=*/false});
  342. break;
  343. case ui::ET_KEY_RELEASED:
  344. pressed_keys_.erase(physical_code_for_currently_processing_event_);
  345. break;
  346. default:
  347. NOTREACHED();
  348. break;
  349. }
  350. }
  351. #if BUILDFLAG(IS_CHROMEOS_ASH)
  352. xkb_tracker_->UpdateKeyboardModifiers(event->flags());
  353. for (auto& observer_list : priority_observer_list_) {
  354. for (auto& observer : observer_list)
  355. observer.OnKeyboardModifierUpdated();
  356. }
  357. #endif
  358. }
  359. ////////////////////////////////////////////////////////////////////////////////
  360. // ui::ClipboardObserver overrides:
  361. void Seat::OnClipboardDataChanged() {
  362. if (!selection_source_ || changing_clipboard_data_to_selection_source_)
  363. return;
  364. selection_source_->get()->Cancelled();
  365. selection_source_.reset();
  366. }
  367. #if BUILDFLAG(IS_CHROMEOS_ASH)
  368. UILockController* Seat::GetUILockControllerForTesting() {
  369. return ui_lock_controller_.get();
  370. }
  371. ////////////////////////////////////////////////////////////////////////////////
  372. // ash::ImeControllerImpl::Observer overrides:
  373. void Seat::OnCapsLockChanged(bool enabled) {}
  374. void Seat::OnKeyboardLayoutNameChanged(const std::string& layout_name) {
  375. xkb_tracker_->UpdateKeyboardLayout(layout_name);
  376. }
  377. #endif
  378. ////////////////////////////////////////////////////////////////////////////////
  379. // DataSourceObserver overrides:
  380. void Seat::OnDataSourceDestroying(DataSource* source) {
  381. if (selection_source_ && selection_source_->get() == source)
  382. selection_source_.reset();
  383. }
  384. } // namespace exo