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"
16 #include "GUniquePtr.h"
18 class nsICookieJarSettings
;
25 } // namespace mozilla
27 class DragData final
{
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
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;
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;
90 nsTArray
<nsString
> mUris
;
94 * GTK native nsIDragSession implementation
96 class nsDragSession
: public nsBaseDragSession
, public nsIObserver
{
98 NS_DECL_ISUPPORTS_INHERITED
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
;
125 explicit AutoEventLoop(RefPtr
<nsDragSession
> aSession
)
126 : mSession(std::move(aSession
)) {
127 nsDragSession::sEventLoopDepth
++;
129 ~AutoEventLoop() { nsDragSession::sEventLoopDepth
--; }
132 static int GetLoopDepth() { return sEventLoopDepth
; };
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).
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
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
;
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
;
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
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;
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
;
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
;
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
,
295 void ScheduleLeaveEvent();
296 gboolean
ScheduleDropEvent(nsWindow
* aWindow
, GdkDragContext
* aDragContext
,
297 mozilla::LayoutDeviceIntPoint aWindowPoint
,
300 nsWindow
* GetMostRecentDestWindow() {
301 return mScheduledTask
== eDragTaskNone
? mTargetWindow
: mPendingWindow
;
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
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
,
326 void SourceBeginDrag(GdkDragContext
* aContext
);
328 // set the drag icon during drag-begin
329 void SetDragIcon(GdkDragContext
* aContext
);
332 virtual ~nsDragSession();
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
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
356 RefPtr
<DragData
> GetDragData(GdkAtom aRequestedFlavor
);
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
,
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();
385 const char* GetDragServiceTaskName(DragTask aTask
);
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
{
398 static already_AddRefed
<nsDragService
> GetInstance();
399 nsIDragSession
* StartDragSession(nsISupports
* aWidgetProvider
) override
;
402 already_AddRefed
<nsIDragSession
> CreateDragSession() override
;
405 #endif // nsDragService_h__