# HG changeset patch # User Martin Stransky # 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 #include #include #include #include @@ -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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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(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 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 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 mClipboardOffer; nsAutoPtr mPrimaryOffer; + RefPtr 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 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(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 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 mPendingWindow; mozilla::LayoutDeviceIntPoint mPendingWindowPoint; nsCountedRef mPendingDragContext; +#ifdef MOZ_WAYLAND + RefPtr 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 mTargetWindow; mozilla::LayoutDeviceIntPoint mTargetWindowPoint; // mTargetWidget and mTargetDragContext are set only while dispatching // motion or drop events. mTime records the corresponding timestamp. nsCountedRef mTargetWidget; nsCountedRef mTargetDragContext; +#ifdef MOZ_WAYLAND + RefPtr mTargetWaylandDragContext; +#endif // mTargetDragContextForRemote is set while waiting for a reply from // a child process. nsCountedRef mTargetDragContextForRemote; +#ifdef MOZ_WAYLAND + RefPtr 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 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 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 window = get_window_for_gtk_widget(aWidget); if (!window) return; RefPtr 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 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 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