wayland: send dndFinished to source if target fails to do so

After receiving a drop a client should call data_offer.finish
to tell the source it's done using the drop.

A client could delete an offer after drop before calling finish.

This can happen with misbehaving/buggy or malicious Wayland clients.
A real case was found in the wild with Chromium, as described in the
linked bug.

In this situation we should let the source know the dnd is finished
as there are no other transfers than can take place.

We don't want to universally send this in all data offer destructors
only, offers that are deleted post drop so the flag on the source is
exposed.

BUG: 482142
wilder/Plasma/6.2
David Edmundson 2 years ago
parent 7710b44f1d
commit 711c5bb43f
  1. 24
      src/wayland/abstract_data_source.h
  2. 9
      src/wayland/dataoffer.cpp
  3. 15
      src/wayland/datasource.cpp
  4. 3
      src/wayland/datasource.h

@ -56,11 +56,16 @@ public:
/**
* The user performed the drop action during a drag and drop operation.
*/
virtual void dropPerformed(){};
virtual void dropPerformed()
{
m_dndDropped = true;
}
/**
* The drop destination finished interoperating with this data source.
*/
virtual void dndFinished(){};
virtual void dndFinished()
{
}
/**
* This event indicates the @p action selected by the compositor after matching the
* source/destination side actions. Only one action (or none) will be offered here.
@ -69,11 +74,22 @@ public:
{
};
bool isDndCancelled() const
{
return m_dndCancelled;
}
bool isDropPerformed() const
{
return m_dndDropped;
}
/**
* Called when a user stops clicking but it is not accepted by a client.
*/
virtual void dndCancelled()
{
m_dndCancelled = true;
}
virtual wl_client *client() const
@ -89,6 +105,10 @@ Q_SIGNALS:
protected:
explicit AbstractDataSource(QObject *parent = nullptr);
private:
bool m_dndCancelled = false;
bool m_dndDropped = false;
};
}

@ -77,6 +77,7 @@ void DataOfferInterfacePrivate::data_offer_finish(Resource *resource)
return;
}
source->dndFinished();
source.clear();
// TODO: It is a client error to perform other requests than wl_data_offer.destroy after this one
}
@ -163,7 +164,13 @@ DataOfferInterface::DataOfferInterface(AbstractDataSource *source, wl_resource *
});
}
DataOfferInterface::~DataOfferInterface() = default;
DataOfferInterface::~DataOfferInterface()
{
if (d->source && d->source->isDropPerformed()) {
d->source->dndFinished();
d->source.clear();
}
}
void DataOfferInterface::sendAllOffers()
{

@ -129,7 +129,7 @@ DataDeviceManagerInterface::DnDAction DataSourceInterface::selectedDndAction() c
void DataSourceInterface::dropPerformed()
{
d->dropPerformed = true;
AbstractDataSource::dropPerformed();
if (d->resource()->version() < WL_DATA_SOURCE_DND_DROP_PERFORMED_SINCE_VERSION) {
return;
}
@ -138,17 +138,13 @@ void DataSourceInterface::dropPerformed()
void DataSourceInterface::dndFinished()
{
AbstractDataSource::dndFinished();
if (d->resource()->version() < WL_DATA_SOURCE_DND_FINISHED_SINCE_VERSION) {
return;
}
d->send_dnd_finished();
}
bool DataSourceInterface::isDropPerformed() const
{
return d->dropPerformed;
}
void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action)
{
d->selectedDndAction = action;
@ -169,7 +165,7 @@ void DataSourceInterface::dndAction(DataDeviceManagerInterface::DnDAction action
void DataSourceInterface::dndCancelled()
{
d->isCanceled = true;
AbstractDataSource::dndCancelled();
// for v3 or less, cancel should not be called after a failed drag operation
if (wl_resource_get_version(resource()) < 3) {
return;
@ -177,11 +173,6 @@ void DataSourceInterface::dndCancelled()
d->send_cancelled();
}
bool DataSourceInterface::isDndCancelled() const
{
return d->isCanceled;
}
wl_resource *DataSourceInterface::resource() const
{
return d->resource()->handle;

@ -43,9 +43,6 @@ public:
void dndAction(DataDeviceManagerInterface::DnDAction action) override;
void dndCancelled() override;
bool isDndCancelled() const;
bool isDropPerformed() const;
wl_resource *resource() const;
wl_client *client() const override;

Loading…
Cancel
Save