[server] Ensure we have a DataSource on the DataDevice in setSelection

Summary:
SeatInterface provides a way to set the current selection. This method
did not verify whether the new DataDeviceInterface actually has a
DataSourceInterface. If there is no DataSourceInterface on that
DataDeviceInterface the selection should not be sent to the current
selection owner. This results in a crash as DataOfferInterface
(correctly) doesn't expect the passed in DataSourceInterface to be null.

To ensure we don't hit this again the DataOfferInterface ctor gained an
Q_ASSERT to validate the DataSourceInterface.

Reviewers: #plasma_on_wayland

Subscribers: plasma-devel

Tags: #plasma_on_wayland

Differential Revision: https://phabricator.kde.org/D3148
This commit is contained in:
Martin Gräßlin 2016-10-24 10:04:33 +02:00
parent a037a0cb4f
commit 988a239637
3 changed files with 48 additions and 1 deletions

View file

@ -79,6 +79,7 @@ private Q_SLOTS:
void testCast();
void testDestroy();
void testSelection();
void testSelectionNoDataSource();
void testTouch();
void testDisconnect();
void testPointerEnterOnUnboundSurface();
@ -1552,6 +1553,51 @@ void TestWaylandSeat::testSelection()
QVERIFY(cancelledSpy.wait());
}
void TestWaylandSeat::testSelectionNoDataSource()
{
// this test verifies that the server doesn't crash when using setSelection with
// a DataDevice which doesn't have a DataSource yet
using namespace KWayland::Client;
using namespace KWayland::Server;
// first create the DataDevice
QScopedPointer<DataDeviceManagerInterface> ddmi(m_display->createDataDeviceManager());
ddmi->create();
QSignalSpy ddiCreatedSpy(ddmi.data(), &DataDeviceManagerInterface::dataDeviceCreated);
QVERIFY(ddiCreatedSpy.isValid());
Registry registry;
QSignalSpy dataDeviceManagerSpy(&registry, &Registry::dataDeviceManagerAnnounced);
QVERIFY(dataDeviceManagerSpy.isValid());
registry.setEventQueue(m_queue);
registry.create(m_connection->display());
QVERIFY(registry.isValid());
registry.setup();
QVERIFY(dataDeviceManagerSpy.wait());
QScopedPointer<DataDeviceManager> ddm(registry.createDataDeviceManager(dataDeviceManagerSpy.first().first().value<quint32>(),
dataDeviceManagerSpy.first().last().value<quint32>()));
QVERIFY(ddm->isValid());
QScopedPointer<DataDevice> dd(ddm->getDataDevice(m_seat));
QVERIFY(dd->isValid());
QVERIFY(ddiCreatedSpy.wait());
auto ddi = ddiCreatedSpy.first().first().value<DataDeviceInterface*>();
QVERIFY(ddi);
// now create a surface and pass it keyboard focus
QSignalSpy surfaceCreatedSpy(m_compositorInterface, &CompositorInterface::surfaceCreated);
QVERIFY(surfaceCreatedSpy.isValid());
QScopedPointer<Surface> surface(m_compositor->createSurface());
QVERIFY(surface->isValid());
QVERIFY(surfaceCreatedSpy.wait());
auto serverSurface = surfaceCreatedSpy.first().first().value<SurfaceInterface*>();
QVERIFY(!m_seatInterface->selection());
m_seatInterface->setFocusedKeyboardSurface(serverSurface);
QCOMPARE(m_seatInterface->focusedKeyboardSurface(), serverSurface);
// now let's set the selection
m_seatInterface->setSelection(ddi);
}
void TestWaylandSeat::testTouch()
{
using namespace KWayland::Client;

View file

@ -93,6 +93,7 @@ void DataOfferInterface::Private::receive(const QString &mimeType, qint32 fd)
DataOfferInterface::DataOfferInterface(DataSourceInterface *source, DataDeviceInterface *parentInterface, wl_resource *parentResource)
: Resource(new Private(source, parentInterface, this, parentResource))
{
Q_ASSERT(source);
connect(source, &DataSourceInterface::mimeTypeOffered, this,
[this](const QString &mimeType) {
Q_D();

View file

@ -1344,7 +1344,7 @@ void SeatInterface::setSelection(DataDeviceInterface *dataDevice)
d->cancelPreviousSelection(dataDevice);
d->currentSelection = dataDevice;
if (d->keys.focus.selection) {
if (dataDevice) {
if (dataDevice && dataDevice->selection()) {
d->keys.focus.selection->sendSelection(dataDevice);
} else {
d->keys.focus.selection->sendClearSelection();