Handle selection data source comes from clients.
The CL partially implements the data flow of setting clipboard data from
exo clients.
1. DataDevice receives a selection event with DataSource from the client
2. Seat requests DataSource for bytes, updates the clipboard with
obtained bytes, and keeps holding DataSource instance to cancel it when
new clipboard data is set.
Follwing CL implements the DataSource part which actually reads bytes
from FD.
Bug: 773978
Test: exo_unittests
Change-Id: I0cde5755dd1ecd28d24a5b17f90590b9a70d4773
Reviewed-on: https://chromium-review.googlesource.com/792410
Commit-Queue: Daichi Hirono <[email protected]>
Reviewed-by: David Reveman <[email protected]>
Cr-Commit-Position: refs/heads/master@{#520420}
diff --git a/components/exo/BUILD.gn b/components/exo/BUILD.gn
index b99ab1a6..65e6b3f 100644
--- a/components/exo/BUILD.gn
+++ b/components/exo/BUILD.gn
@@ -20,6 +20,7 @@
"data_source.cc",
"data_source.h",
"data_source_delegate.h",
+ "data_source_observer.h",
"display.cc",
"display.h",
"gaming_seat.cc",
diff --git a/components/exo/data_device.cc b/components/exo/data_device.cc
index e54a637..8586254 100644
--- a/components/exo/data_device.cc
+++ b/components/exo/data_device.cc
@@ -77,9 +77,9 @@
NOTIMPLEMENTED();
}
-void DataDevice::SetSelection(const DataSource* source, uint32_t serial) {
+void DataDevice::SetSelection(DataSource* source, uint32_t serial) {
// TODO(hirono): Check if serial is valid. crbug.com/746111
- NOTIMPLEMENTED();
+ seat_->SetSelection(source);
}
void DataDevice::OnDragEntered(const ui::DropTargetEvent& event) {
@@ -148,7 +148,7 @@
void DataDevice::OnClipboardDataChanged() {
if (!focused_surface_)
return;
- SendSelection();
+ SetSelectionToCurrentClipboardData();
}
void DataDevice::OnSurfaceFocusing(Surface* surface) {
@@ -167,7 +167,7 @@
// Check if the client newly obtained focus.
if (focused_surface_ && !last_focused_surface)
- SendSelection();
+ SetSelectionToCurrentClipboardData();
}
void DataDevice::OnSurfaceFocused(Surface* surface) {}
@@ -194,7 +194,7 @@
return delegate_->CanAcceptDataEventsForSurface(target) ? target : nullptr;
}
-void DataDevice::SendSelection() {
+void DataDevice::SetSelectionToCurrentClipboardData() {
DataOffer* data_offer = delegate_->OnDataOffer();
data_offer->SetClipboardData(file_helper_,
*ui::Clipboard::GetForCurrentThread());
diff --git a/components/exo/data_device.h b/components/exo/data_device.h
index ca73948..66ecaa6 100644
--- a/components/exo/data_device.h
+++ b/components/exo/data_device.h
@@ -59,7 +59,7 @@
// Sets selection data to the clipboard.
// |source| represents data comes from the client. |serial| is the unique
// number comes from input events which triggers the drag and drop operation.
- void SetSelection(const DataSource* source, uint32_t serial);
+ void SetSelection(DataSource* source, uint32_t serial);
// Overridden from WMHelper::DragDropObserver:
void OnDragEntered(const ui::DropTargetEvent& event) override;
@@ -82,7 +82,7 @@
private:
Surface* GetEffectiveTargetForEvent(const ui::DropTargetEvent& event) const;
- void SendSelection();
+ void SetSelectionToCurrentClipboardData();
DataDeviceDelegate* const delegate_;
Seat* const seat_;
diff --git a/components/exo/data_source.cc b/components/exo/data_source.cc
index e70ddfa..94f48f0 100644
--- a/components/exo/data_source.cc
+++ b/components/exo/data_source.cc
@@ -5,13 +5,35 @@
#include "components/exo/data_source.h"
#include "components/exo/data_source_delegate.h"
+#include "components/exo/data_source_observer.h"
namespace exo {
+ScopedDataSource::ScopedDataSource(DataSource* data_source,
+ DataSourceObserver* observer)
+ : data_source_(data_source), observer_(observer) {
+ data_source_->AddObserver(observer_);
+}
+
+ScopedDataSource::~ScopedDataSource() {
+ data_source_->RemoveObserver(observer_);
+}
+
DataSource::DataSource(DataSourceDelegate* delegate) : delegate_(delegate) {}
DataSource::~DataSource() {
delegate_->OnDataSourceDestroying(this);
+ for (DataSourceObserver& observer : observers_) {
+ observer.OnDataSourceDestroying(this);
+ }
+}
+
+void DataSource::AddObserver(DataSourceObserver* observer) {
+ observers_.AddObserver(observer);
+}
+
+void DataSource::RemoveObserver(DataSourceObserver* observer) {
+ observers_.RemoveObserver(observer);
}
void DataSource::Offer(const std::string& mime_type) {
@@ -22,4 +44,12 @@
NOTIMPLEMENTED();
}
+void DataSource::Cancelled() {
+ NOTIMPLEMENTED();
+}
+
+void DataSource::ReadData(ReadDataCallback callback) {
+ NOTIMPLEMENTED();
+}
+
} // namespace exo
diff --git a/components/exo/data_source.h b/components/exo/data_source.h
index 1e253d1..812e6b8 100644
--- a/components/exo/data_source.h
+++ b/components/exo/data_source.h
@@ -7,12 +7,15 @@
#include <string>
+#include "base/callback.h"
#include "base/containers/flat_set.h"
#include "base/macros.h"
+#include "base/observer_list.h"
namespace exo {
class DataSourceDelegate;
+class DataSourceObserver;
enum class DndAction;
// Object representing transferred data offered by a client.
@@ -21,6 +24,9 @@
explicit DataSource(DataSourceDelegate* delegate);
~DataSource();
+ void AddObserver(DataSourceObserver* observer);
+ void RemoveObserver(DataSourceObserver* observer);
+
// Notifies to DataSource that the client offers new mime type.
void Offer(const std::string& mime_type);
@@ -28,12 +34,37 @@
// DataSource.
void SetActions(const base::flat_set<DndAction>& dnd_actions);
+ // Notifies the data source is cancelled. e.g. Replaced with another data
+ // source.
+ void Cancelled();
+
+ // Reads data from the source. Then |callback| is invoked with read
+ // data. If Cancelled() is invoked or DataSource is destroyed before
+ // completion, the callback is never called.
+ using ReadDataCallback =
+ base::OnceCallback<void(const std::vector<uint8_t>&)>;
+ void ReadData(ReadDataCallback callback);
+
private:
DataSourceDelegate* const delegate_;
+ base::ObserverList<DataSourceObserver> observers_;
DISALLOW_COPY_AND_ASSIGN(DataSource);
};
+class ScopedDataSource {
+ public:
+ ScopedDataSource(DataSource* data_source, DataSourceObserver* observer);
+ ~ScopedDataSource();
+ DataSource* get() { return data_source_; }
+
+ private:
+ DataSource* const data_source_;
+ DataSourceObserver* const observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(ScopedDataSource);
+};
+
} // namespace exo
#endif // COMPONENTS_EXO_DATA_SOURCE_H_
diff --git a/components/exo/data_source_observer.h b/components/exo/data_source_observer.h
new file mode 100644
index 0000000..43fc329
--- /dev/null
+++ b/components/exo/data_source_observer.h
@@ -0,0 +1,27 @@
+// 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.
+
+#ifndef COMPONENTS_EXO_DATA_SOURCE_OBSERVER_H_
+#define COMPONENTS_EXO_DATA_SOURCE_OBSERVER_H_
+
+#include <string>
+
+namespace exo {
+
+class DataSource;
+
+// Handles events on data devices in context-specific ways.
+class DataSourceObserver {
+ public:
+ // Called at the top of the data device's destructor, to give observers a
+ // chance to remove themselves.
+ virtual void OnDataSourceDestroying(DataSource* source) = 0;
+
+ protected:
+ virtual ~DataSourceObserver() {}
+};
+
+} // namespace exo
+
+#endif // COMPONENTS_EXO_DATA_SOURCE_OBSERVER_H_
diff --git a/components/exo/seat.cc b/components/exo/seat.cc
index 21eb9f3..b8d7f590 100644
--- a/components/exo/seat.cc
+++ b/components/exo/seat.cc
@@ -5,11 +5,16 @@
#include "components/exo/seat.h"
#include "ash/shell.h"
+#include "base/auto_reset.h"
+#include "base/strings/utf_string_conversions.h"
+#include "components/exo/data_source.h"
#include "components/exo/keyboard.h"
#include "components/exo/shell_surface.h"
#include "components/exo/surface.h"
#include "components/exo/wm_helper.h"
#include "ui/aura/client/focus_client.h"
+#include "ui/base/clipboard/clipboard_monitor.h"
+#include "ui/base/clipboard/scoped_clipboard_writer.h"
namespace exo {
namespace {
@@ -29,16 +34,19 @@
} // namespace
-Seat::Seat() {
+Seat::Seat() : changing_clipboard_data_to_selection_source_(false) {
aura::client::GetFocusClient(ash::Shell::Get()->GetPrimaryRootWindow())
->AddObserver(this);
WMHelper::GetInstance()->AddPreTargetHandler(this);
+ ui::ClipboardMonitor::GetInstance()->AddObserver(this);
}
Seat::~Seat() {
+ DCHECK(!selection_source_) << "DataSource must be released before Seat";
aura::client::GetFocusClient(ash::Shell::Get()->GetPrimaryRootWindow())
->RemoveObserver(this);
WMHelper::GetInstance()->RemovePreTargetHandler(this);
+ ui::ClipboardMonitor::GetInstance()->RemoveObserver(this);
}
void Seat::AddObserver(SeatObserver* observer) {
@@ -53,6 +61,30 @@
return GetEffectiveFocus(WMHelper::GetInstance()->GetFocusedWindow());
}
+void Seat::SetSelection(DataSource* source) {
+ DCHECK(source);
+
+ if (selection_source_) {
+ if (selection_source_->get() == source)
+ return;
+ selection_source_->get()->Cancelled();
+ }
+
+ selection_source_ = std::make_unique<ScopedDataSource>(source, this);
+
+ // Unretained is safe as Seat always outlives DataSource.
+ source->ReadData(base::Bind(&Seat::OnDataRead, base::Unretained(this)));
+}
+
+void Seat::OnDataRead(const std::vector<uint8_t>& data) {
+ base::AutoReset<bool> auto_reset(
+ &changing_clipboard_data_to_selection_source_, true);
+
+ ui::ScopedClipboardWriter writer(ui::CLIPBOARD_TYPE_COPY_PASTE);
+ writer.WriteText(base::UTF8ToUTF16(base::StringPiece(
+ reinterpret_cast<const char*>(data.data()), data.size())));
+}
+
////////////////////////////////////////////////////////////////////////////////
// aura::client::FocusChangeObserver overrides:
@@ -84,4 +116,22 @@
}
}
+////////////////////////////////////////////////////////////////////////////////
+// ui::ClipboardObserver overrides:
+
+void Seat::OnClipboardDataChanged() {
+ if (!selection_source_ || changing_clipboard_data_to_selection_source_)
+ return;
+ selection_source_->get()->Cancelled();
+ selection_source_.reset();
+}
+
+////////////////////////////////////////////////////////////////////////////////
+// DataSourceObserver overrides:
+
+void Seat::OnDataSourceDestroying(DataSource* source) {
+ if (selection_source_ && selection_source_->get() == source)
+ selection_source_.reset();
+}
+
} // namespace exo
diff --git a/components/exo/seat.h b/components/exo/seat.h
index 3e359223..f247a43 100644
--- a/components/exo/seat.h
+++ b/components/exo/seat.h
@@ -6,9 +6,12 @@
#define COMPONENTS_EXO_SEAT_H_
#include "base/containers/flat_set.h"
+#include "base/memory/weak_ptr.h"
#include "base/observer_list.h"
+#include "components/exo/data_source_observer.h"
#include "ui/aura/client/drag_drop_delegate.h"
#include "ui/aura/client/focus_change_observer.h"
+#include "ui/base/clipboard/clipboard_observer.h"
#include "ui/events/event_handler.h"
namespace ui {
@@ -17,12 +20,16 @@
} // namespace ui
namespace exo {
+class ScopedDataSource;
class SeatObserver;
class Surface;
// Seat object represent a group of input devices such as keyboard, pointer and
// touch devices and keeps track of input focus.
-class Seat : public aura::client::FocusChangeObserver, public ui::EventHandler {
+class Seat : public aura::client::FocusChangeObserver,
+ public ui::EventHandler,
+ public ui::ClipboardObserver,
+ public DataSourceObserver {
public:
Seat();
~Seat() override;
@@ -39,6 +46,9 @@
return pressed_keys_;
}
+ // Sets clipboard data from |source|.
+ void SetSelection(DataSource* source);
+
// Overridden from aura::client::FocusChangeObserver:
void OnWindowFocused(aura::Window* gained_focus,
aura::Window* lost_focus) override;
@@ -46,10 +56,27 @@
// Overridden from ui::EventHandler:
void OnKeyEvent(ui::KeyEvent* event) override;
+ // Overridden from ui::ClipboardObserver:
+ void OnClipboardDataChanged() override;
+
+ // Overridden from DataSourceObserver:
+ void OnDataSourceDestroying(DataSource* source) override;
+
private:
+ // Called when data is read from FD passed from a client.
+ // |data| is read data. |source| is source of the data, or nullptr if
+ // DataSource has already been destroyed.
+ void OnDataRead(const std::vector<uint8_t>& data);
+
base::ObserverList<SeatObserver> observers_;
base::flat_set<ui::DomCode> pressed_keys_;
+ // Data source being used as a clipboard content.
+ std::unique_ptr<ScopedDataSource> selection_source_;
+
+ // True while Seat is updating clipboard data to selection source.
+ bool changing_clipboard_data_to_selection_source_;
+
DISALLOW_COPY_AND_ASSIGN(Seat);
};