Backed out changeset b71c8c052463 (bug 1943846) for causing mass failures. CLOSED...
[gecko.git] / widget / gtk / nsDragService.h
blob284e59a80e1f0c3368f4ef4d073f06054696e5d8
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim: set ts=4 et sw=2 tw=80: */
3 /* This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
7 #ifndef nsDragService_h__
8 #define nsDragService_h__
10 #include "mozilla/RefPtr.h"
11 #include "nsBaseDragService.h"
12 #include "nsCOMArray.h"
13 #include "nsIObserver.h"
14 #include <gtk/gtk.h>
15 #include "nsITimer.h"
16 #include "GUniquePtr.h"
18 class nsICookieJarSettings;
19 class nsWindow;
21 namespace mozilla {
22 namespace gfx {
23 class SourceSurface;
25 } // namespace mozilla
27 class DragData final {
28 public:
29 NS_INLINE_DECL_REFCOUNTING(DragData)
31 explicit DragData(GdkAtom aDataFlavor, const void* aData, uint32_t aDataLen)
32 : mDataFlavor(aDataFlavor),
33 mDragDataLen(aDataLen),
34 mDragData(moz_xmemdup(aData, aDataLen)) {
35 // kURLMime (text/x-moz-url) is received as UTF16 raw data as
36 // Gtk doesn't recognize it as URI format. We need to flip it to URI
37 // format.
38 if (IsURIFlavor()) {
39 ConvertToMozURIList();
42 explicit DragData(GdkAtom aDataFlavor, gchar** aDragUris);
44 GdkAtom GetFlavor() const { return mDataFlavor; }
46 // Try to convert text/uri-list or _NETSCAPE_URL MIME to x-moz-url MIME type
47 // which is used internally.
48 RefPtr<DragData> ConvertToMozURL() const;
50 // Try to convert text/uri-list MIME to application/x-moz-file MIME type.
51 RefPtr<DragData> ConvertToFile() const;
53 bool Export(nsITransferable* aTransferable, uint32_t aItemIndex);
55 bool IsImageFlavor() const;
56 bool IsFileFlavor() const;
57 bool IsTextFlavor() const;
58 bool IsURIFlavor() const;
60 int GetURIsNum() const;
62 bool IsDataValid() const;
64 #ifdef MOZ_LOGGING
65 void Print() const;
66 #endif
68 private:
69 explicit DragData(GdkAtom aDataFlavor) : mDataFlavor(aDataFlavor) {}
70 ~DragData() = default;
72 void ConvertToMozURIList();
74 GdkAtom mDataFlavor = nullptr;
76 bool mAsURIData = false;
78 // In a rare case we export
79 bool mDragDataDOMEndings = false;
81 // Data obtained from Gtk
82 uint32_t mDragDataLen = 0;
83 mozilla::UniqueFreePtr<void> mDragData;
84 mozilla::GUniquePtr<gchar*> mDragUris;
86 // Data which can be passed to transferable. In some cases we can use Gtk data
87 // directly but in most cases we need to do UTF8/UTF16 conversion
88 // and perform line break;
89 nsString mData;
90 nsTArray<nsString> mUris;
93 /**
94 * GTK native nsIDragSession implementation
96 class nsDragSession : public nsBaseDragSession, public nsIObserver {
97 public:
98 NS_DECL_ISUPPORTS_INHERITED
99 NS_DECL_NSIOBSERVER
101 // nsIDragSession
102 NS_IMETHOD SetCanDrop(bool aCanDrop) override;
103 NS_IMETHOD GetCanDrop(bool* aCanDrop) override;
104 NS_IMETHOD GetNumDropItems(uint32_t* aNumItems) override;
105 NS_IMETHOD GetData(nsITransferable* aTransferable,
106 uint32_t aItemIndex) override;
107 NS_IMETHOD IsDataFlavorSupported(const char* aDataFlavor,
108 bool* _retval) override;
110 // Update Drag&Drop state according child process state.
111 // UpdateDragEffect() is called by IPC bridge when child process
112 // accepts/denies D&D operation and uses stored
113 // mTargetDragContextForRemote context.
114 NS_IMETHOD UpdateDragEffect() override;
116 nsAutoCString GetDebugTag() const;
118 MOZ_CAN_RUN_SCRIPT nsresult
119 EndDragSessionImpl(bool aDoneDrag, uint32_t aKeyModifiers) override;
121 class AutoEventLoop {
122 RefPtr<nsDragSession> mSession;
124 public:
125 explicit AutoEventLoop(RefPtr<nsDragSession> aSession)
126 : mSession(std::move(aSession)) {
127 nsDragSession::sEventLoopDepth++;
129 ~AutoEventLoop() { nsDragSession::sEventLoopDepth--; }
132 static int GetLoopDepth() { return sEventLoopDepth; };
134 protected:
135 // mScheduledTask indicates what signal has been received from GTK and
136 // so what needs to be dispatched when the scheduled task is run. It is
137 // eDragTaskNone when there is no task scheduled (but the
138 // previous task may still not have finished running).
139 enum DragTask {
140 eDragTaskNone,
141 eDragTaskMotion,
142 eDragTaskLeave,
143 eDragTaskDrop,
144 eDragTaskSourceEnd
147 void ReplyToDragMotion(GdkDragContext* aDragContext, guint aTime);
148 void ReplyToDragMotion();
150 void GetDragFlavors(nsTArray<nsCString>& aFlavors);
151 // this will get the native data from the last target given a
152 // specific flavor
153 void GetTargetDragData(GdkAtom aFlavor, nsTArray<nsCString>& aDropFlavors,
154 bool aResetTargetData = true);
155 // this will reset all of the target vars
156 void TargetResetData(void);
158 // is the current target drag context contain a list?
159 bool IsTargetContextList(void);
161 // Ensure our data cache belongs to aDragContext and clear the cache if
162 // aDragContext is different than mCachedDragContext.
163 void EnsureCachedDataValidForContext(GdkDragContext* aDragContext);
165 static gboolean TaskRemoveTempFiles(gpointer data);
167 bool RemoveTempFiles();
169 // Where the drag begins. We need to keep it open on Wayland.
170 RefPtr<nsWindow> mSourceWindow;
172 // mTargetWindow and mTargetWindowPoint record the position of the last
173 // eDragTaskMotion or eDragTaskDrop task that was run or is still running.
174 // mTargetWindow is cleared once the drag has completed or left.
175 RefPtr<nsWindow> mTargetWindow;
177 // our source data items
178 nsCOMPtr<nsIArray> mSourceDataItems;
180 // mTargetWidget and mTargetDragContext are set only while dispatching
181 // motion or drop events. mTime records the corresponding timestamp.
182 RefPtr<GtkWidget> mTargetWidget;
183 RefPtr<GdkDragContext> mTargetDragContext;
185 // last data received and its length
186 void* mTargetDragData = nullptr;
187 uint32_t mTargetDragDataLen = 0;
189 // have we received our drag data?
190 bool mTargetDragDataReceived = false;
192 // When we route D'n'D request to child process
193 // (by EventStateManager::DispatchCrossProcessEvent)
194 // we save GdkDragContext to mTargetDragContextForRemote.
195 // When we get a reply from child process we use
196 // the stored GdkDragContext to send reply to OS.
198 // We need to store GdkDragContext because mTargetDragContext is cleared
199 // after every D'n'D event.
200 RefPtr<GdkDragContext> mTargetDragContextForRemote;
201 guint mTargetTime;
203 mozilla::GUniquePtr<gchar*> mTargetDragUris = nullptr;
205 nsTHashMap<nsCStringHashKey, mozilla::GUniquePtr<gchar*>> mCachedUris;
207 // We cache all data for the current drag context,
208 // because waiting for the data in GetTargetDragData can be very slow.
209 nsTHashMap<nsCStringHashKey, nsTArray<uint8_t>> mCachedData;
211 DragTask mScheduledTask = eDragTaskNone;
212 bool mScheduledTaskIsRunning = false;
214 // mPendingWindow, mPendingWindowPoint, mPendingDragContext, and
215 // mPendingTime, carry information from the GTK signal that will be used
216 // when the scheduled task is run. mPendingWindow and mPendingDragContext
217 // will be nullptr if the scheduled task is eDragTaskLeave.
218 RefPtr<nsWindow> mPendingWindow;
219 mozilla::LayoutDeviceIntPoint mPendingWindowPoint;
220 RefPtr<GdkDragContext> mPendingDragContext;
222 guint mPendingTime;
224 // mTaskSource is the GSource id for the task that is either scheduled
225 // or currently running. It is 0 if no task is scheduled or running.
226 guint mTaskSource = 0;
228 // stores all temporary files
229 nsCOMArray<nsIFile> mTemporaryFiles;
230 // timer to trigger deletion of temporary files
231 guint mTempFileTimerID;
232 // the url of the temporary file that has been created in the current drag
233 // session
234 nsTArray<nsCString> mTempFileUrls;
236 // mCachedData are tied to mCachedDragContext. mCachedDragContext is not
237 // ref counted and may be already deleted on Gtk side.
238 // We used it for mCachedData invalidation only and can't be used for
239 // any D&D operation.
240 uintptr_t mCachedDragContext = 0;
242 // How deep we're nested in event loops
243 static int sEventLoopDepth;
245 // is it OK to drop on us?
246 bool mCanDrop = false;
248 public:
249 static GdkAtom sJPEGImageMimeAtom;
250 static GdkAtom sJPGImageMimeAtom;
251 static GdkAtom sPNGImageMimeAtom;
252 static GdkAtom sGIFImageMimeAtom;
253 static GdkAtom sCustomTypesMimeAtom;
254 static GdkAtom sURLMimeAtom;
255 static GdkAtom sRTFMimeAtom;
256 static GdkAtom sTextMimeAtom;
257 static GdkAtom sMozUrlTypeAtom;
258 static GdkAtom sMimeListTypeAtom;
259 static GdkAtom sTextUriListTypeAtom;
260 static GdkAtom sTextPlainUTF8TypeAtom;
261 static GdkAtom sXdndDirectSaveTypeAtom;
262 static GdkAtom sTabDropTypeAtom;
263 static GdkAtom sFileMimeAtom;
264 static GdkAtom sPortalFileAtom;
265 static GdkAtom sPortalFileTransferAtom;
266 static GdkAtom sFilePromiseURLMimeAtom;
267 static GdkAtom sFilePromiseMimeAtom;
268 static GdkAtom sNativeImageMimeAtom;
270 nsDragSession();
272 // nsBaseDragSession
273 MOZ_CAN_RUN_SCRIPT virtual nsresult InvokeDragSessionImpl(
274 nsIWidget* aWidget, nsIArray* anArrayTransferables,
275 const mozilla::Maybe<mozilla::CSSIntRegion>& aRegion,
276 uint32_t aActionType) override;
278 // nsIDragSession
279 MOZ_CAN_RUN_SCRIPT NS_IMETHOD InvokeDragSession(
280 nsIWidget* aWidget, nsINode* aDOMNode, nsIPrincipal* aPrincipal,
281 nsIContentSecurityPolicy* aCsp, nsICookieJarSettings* aCookieJarSettings,
282 nsIArray* anArrayTransferables, uint32_t aActionType,
283 nsContentPolicyType aContentPolicyType) override;
285 // Methods called from nsWindow to handle responding to GTK drag
286 // destination signals
288 void TargetDataReceived(GtkWidget* aWidget, GdkDragContext* aContext, gint aX,
289 gint aY, GtkSelectionData* aSelection_data,
290 guint aInfo, guint32 aTime);
292 gboolean ScheduleMotionEvent(nsWindow* aWindow, GdkDragContext* aDragContext,
293 mozilla::LayoutDeviceIntPoint aWindowPoint,
294 guint aTime);
295 void ScheduleLeaveEvent();
296 gboolean ScheduleDropEvent(nsWindow* aWindow, GdkDragContext* aDragContext,
297 mozilla::LayoutDeviceIntPoint aWindowPoint,
298 guint aTime);
300 nsWindow* GetMostRecentDestWindow() {
301 return mScheduledTask == eDragTaskNone ? mTargetWindow : mPendingWindow;
304 // END PUBLIC API
306 // These methods are public only so that they can be called from functions
307 // with C calling conventions. They are called for drags started with the
308 // invisible widget.
309 void SourceEndDragSession(GdkDragContext* aContext, gint aResult);
310 void SourceDataGet(GtkWidget* widget, GdkDragContext* context,
311 GtkSelectionData* selection_data, guint32 aTime);
312 bool SourceDataGetText(nsITransferable* aItem, const nsACString& aMIMEType,
313 bool aNeedToDoConversionToPlainText,
314 GtkSelectionData* aSelectionData);
315 bool SourceDataGetImage(nsITransferable* aItem,
316 GtkSelectionData* aSelectionData);
317 bool SourceDataGetXDND(nsITransferable* aItem, GdkDragContext* aContext,
318 GtkSelectionData* aSelectionData);
319 void SourceDataGetUriList(GdkDragContext* aContext,
320 GtkSelectionData* aSelectionData,
321 uint32_t aDragItems);
322 bool SourceDataAppendURLFileItem(nsACString& aURI, nsITransferable* aItem);
323 bool SourceDataAppendURLItem(nsITransferable* aItem, bool aExternalDrop,
324 nsACString& aURI);
326 void SourceBeginDrag(GdkDragContext* aContext);
328 // set the drag icon during drag-begin
329 void SetDragIcon(GdkDragContext* aContext);
331 protected:
332 virtual ~nsDragSession();
334 private:
335 // target/destination side vars
336 // These variables keep track of the state of the current drag.
338 // mCachedDragData/mCachedDragFlavors are tied to mCachedDragContext.
339 // mCachedDragContext is not ref counted and may be already deleted
340 // on Gtk side.
341 // We used it for mCachedDragData/mCachedDragFlavors invalidation
342 // only and can't be used for any D&D operation.
343 nsTHashMap<void*, RefPtr<DragData>> mCachedDragData;
344 nsTArray<GdkAtom> mCachedDragFlavors;
346 void SetCachedDragContext(GdkDragContext* aDragContext);
348 mozilla::LayoutDeviceIntPoint mTargetWindowPoint;
350 int mWaitingForDragDataRequests = 0;
352 bool IsDragFlavorAvailable(GdkAtom aRequestedFlavor);
354 // this will get the native data from the last target given a
355 // specific flavor
356 RefPtr<DragData> GetDragData(GdkAtom aRequestedFlavor);
358 // source side vars
360 // the source of our drags
361 GtkWidget* mHiddenWidget;
363 // get a list of the sources in gtk's format
364 GtkTargetList* GetSourceList(void);
366 // attempts to create a semi-transparent drag image. Returns TRUE if
367 // successful, FALSE if not
368 bool SetAlphaPixmap(mozilla::gfx::SourceSurface* aPixbuf,
369 GdkDragContext* aContext, int32_t aXOffset,
370 int32_t aYOffset,
371 const mozilla::LayoutDeviceIntRect& dragRect);
373 gboolean Schedule(DragTask aTask, nsWindow* aWindow,
374 GdkDragContext* aDragContext,
375 mozilla::LayoutDeviceIntPoint aWindowPoint, guint aTime);
377 // Callback for g_idle_add_full() to run mScheduledTask.
378 MOZ_CAN_RUN_SCRIPT static gboolean TaskDispatchCallback(gpointer data);
379 MOZ_CAN_RUN_SCRIPT gboolean RunScheduledTask();
380 MOZ_CAN_RUN_SCRIPT void DispatchMotionEvents();
381 void UpdateDragAction(GdkDragContext* aDragContext);
382 void UpdateDragAction();
384 #ifdef MOZ_LOGGING
385 const char* GetDragServiceTaskName(DragTask aTask);
386 #endif
387 gboolean DispatchDropEvent();
388 static uint32_t GetCurrentModifiers();
390 nsresult CreateTempFile(nsITransferable* aItem, nsACString& aURI);
394 * Native GTK nsIDragService implementation
396 class nsDragService : public nsBaseDragService {
397 public:
398 static already_AddRefed<nsDragService> GetInstance();
399 nsIDragSession* StartDragSession(nsISupports* aWidgetProvider) override;
401 protected:
402 already_AddRefed<nsIDragSession> CreateDragSession() override;
405 #endif // nsDragService_h__