123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439 |
- // Copyright 2017 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 "components/exo/seat.h"
- #include <memory>
- #include "ash/wm/window_util.h"
- #include "base/auto_reset.h"
- #include "base/barrier_closure.h"
- #include "base/bind.h"
- #include "base/callback_helpers.h"
- #include "base/memory/weak_ptr.h"
- #include "base/pickle.h"
- #include "base/strings/utf_string_conversions.h"
- #include "build/chromeos_buildflags.h"
- #include "components/exo/data_exchange_delegate.h"
- #include "components/exo/data_source.h"
- #include "components/exo/drag_drop_operation.h"
- #include "components/exo/mime_utils.h"
- #include "components/exo/seat_observer.h"
- #include "components/exo/shell_surface_base.h"
- #include "components/exo/shell_surface_util.h"
- #include "components/exo/surface.h"
- #include "components/exo/wm_helper.h"
- #include "components/exo/xkb_tracker.h"
- #include "services/data_decoder/public/cpp/decode_image.h"
- #include "ui/aura/client/focus_client.h"
- #include "ui/base/clipboard/clipboard_monitor.h"
- #include "ui/base/clipboard/scoped_clipboard_writer.h"
- #include "ui/base/data_transfer_policy/data_transfer_endpoint.h"
- #include "ui/base/data_transfer_policy/data_transfer_endpoint_serializer.h"
- #include "ui/display/screen.h"
- #include "ui/events/event_utils.h"
- #include "ui/events/platform/platform_event_source.h"
- #include "ui/gfx/geometry/point_f.h"
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- #include "ash/shell.h"
- #include "ui/aura/env.h"
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- namespace exo {
- Seat::Seat(std::unique_ptr<DataExchangeDelegate> delegate)
- : changing_clipboard_data_to_selection_source_(false),
- data_exchange_delegate_(std::move(delegate)) {
- WMHelper::GetInstance()->AddFocusObserver(this);
- // Prepend handler as it's critical that we see all events.
- WMHelper::GetInstance()->PrependPreTargetHandler(this);
- ui::ClipboardMonitor::GetInstance()->AddObserver(this);
- // TODO(reveman): Need to handle the mus case where PlatformEventSource is
- // null. https://crbug.com/856230
- if (ui::PlatformEventSource::GetInstance())
- ui::PlatformEventSource::GetInstance()->AddPlatformEventObserver(this);
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- ui_lock_controller_ = std::make_unique<UILockController>(this);
- // Seat needs to be registered as observers before any Keyboard,
- // because Keyboard expects that the XkbTracker is up-to-date when its
- // observer method is called.
- xkb_tracker_ = std::make_unique<XkbTracker>();
- ash::ImeControllerImpl* ime_controller = ash::Shell::Get()->ime_controller();
- xkb_tracker_->UpdateKeyboardLayout(ime_controller->keyboard_layout_name());
- ime_controller->AddObserver(this);
- #endif
- }
- Seat::Seat() : Seat(nullptr) {}
- Seat::~Seat() {
- Shutdown();
- }
- void Seat::Shutdown() {
- if (was_shutdown_)
- return;
- was_shutdown_ = true;
- DCHECK(!selection_source_) << "DataSource must be released before Seat";
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- ash::Shell::Get()->ime_controller()->RemoveObserver(this);
- #endif
- WMHelper::GetInstance()->RemoveFocusObserver(this);
- WMHelper::GetInstance()->RemovePreTargetHandler(this);
- ui::ClipboardMonitor::GetInstance()->RemoveObserver(this);
- if (ui::PlatformEventSource::GetInstance())
- ui::PlatformEventSource::GetInstance()->RemovePlatformEventObserver(this);
- }
- void Seat::AddObserver(SeatObserver* observer, int priority) {
- for (const auto& observer_list : priority_observer_list_)
- if (observer_list.HasObserver(observer))
- return;
- DCHECK(IsValidObserverPriority(priority));
- priority_observer_list_[priority].AddObserver(observer);
- }
- void Seat::RemoveObserver(SeatObserver* observer) {
- // We assume that the number of priority variations is small enough.
- for (auto& observer_list : priority_observer_list_)
- observer_list.RemoveObserver(observer);
- }
- void Seat::NotifyPointerCaptureEnabled(Pointer* pointer,
- aura::Window* capture_window) {
- for (auto& observer_list : priority_observer_list_) {
- for (auto& observer : observer_list)
- observer.OnPointerCaptureEnabled(pointer, capture_window);
- }
- }
- void Seat::NotifyPointerCaptureDisabled(Pointer* pointer,
- aura::Window* capture_window) {
- for (auto& observer_list : priority_observer_list_) {
- for (auto& observer : observer_list)
- observer.OnPointerCaptureDisabled(pointer, capture_window);
- }
- }
- Surface* Seat::GetFocusedSurface() {
- return GetTargetSurfaceForKeyboardFocus(
- WMHelper::GetInstance()->GetFocusedWindow());
- }
- void Seat::StartDrag(DataSource* source,
- Surface* origin,
- Surface* icon,
- ui::mojom::DragEventSource event_source) {
- gfx::Point cursor_location = aura::Env::GetInstance()->GetLastPointerPoint(
- event_source, origin->window(), /*fallback=*/absl::nullopt);
- // DragDropOperation manages its own lifetime.
- drag_drop_operation_ = DragDropOperation::Create(
- data_exchange_delegate_.get(), source, origin, icon,
- gfx::PointF(cursor_location), event_source);
- }
- void Seat::AbortPendingDragOperation() {
- if (drag_drop_operation_)
- drag_drop_operation_->AbortIfPending();
- }
- void Seat::SetSelection(DataSource* source) {
- Surface* focused_surface = GetFocusedSurface();
- if (!source || !focused_surface ||
- !source->CanBeDataSourceForCopy(focused_surface)) {
- if (source)
- source->Cancelled();
- return;
- }
- if (selection_source_) {
- if (selection_source_->get() == source)
- return;
- selection_source_->get()->Cancelled();
- }
- selection_source_ = std::make_unique<ScopedDataSource>(source, this);
- ui::EndpointType endpoint_type =
- data_exchange_delegate_->GetDataTransferEndpointType(
- focused_surface->window());
- scoped_refptr<RefCountedScopedClipboardWriter> writer =
- base::MakeRefCounted<RefCountedScopedClipboardWriter>(endpoint_type);
- size_t num_data_read_callbacks = DataSource::kMaxDataTypes;
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- // Lacros sends additional metadata, in a custom MIME type, to sync clipboard
- // source metadata,
- if (endpoint_type == ui::EndpointType::kLacros)
- ++num_data_read_callbacks;
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- base::RepeatingClosure data_read_callback = base::BarrierClosure(
- num_data_read_callbacks,
- base::BindOnce(&Seat::OnAllReadsFinished, weak_ptr_factory_.GetWeakPtr(),
- writer));
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- if (endpoint_type == ui::EndpointType::kLacros) {
- source->ReadDataTransferEndpoint(
- base::BindOnce(&Seat::OnDataTransferEndpointRead,
- weak_ptr_factory_.GetWeakPtr(), writer,
- data_read_callback),
- data_read_callback);
- }
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- source->GetDataForPreferredMimeTypes(
- base::BindOnce(&Seat::OnTextRead, weak_ptr_factory_.GetWeakPtr(), writer,
- data_read_callback),
- base::BindOnce(&Seat::OnRTFRead, weak_ptr_factory_.GetWeakPtr(), writer,
- data_read_callback),
- base::BindOnce(&Seat::OnHTMLRead, weak_ptr_factory_.GetWeakPtr(), writer,
- data_read_callback),
- base::BindOnce(&Seat::OnImageRead, weak_ptr_factory_.GetWeakPtr(), writer,
- data_read_callback),
- base::BindOnce(&Seat::OnFilenamesRead, weak_ptr_factory_.GetWeakPtr(),
- endpoint_type, writer, data_read_callback),
- DataSource::ReadFileContentsDataCallback(),
- base::BindOnce(&Seat::OnWebCustomDataRead, weak_ptr_factory_.GetWeakPtr(),
- writer, data_read_callback),
- data_read_callback);
- }
- class Seat::RefCountedScopedClipboardWriter
- : public ui::ScopedClipboardWriter,
- public base::RefCounted<RefCountedScopedClipboardWriter> {
- public:
- explicit RefCountedScopedClipboardWriter(ui::EndpointType type)
- : ScopedClipboardWriter(
- ui::ClipboardBuffer::kCopyPaste,
- std::make_unique<ui::DataTransferEndpoint>(type)) {}
- private:
- friend class base::RefCounted<RefCountedScopedClipboardWriter>;
- virtual ~RefCountedScopedClipboardWriter() = default;
- };
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- void Seat::OnDataTransferEndpointRead(
- scoped_refptr<RefCountedScopedClipboardWriter> writer,
- base::OnceClosure callback,
- const std::string& mime_type,
- std::u16string data) {
- std::string utf8_json = base::UTF16ToUTF8(data);
- auto clipboard_source = ui::ConvertJsonToDataTransferEndpoint(utf8_json);
- writer->SetDataSource(std::move(clipboard_source));
- std::move(callback).Run();
- }
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- void Seat::OnTextRead(scoped_refptr<RefCountedScopedClipboardWriter> writer,
- base::OnceClosure callback,
- const std::string& mime_type,
- std::u16string data) {
- writer->WriteText(std::move(data));
- std::move(callback).Run();
- }
- void Seat::OnRTFRead(scoped_refptr<RefCountedScopedClipboardWriter> writer,
- base::OnceClosure callback,
- const std::string& mime_type,
- const std::vector<uint8_t>& data) {
- writer->WriteRTF(
- std::string(reinterpret_cast<const char*>(data.data()), data.size()));
- std::move(callback).Run();
- }
- void Seat::OnHTMLRead(scoped_refptr<RefCountedScopedClipboardWriter> writer,
- base::OnceClosure callback,
- const std::string& mime_type,
- std::u16string data) {
- writer->WriteHTML(std::move(data), std::string());
- std::move(callback).Run();
- }
- void Seat::OnImageRead(scoped_refptr<RefCountedScopedClipboardWriter> writer,
- base::OnceClosure callback,
- const std::string& mime_type,
- const std::vector<uint8_t>& data) {
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- data_decoder::DecodeImageIsolated(
- data, data_decoder::mojom::ImageCodec::kDefault, false,
- std::numeric_limits<int64_t>::max(), gfx::Size(),
- base::BindOnce(&Seat::OnImageDecoded, weak_ptr_factory_.GetWeakPtr(),
- std::move(callback), writer));
- #else
- std::move(callback).Run();
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- }
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- void Seat::OnImageDecoded(base::OnceClosure callback,
- scoped_refptr<RefCountedScopedClipboardWriter> writer,
- const SkBitmap& bitmap) {
- if (!bitmap.isNull() && !bitmap.empty())
- writer->WriteImage(bitmap);
- std::move(callback).Run();
- }
- #endif // BUILDFLAG(IS_CHROMEOS_ASH)
- void Seat::OnFilenamesRead(
- ui::EndpointType source,
- scoped_refptr<RefCountedScopedClipboardWriter> writer,
- base::OnceClosure callback,
- const std::string& mime_type,
- const std::vector<uint8_t>& data) {
- std::vector<ui::FileInfo> filenames =
- data_exchange_delegate_->GetFilenames(source, data);
- writer->WriteFilenames(ui::FileInfosToURIList(filenames));
- std::move(callback).Run();
- }
- void Seat::OnWebCustomDataRead(
- scoped_refptr<RefCountedScopedClipboardWriter> writer,
- base::OnceClosure callback,
- const std::string& mime_type,
- const std::vector<uint8_t>& data) {
- base::Pickle pickle(reinterpret_cast<const char*>(data.data()), data.size());
- writer->WritePickledData(pickle,
- ui::ClipboardFormatType::WebCustomDataType());
- std::move(callback).Run();
- }
- void Seat::OnAllReadsFinished(
- scoped_refptr<RefCountedScopedClipboardWriter> writer) {
- // We need to destroy the ScopedClipboardWriter in this call, before
- // |auto_reset| is destroyed, so if there are outstanding references that
- // would prevent that, reschedule this task.
- if (!writer->HasOneRef()) {
- base::ThreadTaskRunnerHandle::Get()->PostTask(
- FROM_HERE,
- base::BindOnce(&Seat::OnAllReadsFinished,
- weak_ptr_factory_.GetWeakPtr(), std::move(writer)));
- return;
- }
- base::AutoReset<bool> auto_reset(
- &changing_clipboard_data_to_selection_source_, true);
- writer.reset();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // aura::client::FocusChangeObserver overrides:
- void Seat::OnWindowFocused(aura::Window* gained_focus,
- aura::Window* lost_focus) {
- Surface* const gaining_focus_surface =
- GetTargetSurfaceForKeyboardFocus(gained_focus);
- Surface* const lost_focus_surface =
- GetTargetSurfaceForKeyboardFocus(lost_focus);
- for (auto& observer_list : priority_observer_list_) {
- for (auto& observer : observer_list)
- observer.OnSurfaceFocused(gaining_focus_surface, lost_focus_surface,
- !!gained_focus);
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ui::PlatformEventObserver overrides:
- void Seat::WillProcessEvent(const ui::PlatformEvent& event) {
- switch (ui::EventTypeFromNative(event)) {
- case ui::ET_KEY_PRESSED:
- case ui::ET_KEY_RELEASED:
- physical_code_for_currently_processing_event_ = ui::CodeFromNative(event);
- break;
- default:
- break;
- }
- }
- void Seat::DidProcessEvent(const ui::PlatformEvent& event) {
- switch (ui::EventTypeFromNative(event)) {
- case ui::ET_KEY_PRESSED:
- physical_code_for_currently_processing_event_ = ui::DomCode::NONE;
- break;
- case ui::ET_KEY_RELEASED:
- // Remove this from the pressed key map because when IME is active we can
- // end up getting the DidProcessEvent call before we get the OnKeyEvent
- // callback and then the key will end up being stuck pressed.
- if (physical_code_for_currently_processing_event_ != ui::DomCode::NONE) {
- pressed_keys_.erase(physical_code_for_currently_processing_event_);
- physical_code_for_currently_processing_event_ = ui::DomCode::NONE;
- }
- break;
- default:
- break;
- }
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ui::EventHandler overrides:
- void Seat::OnKeyEvent(ui::KeyEvent* event) {
- // Ignore synthetic key repeat events.
- if (event->is_repeat())
- return;
- if (physical_code_for_currently_processing_event_ != ui::DomCode::NONE) {
- switch (event->type()) {
- case ui::ET_KEY_PRESSED:
- pressed_keys_.emplace(
- physical_code_for_currently_processing_event_,
- KeyState{event->code(), /*consumed_by_ime=*/false});
- break;
- case ui::ET_KEY_RELEASED:
- pressed_keys_.erase(physical_code_for_currently_processing_event_);
- break;
- default:
- NOTREACHED();
- break;
- }
- }
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- xkb_tracker_->UpdateKeyboardModifiers(event->flags());
- for (auto& observer_list : priority_observer_list_) {
- for (auto& observer : observer_list)
- observer.OnKeyboardModifierUpdated();
- }
- #endif
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ui::ClipboardObserver overrides:
- void Seat::OnClipboardDataChanged() {
- if (!selection_source_ || changing_clipboard_data_to_selection_source_)
- return;
- selection_source_->get()->Cancelled();
- selection_source_.reset();
- }
- #if BUILDFLAG(IS_CHROMEOS_ASH)
- UILockController* Seat::GetUILockControllerForTesting() {
- return ui_lock_controller_.get();
- }
- ////////////////////////////////////////////////////////////////////////////////
- // ash::ImeControllerImpl::Observer overrides:
- void Seat::OnCapsLockChanged(bool enabled) {}
- void Seat::OnKeyboardLayoutNameChanged(const std::string& layout_name) {
- xkb_tracker_->UpdateKeyboardLayout(layout_name);
- }
- #endif
- ////////////////////////////////////////////////////////////////////////////////
- // DataSourceObserver overrides:
- void Seat::OnDataSourceDestroying(DataSource* source) {
- if (selection_source_ && selection_source_->get() == source)
- selection_source_.reset();
- }
- } // namespace exo
|