summaryrefslogtreecommitdiff
path: root/mozilla-1438131.patch
diff options
context:
space:
mode:
Diffstat (limited to 'mozilla-1438131.patch')
-rw-r--r--mozilla-1438131.patch1558
1 files changed, 1558 insertions, 0 deletions
diff --git a/mozilla-1438131.patch b/mozilla-1438131.patch
new file mode 100644
index 0000000..e77186d
--- /dev/null
+++ b/mozilla-1438131.patch
@@ -0,0 +1,1558 @@
+
+# HG changeset patch
+# User Martin Stransky <stransky@redhat.com>
+# Date 1522937803 -7200
+# Node ID 7e4166e13b3ec513ef3003527df601adf85f654b
+# Parent bf4962739d38ac21ba6ba216fa61f572e7976354
+Bug 1438131 - Implement Drop on Wayland, r=jhorak
+
+This patch implements Drop operation on Wayland/Gtk+. That's because drop operations are part
+of clipboard on Wayland and we use our own paste clipboard handler on Wayland (Bug 1282015).
+
+Wayland drop data are provided by wl_data_device_listener, it provides us drag and drop callbacks
+which we route to nsDragService module.
+
+MozReview-Commit-ID: 9uGYPg9YF6P
+
+diff --git a/widget/gtk/nsClipboardWayland.cpp b/widget/gtk/nsClipboardWayland.cpp
+--- a/widget/gtk/nsClipboardWayland.cpp
++++ b/widget/gtk/nsClipboardWayland.cpp
+@@ -18,16 +18,17 @@
+ #include "nsPrimitiveHelpers.h"
+ #include "nsIServiceManager.h"
+ #include "nsImageToPixbuf.h"
+ #include "nsStringStream.h"
+ #include "nsIObserverService.h"
+ #include "mozilla/Services.h"
+ #include "mozilla/RefPtr.h"
+ #include "mozilla/TimeStamp.h"
++#include "nsDragService.h"
+
+ #include "imgIContainer.h"
+
+ #include <gtk/gtk.h>
+ #include <poll.h>
+ #include <sys/epoll.h>
+ #include <stdlib.h>
+ #include <string.h>
+@@ -41,16 +42,54 @@
+ const char*
+ nsRetrievalContextWayland::sTextMimeTypes[TEXT_MIME_TYPES_NUM] =
+ {
+ "text/plain;charset=utf-8",
+ "UTF8_STRING",
+ "COMPOUND_TEXT"
+ };
+
++static inline GdkDragAction
++wl_to_gdk_actions(uint32_t dnd_actions)
++{
++ GdkDragAction actions = GdkDragAction(0);
++
++ if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY)
++ actions = GdkDragAction(actions|GDK_ACTION_COPY);
++ if (dnd_actions & WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE)
++ actions = GdkDragAction(actions|GDK_ACTION_MOVE);
++
++ return actions;
++}
++
++static inline uint32_t
++gdk_to_wl_actions(GdkDragAction action)
++{
++ uint32_t dnd_actions = 0;
++
++ if (action & (GDK_ACTION_COPY | GDK_ACTION_LINK | GDK_ACTION_PRIVATE))
++ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY;
++ if (action & GDK_ACTION_MOVE)
++ dnd_actions |= WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
++
++ return dnd_actions;
++}
++
++static GtkWidget*
++get_gtk_widget_for_wl_surface(struct wl_surface *surface)
++{
++ GdkWindow *gdkParentWindow =
++ static_cast<GdkWindow*>(wl_surface_get_user_data(surface));
++
++ gpointer user_data = nullptr;
++ gdk_window_get_user_data(gdkParentWindow, &user_data);
++
++ return GTK_WIDGET(user_data);
++}
++
+ void
+ DataOffer::AddMIMEType(const char *aMimeType)
+ {
+ GdkAtom atom = gdk_atom_intern(aMimeType, FALSE);
+ mTargetMIMETypes.AppendElement(atom);
+ }
+
+ GdkAtom*
+@@ -150,44 +189,99 @@ WaylandDataOffer::RequestDataTransfer(co
+ if (mWaylandDataOffer) {
+ wl_data_offer_receive(mWaylandDataOffer, aMimeType, fd);
+ return true;
+ }
+
+ return false;
+ }
+
++void
++WaylandDataOffer::DragOfferAccept(const char* aMimeType, uint32_t aTime)
++{
++ wl_data_offer_accept(mWaylandDataOffer, aTime, aMimeType);
++}
++
++/* We follow logic of gdk_wayland_drag_context_commit_status()/gdkdnd-wayland.c
++ * here.
++ */
++void
++WaylandDataOffer::SetDragStatus(GdkDragAction aAction, uint32_t aTime)
++{
++ uint32_t dnd_actions = gdk_to_wl_actions(aAction);
++ uint32_t all_actions = WL_DATA_DEVICE_MANAGER_DND_ACTION_COPY |
++ WL_DATA_DEVICE_MANAGER_DND_ACTION_MOVE;
++
++ wl_data_offer_set_actions(mWaylandDataOffer, all_actions, dnd_actions);
++
++ /* Workaround Wayland D&D architecture here. To get the data_device_drop()
++ signal (which routes to nsDragService::GetData() call) we need to
++ accept at least one mime type before data_device_leave().
++
++ Real wl_data_offer_accept() for actualy requested data mime type is
++ called from nsDragService::GetData().
++ */
++ if (mTargetMIMETypes[0]) {
++ wl_data_offer_accept(mWaylandDataOffer, aTime,
++ gdk_atom_name(mTargetMIMETypes[0]));
++ }
++}
++
++void
++WaylandDataOffer::SetSelectedDragAction(uint32_t aWaylandAction)
++{
++ mSelectedDragAction = aWaylandAction;
++}
++
++GdkDragAction
++WaylandDataOffer::GetSelectedDragAction()
++{
++ return wl_to_gdk_actions(mSelectedDragAction);
++}
++
+ static void
+ data_offer_offer (void *data,
+ struct wl_data_offer *wl_data_offer,
+ const char *type)
+ {
+ auto *offer = static_cast<DataOffer*>(data);
+ offer->AddMIMEType(type);
+ }
+
++/* Advertise all available drag and drop actions from source.
++ * We don't use that but follow gdk_wayland_drag_context_commit_status()
++ * from gdkdnd-wayland.c here.
++ */
+ static void
+ data_offer_source_actions(void *data,
+ struct wl_data_offer *wl_data_offer,
+ uint32_t source_actions)
+ {
+ }
+
++/* Advertise recently selected drag and drop action by compositor, based
++ * on source actions and user choice (key modifiers, etc.).
++ */
+ static void
+ data_offer_action(void *data,
+ struct wl_data_offer *wl_data_offer,
+ uint32_t dnd_action)
+ {
++ auto *offer = static_cast<WaylandDataOffer*>(data);
++ offer->SetSelectedDragAction(dnd_action);
+ }
+
+ /* wl_data_offer callback description:
+ *
+ * data_offer_offer - Is called for each MIME type available at wl_data_offer.
+- * data_offer_source_actions - Exposes all available D&D actions.
+- * data_offer_action - Expose one actually selected D&D action.
++ * data_offer_source_actions - This event indicates the actions offered by
++ * the data source.
++ * data_offer_action - This event indicates the action selected by
++ * the compositor after matching the source/destination
++ * side actions.
+ */
+ static const struct wl_data_offer_listener data_offer_listener = {
+ data_offer_offer,
+ data_offer_source_actions,
+ data_offer_action
+ };
+
+ WaylandDataOffer::WaylandDataOffer(wl_data_offer* aWaylandDataOffer)
+@@ -241,92 +335,203 @@ PrimaryDataOffer::PrimaryDataOffer(gtk_p
+
+ PrimaryDataOffer::~PrimaryDataOffer(void)
+ {
+ if(mPrimaryDataOffer) {
+ gtk_primary_selection_offer_destroy(mPrimaryDataOffer);
+ }
+ }
+
++NS_IMPL_ISUPPORTS(nsWaylandDragContext, nsISupports);
++
++nsWaylandDragContext::nsWaylandDragContext(WaylandDataOffer* aDataOffer,
++ wl_display *aDisplay)
++ : mDataOffer(aDataOffer)
++ , mDisplay(aDisplay)
++ , mTime(0)
++ , mGtkWidget(nullptr)
++ , mX(0)
++ , mY(0)
++{
++}
++
+ void
+-nsRetrievalContextWayland::RegisterDataOffer(wl_data_offer *aWaylandDataOffer)
++nsWaylandDragContext::DropDataEnter(GtkWidget* aGtkWidget, uint32_t aTime,
++ nscoord aX, nscoord aY)
++{
++ mTime = aTime;
++ mGtkWidget = aGtkWidget;
++ mX = aX;
++ mY = aY;
++}
++
++void
++nsWaylandDragContext::DropMotion(uint32_t aTime, nscoord aX, nscoord aY)
++{
++ mTime = aTime;
++ mX = aX;
++ mY = aY;
++}
++
++void
++nsWaylandDragContext::GetLastDropInfo(uint32_t *aTime, nscoord *aX, nscoord *aY)
++{
++ *aTime = mTime;
++ *aX = mX;
++ *aY = mY;
++}
++
++void
++nsWaylandDragContext::SetDragStatus(GdkDragAction aAction)
++{
++ mDataOffer->SetDragStatus(aAction, mTime);
++}
++
++GdkDragAction
++nsWaylandDragContext::GetSelectedDragAction()
++{
++ return mDataOffer->GetSelectedDragAction();
++}
++
++GList*
++nsWaylandDragContext::GetTargets()
++{
++ int targetNums;
++ GdkAtom *atoms = mDataOffer->GetTargets(&targetNums);
++
++ GList* targetList = nullptr;
++ for (int i = 0; i < targetNums; i++) {
++ targetList = g_list_append(targetList, GDK_ATOM_TO_POINTER(atoms[i]));
++ }
++
++ return targetList;
++}
++
++char*
++nsWaylandDragContext::GetData(const char* aMimeType, uint32_t* aContentLength)
++{
++ mDataOffer->DragOfferAccept(aMimeType, mTime);
++ return mDataOffer->GetData(mDisplay, aMimeType, aContentLength);
++}
++
++void
++nsRetrievalContextWayland::RegisterNewDataOffer(wl_data_offer *aWaylandDataOffer)
+ {
+ DataOffer* dataOffer =
+ static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aWaylandDataOffer));
++ MOZ_ASSERT(dataOffer == nullptr,
++ "Registered WaylandDataOffer already exists. Wayland protocol error?");
++
+ if (!dataOffer) {
+ dataOffer = new WaylandDataOffer(aWaylandDataOffer);
+ g_hash_table_insert(mActiveOffers, aWaylandDataOffer, dataOffer);
+ }
+ }
+
+ void
+-nsRetrievalContextWayland::RegisterDataOffer(
++nsRetrievalContextWayland::RegisterNewDataOffer(
+ gtk_primary_selection_offer *aPrimaryDataOffer)
+ {
+ DataOffer* dataOffer =
+ static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aPrimaryDataOffer));
++ MOZ_ASSERT(dataOffer == nullptr,
++ "Registered PrimaryDataOffer already exists. Wayland protocol error?");
++
+ if (!dataOffer) {
+ dataOffer = new PrimaryDataOffer(aPrimaryDataOffer);
+ g_hash_table_insert(mActiveOffers, aPrimaryDataOffer, dataOffer);
+ }
+ }
+
+ void
+ nsRetrievalContextWayland::SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer)
+ {
++ // Delete existing clipboard data offer
++ mClipboardOffer = nullptr;
++
+ DataOffer* dataOffer =
+ static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aWaylandDataOffer));
+ NS_ASSERTION(dataOffer, "We're missing clipboard data offer!");
+ if (dataOffer) {
+ g_hash_table_remove(mActiveOffers, aWaylandDataOffer);
+ mClipboardOffer = dataOffer;
+ }
+ }
+
+ void
+ nsRetrievalContextWayland::SetPrimaryDataOffer(
+ gtk_primary_selection_offer *aPrimaryDataOffer)
+ {
+- if (aPrimaryDataOffer == nullptr) {
+- // Release any primary offer we have.
+- mPrimaryOffer = nullptr;
+- } else {
++ // Release any primary offer we have.
++ mPrimaryOffer = nullptr;
++
++ // aPrimaryDataOffer can be null which means we lost
++ // the mouse selection.
++ if (aPrimaryDataOffer) {
+ DataOffer* dataOffer =
+ static_cast<DataOffer*>(g_hash_table_lookup(mActiveOffers,
+ aPrimaryDataOffer));
+ NS_ASSERTION(dataOffer, "We're missing primary data offer!");
+ if (dataOffer) {
+ g_hash_table_remove(mActiveOffers, aPrimaryDataOffer);
+ mPrimaryOffer = dataOffer;
+ }
+ }
+ }
+
+ void
+-nsRetrievalContextWayland::ClearDataOffers(void)
++nsRetrievalContextWayland::AddDragAndDropDataOffer(wl_data_offer *aDropDataOffer)
++{
++ // Remove any existing D&D contexts.
++ mDragContext = nullptr;
++
++ WaylandDataOffer* dataOffer =
++ static_cast<WaylandDataOffer*>(g_hash_table_lookup(mActiveOffers,
++ aDropDataOffer));
++ NS_ASSERTION(dataOffer, "We're missing drag and drop data offer!");
++ if (dataOffer) {
++ g_hash_table_remove(mActiveOffers, aDropDataOffer);
++ mDragContext = new nsWaylandDragContext(dataOffer, mDisplay);
++ }
++}
++
++nsWaylandDragContext*
++nsRetrievalContextWayland::GetDragContext(void)
++{
++ return mDragContext;
++}
++
++void
++nsRetrievalContextWayland::ClearClipboardDataOffers(void)
+ {
+ if (mClipboardOffer)
+ mClipboardOffer = nullptr;
+ if (mPrimaryOffer)
+ mPrimaryOffer = nullptr;
+ }
+
++void
++nsRetrievalContextWayland::ClearDragAndDropDataOffer(void)
++{
++ mDragContext = nullptr;
++}
++
+ // We have a new fresh data content.
+ // We should attach listeners to it and save for further use.
+ static void
+ data_device_data_offer (void *data,
+ struct wl_data_device *data_device,
+ struct wl_data_offer *offer)
+ {
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+- context->RegisterDataOffer(offer);
++ context->RegisterNewDataOffer(offer);
+ }
+
+ // The new fresh data content is clipboard.
+ static void
+ data_device_selection (void *data,
+ struct wl_data_device *wl_data_device,
+ struct wl_data_offer *offer)
+ {
+@@ -336,41 +541,88 @@ data_device_selection (void
+ }
+
+ // The new fresh wayland data content is drag and drop.
+ static void
+ data_device_enter (void *data,
+ struct wl_data_device *data_device,
+ uint32_t time,
+ struct wl_surface *surface,
+- int32_t x,
+- int32_t y,
++ int32_t x_fixed,
++ int32_t y_fixed,
+ struct wl_data_offer *offer)
+ {
++ nsRetrievalContextWayland *context =
++ static_cast<nsRetrievalContextWayland*>(data);
++ context->AddDragAndDropDataOffer(offer);
++
++ nsWaylandDragContext* dragContext = context->GetDragContext();
++
++ GtkWidget* gtkWidget = get_gtk_widget_for_wl_surface(surface);
++ if (!gtkWidget) {
++ NS_WARNING("DragAndDrop: Unable to get GtkWidget for wl_surface!");
++ return;
++ }
++
++ LOGDRAG(("nsWindow data_device_enter for GtkWidget %p\n",
++ (void*)gtkWidget));
++
++ dragContext->DropDataEnter(gtkWidget, time,
++ wl_fixed_to_int(x_fixed),
++ wl_fixed_to_int(y_fixed));
+ }
+
+ static void
+ data_device_leave (void *data,
+ struct wl_data_device *data_device)
+ {
++ nsRetrievalContextWayland *context =
++ static_cast<nsRetrievalContextWayland*>(data);
++
++ nsWaylandDragContext* dropContext = context->GetDragContext();
++ WindowDragLeaveHandler(dropContext->GetWidget());
++
++ context->ClearDragAndDropDataOffer();
+ }
+
+ static void
+ data_device_motion (void *data,
+ struct wl_data_device *data_device,
+ uint32_t time,
+- int32_t x,
+- int32_t y)
++ int32_t x_fixed,
++ int32_t y_fixed)
+ {
++ nsRetrievalContextWayland *context =
++ static_cast<nsRetrievalContextWayland*>(data);
++
++ nsWaylandDragContext* dropContext = context->GetDragContext();
++
++ nscoord x = wl_fixed_to_int(x_fixed);
++ nscoord y = wl_fixed_to_int(y_fixed);
++ dropContext->DropMotion(time, x, y);
++
++ WindowDragMotionHandler(dropContext->GetWidget(), nullptr,
++ dropContext, x, y, time);
+ }
+
+ static void
+ data_device_drop (void *data,
+ struct wl_data_device *data_device)
+ {
++ nsRetrievalContextWayland *context =
++ static_cast<nsRetrievalContextWayland*>(data);
++
++ nsWaylandDragContext* dropContext = context->GetDragContext();
++
++ uint32_t time;
++ nscoord x, y;
++ dropContext->GetLastDropInfo(&time, &x, &y);
++
++ WindowDragDropHandler(dropContext->GetWidget(), nullptr, dropContext,
++ x, y, time);
+ }
+
+ /* wl_data_device callback description:
+ *
+ * data_device_data_offer - It's called when there's a new wl_data_offer
+ * available. We need to attach wl_data_offer_listener
+ * to it to get available MIME types.
+ *
+@@ -400,29 +652,41 @@ static const struct wl_data_device_liste
+ static void
+ primary_selection_data_offer (void *data,
+ struct gtk_primary_selection_device *gtk_primary_selection_device,
+ struct gtk_primary_selection_offer *gtk_primary_offer)
+ {
+ // create and add listener
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+- context->RegisterDataOffer(gtk_primary_offer);
++ context->RegisterNewDataOffer(gtk_primary_offer);
+ }
+
+ static void
+ primary_selection_selection (void *data,
+ struct gtk_primary_selection_device *gtk_primary_selection_device,
+ struct gtk_primary_selection_offer *gtk_primary_offer)
+ {
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+ context->SetPrimaryDataOffer(gtk_primary_offer);
+ }
+
++/* gtk_primary_selection_device callback description:
++ *
++ * primary_selection_data_offer - It's called when there's a new
++ * gtk_primary_selection_offer available.
++ * We need to attach gtk_primary_selection_offer_listener
++ * to it to get available MIME types.
++ *
++ * primary_selection_selection - It's called when the new gtk_primary_selection_offer
++ * is a primary selection content. It can be also called with
++ * gtk_primary_selection_offer = null which means there's no
++ * primary selection.
++ */
+ static const struct
+ gtk_primary_selection_device_listener primary_selection_device_listener = {
+ primary_selection_data_offer,
+ primary_selection_selection,
+ };
+
+ bool
+ nsRetrievalContextWayland::HasSelectionSupport(void)
+@@ -446,17 +710,17 @@ keyboard_handle_enter(void *data, struct
+ static void
+ keyboard_handle_leave(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, struct wl_surface *surface)
+ {
+ // We lost focus so our clipboard data are outdated
+ nsRetrievalContextWayland *context =
+ static_cast<nsRetrievalContextWayland*>(data);
+
+- context->ClearDataOffers();
++ context->ClearClipboardDataOffers();
+ }
+
+ static void
+ keyboard_handle_key(void *data, struct wl_keyboard *keyboard,
+ uint32_t serial, uint32_t time, uint32_t key,
+ uint32_t state)
+ {
+ }
+@@ -568,16 +832,17 @@ nsRetrievalContextWayland::nsRetrievalCo
+ : mInitialized(false)
+ , mSeat(nullptr)
+ , mDataDeviceManager(nullptr)
+ , mPrimarySelectionDataDeviceManager(nullptr)
+ , mKeyboard(nullptr)
+ , mActiveOffers(g_hash_table_new(NULL, NULL))
+ , mClipboardOffer(nullptr)
+ , mPrimaryOffer(nullptr)
++ , mDragContext(nullptr)
+ , mClipboardRequestNumber(0)
+ , mClipboardData(nullptr)
+ , mClipboardDataLength(0)
+ {
+ // Available as of GTK 3.8+
+ static auto sGdkWaylandDisplayGetWlDisplay =
+ (wl_display *(*)(GdkDisplay *))
+ dlsym(RTLD_DEFAULT, "gdk_wayland_display_get_wl_display");
+@@ -611,18 +876,31 @@ nsRetrievalContextWayland::nsRetrievalCo
+ mSeat);
+ gtk_primary_selection_device_add_listener(primaryDataDevice,
+ &primary_selection_device_listener, this);
+ }
+
+ mInitialized = true;
+ }
+
++static gboolean
++offer_hash_remove(gpointer wl_offer, gpointer aDataOffer, gpointer user_data)
++{
++#ifdef DEBUG
++ nsPrintfCString msg("nsRetrievalContextWayland(): leaked nsDataOffer %p\n",
++ aDataOffer);
++ NS_WARNING(msg.get());
++#endif
++ delete static_cast<DataOffer*>(aDataOffer);
++ return true;
++}
++
+ nsRetrievalContextWayland::~nsRetrievalContextWayland(void)
+ {
++ g_hash_table_foreach_remove(mActiveOffers, offer_hash_remove, nullptr);
+ g_hash_table_destroy(mActiveOffers);
+ }
+
+ GdkAtom*
+ nsRetrievalContextWayland::GetTargets(int32_t aWhichClipboard,
+ int* aTargetNum)
+ {
+ if (GetSelectionAtom(aWhichClipboard) == GDK_SELECTION_CLIPBOARD) {
+diff --git a/widget/gtk/nsClipboardWayland.h b/widget/gtk/nsClipboardWayland.h
+--- a/widget/gtk/nsClipboardWayland.h
++++ b/widget/gtk/nsClipboardWayland.h
+@@ -27,65 +27,108 @@ public:
+
+ char* GetData(wl_display* aDisplay, const char* aMimeType,
+ uint32_t* aContentLength);
+
+ virtual ~DataOffer() {};
+ private:
+ virtual bool RequestDataTransfer(const char* aMimeType, int fd) = 0;
+
++protected:
+ nsTArray<GdkAtom> mTargetMIMETypes;
+ };
+
+ class WaylandDataOffer : public DataOffer
+ {
+ public:
+ WaylandDataOffer(wl_data_offer* aWaylandDataOffer);
+
++ void DragOfferAccept(const char* aMimeType, uint32_t aTime);
++ void SetDragStatus(GdkDragAction aAction, uint32_t aTime);
++
++ GdkDragAction GetSelectedDragAction();
++ void SetSelectedDragAction(uint32_t aWaylandAction);
++
++ void SetSourceDragActions(uint32_t aWaylandActions);
++
++ virtual ~WaylandDataOffer();
+ private:
+- virtual ~WaylandDataOffer();
+ bool RequestDataTransfer(const char* aMimeType, int fd) override;
+
+ wl_data_offer* mWaylandDataOffer;
++ uint32_t mSelectedDragAction;
+ };
+
+ class PrimaryDataOffer : public DataOffer
+ {
+ public:
+ PrimaryDataOffer(gtk_primary_selection_offer* aPrimaryDataOffer);
++ void SetAvailableDragActions(uint32_t aWaylandActions) {};
+
++ virtual ~PrimaryDataOffer();
+ private:
+- virtual ~PrimaryDataOffer();
+ bool RequestDataTransfer(const char* aMimeType, int fd) override;
+
+ gtk_primary_selection_offer* mPrimaryDataOffer;
+ };
+
++class nsWaylandDragContext : public nsISupports
++{
++ NS_DECL_ISUPPORTS
++
++public:
++ nsWaylandDragContext(WaylandDataOffer* aWaylandDataOffer,
++ wl_display *aDisplay);
++
++ void DropDataEnter(GtkWidget* aGtkWidget, uint32_t aTime,
++ nscoord aX, nscoord aY);
++ void DropMotion(uint32_t aTime, nscoord aX, nscoord aY);
++ void GetLastDropInfo(uint32_t *aTime, nscoord *aX, nscoord *aY);
++
++ void SetDragStatus(GdkDragAction action);
++ GdkDragAction GetSelectedDragAction();
++
++ GtkWidget* GetWidget() { return mGtkWidget; }
++ GList* GetTargets();
++ char* GetData(const char* aMimeType, uint32_t* aContentLength);
++private:
++ virtual ~nsWaylandDragContext() {};
++
++ nsAutoPtr<WaylandDataOffer> mDataOffer;
++ wl_display* mDisplay;
++ uint32_t mTime;
++ GtkWidget* mGtkWidget;
++ nscoord mX, mY;
++};
++
+ class nsRetrievalContextWayland : public nsRetrievalContext
+ {
+ public:
+ nsRetrievalContextWayland();
+
+ virtual const char* GetClipboardData(const char* aMimeType,
+ int32_t aWhichClipboard,
+ uint32_t* aContentLength) override;
+ virtual const char* GetClipboardText(int32_t aWhichClipboard) override;
+ virtual void ReleaseClipboardData(const char* aClipboardData) override;
+
+ virtual GdkAtom* GetTargets(int32_t aWhichClipboard,
+ int* aTargetNum) override;
+ virtual bool HasSelectionSupport(void) override;
+
+- void RegisterDataOffer(wl_data_offer *aWaylandDataOffer);
+- void RegisterDataOffer(gtk_primary_selection_offer *aPrimaryDataOffer);
++ void RegisterNewDataOffer(wl_data_offer *aWaylandDataOffer);
++ void RegisterNewDataOffer(gtk_primary_selection_offer *aPrimaryDataOffer);
+
+ void SetClipboardDataOffer(wl_data_offer *aWaylandDataOffer);
+ void SetPrimaryDataOffer(gtk_primary_selection_offer *aPrimaryDataOffer);
++ void AddDragAndDropDataOffer(wl_data_offer *aWaylandDataOffer);
++ nsWaylandDragContext* GetDragContext();
+
+- void ClearDataOffers();
++ void ClearClipboardDataOffers();
++ void ClearDragAndDropDataOffer();
+
+ void ConfigureKeyboard(wl_seat_capability caps);
+ void TransferFastTrackClipboard(int aClipboardRequestNumber,
+ GtkSelectionData *aSelectionData);
+
+ void InitDataDeviceManager(wl_registry *registry, uint32_t id, uint32_t version);
+ void InitPrimarySelectionDataDeviceManager(wl_registry *registry, uint32_t id);
+ void InitSeat(wl_registry *registry, uint32_t id, uint32_t version, void *data);
+@@ -98,16 +141,17 @@ private:
+ wl_data_device_manager *mDataDeviceManager;
+ gtk_primary_selection_device_manager *mPrimarySelectionDataDeviceManager;
+ wl_keyboard *mKeyboard;
+
+ // Data offers provided by Wayland data device
+ GHashTable* mActiveOffers;
+ nsAutoPtr<DataOffer> mClipboardOffer;
+ nsAutoPtr<DataOffer> mPrimaryOffer;
++ RefPtr<nsWaylandDragContext> mDragContext;
+
+ int mClipboardRequestNumber;
+ char* mClipboardData;
+ uint32_t mClipboardDataLength;
+
+ // Mime types used for text data at Gtk+, see request_text_received_func()
+ // at gtkclipboard.c.
+ #define TEXT_MIME_TYPES_NUM 3
+diff --git a/widget/gtk/nsDragService.cpp b/widget/gtk/nsDragService.cpp
+--- a/widget/gtk/nsDragService.cpp
++++ b/widget/gtk/nsDragService.cpp
+@@ -37,16 +37,19 @@
+ #include "nsViewManager.h"
+ #include "nsIFrame.h"
+ #include "nsGtkUtils.h"
+ #include "nsGtkKeyUtils.h"
+ #include "mozilla/gfx/2D.h"
+ #include "gfxPlatform.h"
+ #include "ScreenHelperGTK.h"
+ #include "nsArrayUtils.h"
++#ifdef MOZ_WAYLAND
++#include "nsClipboardWayland.h"
++#endif
+
+ using namespace mozilla;
+ using namespace mozilla::gfx;
+
+ // This sets how opaque the drag image is
+ #define DRAG_IMAGE_ALPHA_LEVEL 0.5
+
+ // These values are copied from GtkDragResult (rather than using GtkDragResult
+@@ -93,16 +96,20 @@ invisibleSourceDragDataGet(GtkWidget
+ GtkSelectionData *aSelectionData,
+ guint aInfo,
+ guint32 aTime,
+ gpointer aData);
+
+ nsDragService::nsDragService()
+ : mScheduledTask(eDragTaskNone)
+ , mTaskSource(0)
++#ifdef MOZ_WAYLAND
++ , mPendingWaylandDragContext(nullptr)
++ , mTargetWaylandDragContext(nullptr)
++#endif
+ {
+ // We have to destroy the hidden widget before the event loop stops
+ // running.
+ nsCOMPtr<nsIObserverService> obsServ =
+ mozilla::services::GetObserverService();
+ obsServ->AddObserver(this, "quit-application", false);
+
+ // our hidden source widget
+@@ -509,16 +516,19 @@ nsDragService::EndDragSession(bool aDone
+ }
+ }
+
+ // unset our drag action
+ SetDragAction(DRAGDROP_ACTION_NONE);
+
+ // We're done with the drag context.
+ mTargetDragContextForRemote = nullptr;
++#ifdef MOZ_WAYLAND
++ mTargetWaylandDragContextForRemote = nullptr;
++#endif
+
+ return nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers);
+ }
+
+ // nsIDragSession
+ NS_IMETHODIMP
+ nsDragService::SetCanDrop(bool aCanDrop)
+ {
+@@ -629,16 +639,24 @@ nsDragService::GetNumDropItems(uint32_t
+ if (!mTargetWidget) {
+ MOZ_LOG(sDragLm, LogLevel::Debug,
+ ("*** warning: GetNumDropItems \
+ called without a valid target widget!\n"));
+ *aNumItems = 0;
+ return NS_OK;
+ }
+
++#ifdef MOZ_WAYLAND
++ // TODO: Wayland implementation of text/uri-list.
++ if (!mTargetDragContext) {
++ *aNumItems = 1;
++ return NS_OK;
++ }
++#endif
++
+ bool isList = IsTargetContextList();
+ if (isList)
+ mSourceDataItems->GetLength(aNumItems);
+ else {
+ GdkAtom gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
+ GetTargetDragData(gdkFlavor);
+ if (mTargetDragData) {
+ const char *data = reinterpret_cast<char*>(mTargetDragData);
+@@ -1020,19 +1038,28 @@ nsDragService::IsDataFlavorSupported(con
+ }
+ }
+ }
+ }
+ return NS_OK;
+ }
+
+ // check the target context vs. this flavor, one at a time
+- GList *tmp;
+- for (tmp = gdk_drag_context_list_targets(mTargetDragContext);
+- tmp; tmp = tmp->next) {
++ GList *tmp = nullptr;
++ if (mTargetDragContext) {
++ tmp = gdk_drag_context_list_targets(mTargetDragContext);
++ }
++#ifdef MOZ_WAYLAND
++ else {
++ tmp = mTargetWaylandDragContext->GetTargets();
++ }
++#endif
++ GList *tmp_head = tmp;
++
++ for (; tmp; tmp = tmp->next) {
+ /* Bug 331198 */
+ GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data);
+ gchar *name = nullptr;
+ name = gdk_atom_name(atom);
+ MOZ_LOG(sDragLm, LogLevel::Debug,
+ ("checking %s against %s\n", name, aDataFlavor));
+ if (name && (strcmp(name, aDataFlavor) == 0)) {
+ MOZ_LOG(sDragLm, LogLevel::Debug, ("good!\n"));
+@@ -1067,16 +1094,23 @@ nsDragService::IsDataFlavorSupported(con
+ (strcmp(aDataFlavor, kFileMime) == 0))) {
+ MOZ_LOG(sDragLm, LogLevel::Debug,
+ ("good! ( it's text plain and we're checking \
+ against text/unicode or application/x-moz-file)\n"));
+ *_retval = true;
+ }
+ g_free(name);
+ }
++
++ // mTargetWaylandDragContext->GetTargets allocates the list
++ // so we need to free it here.
++ if (!mTargetDragContext) {
++ g_list_free(tmp_head);
++ }
++
+ return NS_OK;
+ }
+
+ void
+ nsDragService::ReplyToDragMotion(GdkDragContext* aDragContext)
+ {
+ MOZ_LOG(sDragLm, LogLevel::Debug,
+ ("nsDragService::ReplyToDragMotion %d", mCanDrop));
+@@ -1098,16 +1132,46 @@ nsDragService::ReplyToDragMotion(GdkDrag
+ action = GDK_ACTION_MOVE;
+ break;
+ }
+ }
+
+ gdk_drag_status(aDragContext, action, mTargetTime);
+ }
+
++#ifdef MOZ_WAYLAND
++void
++nsDragService::ReplyToDragMotion(nsWaylandDragContext* aDragContext)
++{
++ MOZ_LOG(sDragLm, LogLevel::Debug,
++ ("nsDragService::ReplyToDragMotion %d", mCanDrop));
++
++ GdkDragAction action = (GdkDragAction)0;
++ if (mCanDrop) {
++ // notify the dragger if we can drop
++ switch (mDragAction) {
++ case DRAGDROP_ACTION_COPY:
++ action = GDK_ACTION_COPY;
++ break;
++ case DRAGDROP_ACTION_LINK:
++ action = GDK_ACTION_LINK;
++ break;
++ case DRAGDROP_ACTION_NONE:
++ action = (GdkDragAction)0;
++ break;
++ default:
++ action = GDK_ACTION_MOVE;
++ break;
++ }
++ }
++
++ aDragContext->SetDragStatus(action);
++}
++#endif
++
+ void
+ nsDragService::TargetDataReceived(GtkWidget *aWidget,
+ GdkDragContext *aContext,
+ gint aX,
+ gint aY,
+ GtkSelectionData *aSelectionData,
+ guint aInfo,
+ guint32 aTime)
+@@ -1129,16 +1193,22 @@ nsDragService::TargetDataReceived(GtkWid
+ }
+ }
+
+ bool
+ nsDragService::IsTargetContextList(void)
+ {
+ bool retval = false;
+
++#ifdef MOZ_WAYLAND
++ // TODO: We need a wayland implementation here.
++ if (!mTargetDragContext)
++ return retval;
++#endif
++
+ // gMimeListType drags only work for drags within a single process. The
+ // gtk_drag_get_source_widget() function will return nullptr if the source
+ // of the drag is another app, so we use it to check if a gMimeListType
+ // drop will work or not.
+ if (gtk_drag_get_source_widget(mTargetDragContext) == nullptr)
+ return retval;
+
+ GList *tmp;
+@@ -1167,27 +1237,38 @@ void
+ nsDragService::GetTargetDragData(GdkAtom aFlavor)
+ {
+ MOZ_LOG(sDragLm, LogLevel::Debug, ("getting data flavor %p\n", aFlavor));
+ MOZ_LOG(sDragLm, LogLevel::Debug, ("mLastWidget is %p and mLastContext is %p\n",
+ mTargetWidget.get(),
+ mTargetDragContext.get()));
+ // reset our target data areas
+ TargetResetData();
+- gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
++
++ if (mTargetDragContext) {
++ gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
+
+- MOZ_LOG(sDragLm, LogLevel::Debug, ("about to start inner iteration."));
+- PRTime entryTime = PR_Now();
+- while (!mTargetDragDataReceived && mDoingDrag) {
+- // check the number of iterations
+- MOZ_LOG(sDragLm, LogLevel::Debug, ("doing iteration...\n"));
+- PR_Sleep(20*PR_TicksPerSecond()/1000); /* sleep for 20 ms/iteration */
+- if (PR_Now()-entryTime > NS_DND_TIMEOUT) break;
+- gtk_main_iteration();
++ MOZ_LOG(sDragLm, LogLevel::Debug, ("about to start inner iteration."));
++ PRTime entryTime = PR_Now();
++ while (!mTargetDragDataReceived && mDoingDrag) {
++ // check the number of iterations
++ MOZ_LOG(sDragLm, LogLevel::Debug, ("doing iteration...\n"));
++ PR_Sleep(20*PR_TicksPerSecond()/1000); /* sleep for 20 ms/iteration */
++ if (PR_Now()-entryTime > NS_DND_TIMEOUT) break;
++ gtk_main_iteration();
++ }
+ }
++#ifdef MOZ_WAYLAND
++ else {
++ mTargetDragData =
++ mTargetWaylandDragContext->GetData(gdk_atom_name(aFlavor),
++ &mTargetDragDataLen);
++ mTargetDragDataReceived = true;
++ }
++#endif
+ MOZ_LOG(sDragLm, LogLevel::Debug, ("finished inner iteration\n"));
+ }
+
+ void
+ nsDragService::TargetResetData(void)
+ {
+ mTargetDragDataReceived = false;
+ // make sure to free old data if we have to
+@@ -1428,17 +1509,17 @@ nsDragService::SourceEndDragSession(GdkD
+ }
+ }
+
+ if (mDataTransfer) {
+ mDataTransfer->SetDropEffectInt(dropEffect);
+ }
+
+ // Schedule the appropriate drag end dom events.
+- Schedule(eDragTaskSourceEnd, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
++ Schedule(eDragTaskSourceEnd, nullptr, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
+ }
+
+ static void
+ CreateUriList(nsIArray *items, gchar **text, gint *length)
+ {
+ uint32_t i, count;
+ GString *uriList = g_string_new(nullptr);
+
+@@ -1778,64 +1859,68 @@ invisibleSourceDragEnd(GtkWidget
+ //
+ // No Gecko drag events are dispatched (during nested event loops) while other
+ // Gecko drag events are in flight. This helps event handlers that may not
+ // expect nested events, while accessing an event's dataTransfer for example.
+
+ gboolean
+ nsDragService::ScheduleMotionEvent(nsWindow *aWindow,
+ GdkDragContext *aDragContext,
++ nsWaylandDragContext *aWaylandDragContext,
+ LayoutDeviceIntPoint aWindowPoint, guint aTime)
+ {
+- if (mScheduledTask == eDragTaskMotion) {
++ if (aDragContext && mScheduledTask == eDragTaskMotion) {
+ // The drag source has sent another motion message before we've
+ // replied to the previous. That shouldn't happen with Xdnd. The
+ // spec for Motif drags is less clear, but we'll just update the
+ // scheduled task with the new position reply only to the most
+ // recent message.
+ NS_WARNING("Drag Motion message received before previous reply was sent");
+ }
+
+ // Returning TRUE means we'll reply with a status message, unless we first
+ // get a leave.
+- return Schedule(eDragTaskMotion, aWindow, aDragContext,
++ return Schedule(eDragTaskMotion, aWindow, aDragContext, aWaylandDragContext,
+ aWindowPoint, aTime);
+ }
+
+ void
+ nsDragService::ScheduleLeaveEvent()
+ {
+ // We don't know at this stage whether a drop signal will immediately
+ // follow. If the drop signal gets sent it will happen before we return
+ // to the main loop and the scheduled leave task will be replaced.
+- if (!Schedule(eDragTaskLeave, nullptr, nullptr, LayoutDeviceIntPoint(), 0)) {
++ if (!Schedule(eDragTaskLeave, nullptr, nullptr, nullptr,
++ LayoutDeviceIntPoint(), 0)) {
+ NS_WARNING("Drag leave after drop");
+ }
+ }
+
+ gboolean
+ nsDragService::ScheduleDropEvent(nsWindow *aWindow,
+ GdkDragContext *aDragContext,
++ nsWaylandDragContext *aWaylandDragContext,
+ LayoutDeviceIntPoint aWindowPoint, guint aTime)
+ {
+ if (!Schedule(eDragTaskDrop, aWindow,
+- aDragContext, aWindowPoint, aTime)) {
++ aDragContext, aWaylandDragContext, aWindowPoint, aTime)) {
+ NS_WARNING("Additional drag drop ignored");
+ return FALSE;
+ }
+
+ SetDragEndPoint(aWindowPoint + aWindow->WidgetToScreenOffset());
+
+ // We'll reply with gtk_drag_finish().
+ return TRUE;
+ }
+
+ gboolean
+ nsDragService::Schedule(DragTask aTask, nsWindow *aWindow,
+ GdkDragContext *aDragContext,
++ nsWaylandDragContext *aWaylandDragContext,
+ LayoutDeviceIntPoint aWindowPoint, guint aTime)
+ {
+ // If there is an existing leave or motion task scheduled, then that
+ // will be replaced. When the new task is run, it will dispatch
+ // any necessary leave or motion events.
+
+ // If aTask is eDragTaskSourceEnd, then it will replace even a scheduled
+ // drop event (which could happen if the drop event has not been processed
+@@ -1844,16 +1929,19 @@ nsDragService::Schedule(DragTask aTask,
+ // drop.
+ if (mScheduledTask == eDragTaskSourceEnd ||
+ (mScheduledTask == eDragTaskDrop && aTask != eDragTaskSourceEnd))
+ return FALSE;
+
+ mScheduledTask = aTask;
+ mPendingWindow = aWindow;
+ mPendingDragContext = aDragContext;
++#ifdef MOZ_WAYLAND
++ mPendingWaylandDragContext = aWaylandDragContext;
++#endif
+ mPendingWindowPoint = aWindowPoint;
+ mPendingTime = aTime;
+
+ if (!mTaskSource) {
+ // High priority is used here because the native events involved have
+ // already waited at default priority. Perhaps a lower than default
+ // priority could be used for motion tasks because there is a chance
+ // that a leave or drop is waiting, but managing different priorities
+@@ -1919,17 +2007,24 @@ nsDragService::RunScheduledTask()
+ // This may be the start of a destination drag session.
+ StartDragSession();
+
+ // mTargetWidget may be nullptr if the window has been destroyed.
+ // (The leave event is not scheduled if a drop task is still scheduled.)
+ // We still reply appropriately to indicate that the drop will or didn't
+ // succeeed.
+ mTargetWidget = mTargetWindow->GetMozContainerWidget();
+- mTargetDragContext.steal(mPendingDragContext);
++ if (mTargetDragContext) {
++ mTargetDragContext.steal(mPendingDragContext);
++ }
++#ifdef MOZ_WAYLAND
++ else {
++ mTargetWaylandDragContext = mPendingWaylandDragContext.forget();
++ }
++#endif
+ mTargetTime = mPendingTime;
+
+ // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
+ // (as at 27 December 2010) indicates that a "drop" event should only be
+ // fired (at the current target element) if the current drag operation is
+ // not none. The current drag operation will only be set to a non-none
+ // value during a "dragover" event.
+ //
+@@ -1951,44 +2046,59 @@ nsDragService::RunScheduledTask()
+ // protocol is used.
+ if (task == eDragTaskMotion || positionHasChanged) {
+ UpdateDragAction();
+ TakeDragEventDispatchedToChildProcess(); // Clear the old value.
+ DispatchMotionEvents();
+ if (task == eDragTaskMotion) {
+ if (TakeDragEventDispatchedToChildProcess()) {
+ mTargetDragContextForRemote = mTargetDragContext;
++#ifdef MOZ_WAYLAND
++ mTargetWaylandDragContextForRemote = mTargetWaylandDragContext;
++#endif
+ } else {
+ // Reply to tell the source whether we can drop and what
+ // action would be taken.
+- ReplyToDragMotion(mTargetDragContext);
++ if (mTargetDragContext) {
++ ReplyToDragMotion(mTargetDragContext);
++ }
++#ifdef MOZ_WAYLAND
++ else {
++ ReplyToDragMotion(mTargetWaylandDragContext);
++ }
++#endif
+ }
+ }
+ }
+
+ if (task == eDragTaskDrop) {
+ gboolean success = DispatchDropEvent();
+
+ // Perhaps we should set the del parameter to TRUE when the drag
+ // action is move, but we don't know whether the data was successfully
+ // transferred.
+- gtk_drag_finish(mTargetDragContext, success,
+- /* del = */ FALSE, mTargetTime);
++ if (mTargetDragContext) {
++ gtk_drag_finish(mTargetDragContext, success,
++ /* del = */ FALSE, mTargetTime);
++ }
+
+ // This drag is over, so clear out our reference to the previous
+ // window.
+ mTargetWindow = nullptr;
+ // Make sure to end the drag session. If this drag started in a
+ // different app, we won't get a drag_end signal to end it from.
+ EndDragSession(true, GetCurrentModifiers());
+ }
+
+ // We're done with the drag context.
+ mTargetWidget = nullptr;
+ mTargetDragContext = nullptr;
++#ifdef MOZ_WAYLAND
++ mTargetWaylandDragContext = nullptr;
++#endif
+
+ // If we got another drag signal while running the sheduled task, that
+ // must have happened while running a nested event loop. Leave the task
+ // source on the event loop.
+ if (mScheduledTask != eDragTaskNone)
+ return TRUE;
+
+ // We have no task scheduled.
+@@ -2008,17 +2118,26 @@ nsDragService::UpdateDragAction()
+ // nsContentUtils::SetDataTransferInEvent() to set the initial
+ // dataTransfer.dropEffect, so GdkDragContext::suggested_action would be
+ // more appropriate. GdkDragContext::actions should be used to set
+ // dataTransfer.effectAllowed, which doesn't currently happen with
+ // external sources.
+
+ // default is to do nothing
+ int action = nsIDragService::DRAGDROP_ACTION_NONE;
+- GdkDragAction gdkAction = gdk_drag_context_get_actions(mTargetDragContext);
++ GdkDragAction gdkAction = GDK_ACTION_DEFAULT;
++ if (mTargetDragContext) {
++ gdkAction = gdk_drag_context_get_actions(mTargetDragContext);
++ }
++#ifdef MOZ_WAYLAND
++ else {
++ // We got the selected D&D action from compositor on Wayland.
++ gdkAction = mTargetWaylandDragContext->GetSelectedDragAction();
++ }
++#endif
+
+ // set the default just in case nothing matches below
+ if (gdkAction & GDK_ACTION_DEFAULT)
+ action = nsIDragService::DRAGDROP_ACTION_MOVE;
+
+ // first check to see if move is set
+ if (gdkAction & GDK_ACTION_MOVE)
+ action = nsIDragService::DRAGDROP_ACTION_MOVE;
+@@ -2037,16 +2156,22 @@ nsDragService::UpdateDragAction()
+
+ NS_IMETHODIMP
+ nsDragService::UpdateDragEffect()
+ {
+ if (mTargetDragContextForRemote) {
+ ReplyToDragMotion(mTargetDragContextForRemote);
+ mTargetDragContextForRemote = nullptr;
+ }
++#ifdef MOZ_WAYLAND
++ else if (mTargetWaylandDragContextForRemote) {
++ ReplyToDragMotion(mTargetWaylandDragContextForRemote);
++ mTargetWaylandDragContextForRemote = nullptr;
++ }
++#endif
+ return NS_OK;
+ }
+
+ void
+ nsDragService::DispatchMotionEvents()
+ {
+ mCanDrop = false;
+
+diff --git a/widget/gtk/nsDragService.h b/widget/gtk/nsDragService.h
+--- a/widget/gtk/nsDragService.h
++++ b/widget/gtk/nsDragService.h
+@@ -9,16 +9,17 @@
+
+ #include "mozilla/RefPtr.h"
+ #include "nsBaseDragService.h"
+ #include "nsIObserver.h"
+ #include "nsAutoRef.h"
+ #include <gtk/gtk.h>
+
+ class nsWindow;
++class nsWaylandDragContext;
+
+ namespace mozilla {
+ namespace gfx {
+ class SourceSurface;
+ }
+ }
+
+ #ifndef HAVE_NSGOBJECTREFTRAITS
+@@ -93,21 +94,23 @@ public:
+ gint aX,
+ gint aY,
+ GtkSelectionData *aSelection_data,
+ guint aInfo,
+ guint32 aTime);
+
+ gboolean ScheduleMotionEvent(nsWindow *aWindow,
+ GdkDragContext *aDragContext,
++ nsWaylandDragContext* aPendingWaylandDragContext,
+ mozilla::LayoutDeviceIntPoint aWindowPoint,
+ guint aTime);
+ void ScheduleLeaveEvent();
+ gboolean ScheduleDropEvent(nsWindow *aWindow,
+ GdkDragContext *aDragContext,
++ nsWaylandDragContext* aPendingWaylandDragContext,
+ mozilla::LayoutDeviceIntPoint aWindowPoint,
+ guint aTime);
+
+ nsWindow* GetMostRecentDestWindow()
+ {
+ return mScheduledTask == eDragTaskNone ? mTargetWindow
+ : mPendingWindow;
+ }
+@@ -153,30 +156,39 @@ private:
+
+ // mPendingWindow, mPendingWindowPoint, mPendingDragContext, and
+ // mPendingTime, carry information from the GTK signal that will be used
+ // when the scheduled task is run. mPendingWindow and mPendingDragContext
+ // will be nullptr if the scheduled task is eDragTaskLeave.
+ RefPtr<nsWindow> mPendingWindow;
+ mozilla::LayoutDeviceIntPoint mPendingWindowPoint;
+ nsCountedRef<GdkDragContext> mPendingDragContext;
++#ifdef MOZ_WAYLAND
++ RefPtr<nsWaylandDragContext> mPendingWaylandDragContext;
++#endif
+ guint mPendingTime;
+
+ // mTargetWindow and mTargetWindowPoint record the position of the last
+ // eDragTaskMotion or eDragTaskDrop task that was run or is still running.
+ // mTargetWindow is cleared once the drag has completed or left.
+ RefPtr<nsWindow> mTargetWindow;
+ mozilla::LayoutDeviceIntPoint mTargetWindowPoint;
+ // mTargetWidget and mTargetDragContext are set only while dispatching
+ // motion or drop events. mTime records the corresponding timestamp.
+ nsCountedRef<GtkWidget> mTargetWidget;
+ nsCountedRef<GdkDragContext> mTargetDragContext;
++#ifdef MOZ_WAYLAND
++ RefPtr<nsWaylandDragContext> mTargetWaylandDragContext;
++#endif
+ // mTargetDragContextForRemote is set while waiting for a reply from
+ // a child process.
+ nsCountedRef<GdkDragContext> mTargetDragContextForRemote;
++#ifdef MOZ_WAYLAND
++ RefPtr<nsWaylandDragContext> mTargetWaylandDragContextForRemote;
++#endif
+ guint mTargetTime;
+
+ // is it OK to drop on us?
+ bool mCanDrop;
+
+ // have we received our drag data?
+ bool mTargetDragDataReceived;
+ // last data received and its length
+@@ -207,22 +219,25 @@ private:
+ bool SetAlphaPixmap(SourceSurface *aPixbuf,
+ GdkDragContext *aContext,
+ int32_t aXOffset,
+ int32_t aYOffset,
+ const mozilla::LayoutDeviceIntRect &dragRect);
+
+ gboolean Schedule(DragTask aTask, nsWindow *aWindow,
+ GdkDragContext *aDragContext,
++ nsWaylandDragContext* aPendingWaylandDragContext,
+ mozilla::LayoutDeviceIntPoint aWindowPoint, guint aTime);
+
+ // Callback for g_idle_add_full() to run mScheduledTask.
+ static gboolean TaskDispatchCallback(gpointer data);
+ gboolean RunScheduledTask();
+ void UpdateDragAction();
+ void DispatchMotionEvents();
+ void ReplyToDragMotion(GdkDragContext* aDragContext);
++#ifdef MOZ_WAYLAND
++ void ReplyToDragMotion(nsWaylandDragContext* aDragContext);
++#endif
+ gboolean DispatchDropEvent();
+ static uint32_t GetCurrentModifiers();
+ };
+
+ #endif // nsDragService_h__
+-
+diff --git a/widget/gtk/nsWindow.cpp b/widget/gtk/nsWindow.cpp
+--- a/widget/gtk/nsWindow.cpp
++++ b/widget/gtk/nsWindow.cpp
+@@ -6081,23 +6081,23 @@ touch_event_cb(GtkWidget* aWidget, GdkEv
+ void
+ nsWindow::InitDragEvent(WidgetDragEvent &aEvent)
+ {
+ // set the keyboard modifiers
+ guint modifierState = KeymapWrapper::GetCurrentModifierState();
+ KeymapWrapper::InitInputEvent(aEvent, modifierState);
+ }
+
+-static gboolean
+-drag_motion_event_cb(GtkWidget *aWidget,
+- GdkDragContext *aDragContext,
+- gint aX,
+- gint aY,
+- guint aTime,
+- gpointer aData)
++gboolean
++WindowDragMotionHandler(GtkWidget *aWidget,
++ GdkDragContext *aDragContext,
++ nsWaylandDragContext *aWaylandDragContext,
++ gint aX,
++ gint aY,
++ guint aTime)
+ {
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
+ if (!window)
+ return FALSE;
+
+ // figure out which internal widget this drag motion actually happened on
+ nscoord retx = 0;
+ nscoord rety = 0;
+@@ -6112,25 +6112,34 @@ drag_motion_event_cb(GtkWidget *aWidget,
+ }
+
+ LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
+
+ LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
+
+ RefPtr<nsDragService> dragService = nsDragService::GetInstance();
+ return dragService->
+- ScheduleMotionEvent(innerMostWindow, aDragContext,
++ ScheduleMotionEvent(innerMostWindow, aDragContext, aWaylandDragContext,
+ point, aTime);
+ }
+
+-static void
+-drag_leave_event_cb(GtkWidget *aWidget,
+- GdkDragContext *aDragContext,
+- guint aTime,
+- gpointer aData)
++static gboolean
++drag_motion_event_cb(GtkWidget *aWidget,
++ GdkDragContext *aDragContext,
++ gint aX,
++ gint aY,
++ guint aTime,
++ gpointer aData)
++{
++ return WindowDragMotionHandler(aWidget, aDragContext, nullptr,
++ aX, aY, aTime);
++}
++
++void
++WindowDragLeaveHandler(GtkWidget *aWidget)
+ {
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
+ if (!window)
+ return;
+
+ RefPtr<nsDragService> dragService = nsDragService::GetInstance();
+
+ nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow();
+@@ -6153,24 +6162,32 @@ drag_leave_event_cb(GtkWidget *aWidget,
+ }
+
+ LOGDRAG(("nsWindow drag-leave signal for %p\n",
+ (void*)mostRecentDragWindow));
+
+ dragService->ScheduleLeaveEvent();
+ }
+
+-
+-static gboolean
+-drag_drop_event_cb(GtkWidget *aWidget,
+- GdkDragContext *aDragContext,
+- gint aX,
+- gint aY,
+- guint aTime,
+- gpointer aData)
++static void
++drag_leave_event_cb(GtkWidget *aWidget,
++ GdkDragContext *aDragContext,
++ guint aTime,
++ gpointer aData)
++{
++ WindowDragLeaveHandler(aWidget);
++}
++
++gboolean
++WindowDragDropHandler(GtkWidget *aWidget,
++ GdkDragContext *aDragContext,
++ nsWaylandDragContext *aWaylandDragContext,
++ gint aX,
++ gint aY,
++ guint aTime)
+ {
+ RefPtr<nsWindow> window = get_window_for_gtk_widget(aWidget);
+ if (!window)
+ return FALSE;
+
+ // figure out which internal widget this drag motion actually happened on
+ nscoord retx = 0;
+ nscoord rety = 0;
+@@ -6185,20 +6202,31 @@ drag_drop_event_cb(GtkWidget *aWidget,
+ }
+
+ LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
+
+ LayoutDeviceIntPoint point = window->GdkPointToDevicePixels({ retx, rety });
+
+ RefPtr<nsDragService> dragService = nsDragService::GetInstance();
+ return dragService->
+- ScheduleDropEvent(innerMostWindow, aDragContext,
++ ScheduleDropEvent(innerMostWindow, aDragContext, aWaylandDragContext,
+ point, aTime);
+ }
+
++static gboolean
++drag_drop_event_cb(GtkWidget *aWidget,
++ GdkDragContext *aDragContext,
++ gint aX,
++ gint aY,
++ guint aTime,
++ gpointer aData)
++{
++ return WindowDragDropHandler(aWidget, aDragContext, nullptr, aX, aY, aTime);
++}
++
+ static void
+ drag_data_received_event_cb(GtkWidget *aWidget,
+ GdkDragContext *aDragContext,
+ gint aX,
+ gint aY,
+ GtkSelectionData *aSelectionData,
+ guint aInfo,
+ guint aTime,
+diff --git a/widget/gtk/nsWindow.h b/widget/gtk/nsWindow.h
+--- a/widget/gtk/nsWindow.h
++++ b/widget/gtk/nsWindow.h
+@@ -61,16 +61,31 @@ extern mozilla::LazyLogModule gWidgetDra
+
+ #define LOG(args)
+ #define LOGFOCUS(args)
+ #define LOGDRAG(args)
+ #define LOGDRAW(args)
+
+ #endif /* MOZ_LOGGING */
+
++#ifdef MOZ_WAYLAND
++class nsWaylandDragContext;
++
++gboolean
++WindowDragMotionHandler(GtkWidget *aWidget, GdkDragContext *aDragContext,
++ nsWaylandDragContext *aWaylandDragContext,
++ gint aX, gint aY, guint aTime);
++gboolean
++WindowDragDropHandler(GtkWidget *aWidget, GdkDragContext *aDragContext,
++ nsWaylandDragContext *aWaylandDragContext, gint aX, gint aY,
++ guint aTime);
++void
++WindowDragLeaveHandler(GtkWidget *aWidget);
++#endif
++
+ class gfxPattern;
+
+ namespace mozilla {
+ class TimeStamp;
+ class CurrentX11TimeGetter;
+ }
+
+ class nsWindow final : public nsBaseWidget
+
bgstack15