Coverage Report

Created: 2018-09-25 14:53

/src/mozilla-central/widget/gtk/nsDragService.cpp
Line
Count
Source (jump to first uncovered line)
1
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2
/* vim: set ts=4 et sw=4 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/. */
6
7
#include "nsDragService.h"
8
#include "nsArrayUtils.h"
9
#include "nsIObserverService.h"
10
#include "nsWidgetsCID.h"
11
#include "nsWindow.h"
12
#include "nsIServiceManager.h"
13
#include "nsXPCOM.h"
14
#include "nsISupportsPrimitives.h"
15
#include "nsIIOService.h"
16
#include "nsIFileURL.h"
17
#include "nsNetUtil.h"
18
#include "mozilla/Logging.h"
19
#include "nsTArray.h"
20
#include "nsPrimitiveHelpers.h"
21
#include "prtime.h"
22
#include "prthread.h"
23
#include <dlfcn.h>
24
#include <gtk/gtk.h>
25
#include <gdk/gdkx.h>
26
#include "nsCRT.h"
27
#include "mozilla/BasicEvents.h"
28
#include "mozilla/Services.h"
29
#include "mozilla/ClearOnShutdown.h"
30
31
#include "gfxXlibSurface.h"
32
#include "gfxContext.h"
33
#include "nsImageToPixbuf.h"
34
#include "nsPresContext.h"
35
#include "nsIContent.h"
36
#include "nsIDocument.h"
37
#include "nsViewManager.h"
38
#include "nsIFrame.h"
39
#include "nsGtkUtils.h"
40
#include "nsGtkKeyUtils.h"
41
#include "mozilla/gfx/2D.h"
42
#include "gfxPlatform.h"
43
#include "ScreenHelperGTK.h"
44
#include "nsArrayUtils.h"
45
#ifdef MOZ_WAYLAND
46
#include "nsClipboardWayland.h"
47
#endif
48
49
using namespace mozilla;
50
using namespace mozilla::gfx;
51
52
// This sets how opaque the drag image is
53
0
#define DRAG_IMAGE_ALPHA_LEVEL 0.5
54
55
// These values are copied from GtkDragResult (rather than using GtkDragResult
56
// directly) so that this code can be compiled against versions of GTK+ that
57
// do not have GtkDragResult.
58
// GtkDragResult is available from GTK+ version 2.12.
59
enum {
60
  MOZ_GTK_DRAG_RESULT_SUCCESS,
61
  MOZ_GTK_DRAG_RESULT_NO_TARGET
62
};
63
64
static LazyLogModule sDragLm("nsDragService");
65
66
// data used for synthetic periodic motion events sent to the source widget
67
// grabbing real events for the drag.
68
static guint sMotionEventTimerID;
69
static GdkEvent *sMotionEvent;
70
static GtkWidget *sGrabWidget;
71
72
static const char gMimeListType[] = "application/x-moz-internal-item-list";
73
static const char gMozUrlType[] = "_NETSCAPE_URL";
74
static const char gTextUriListType[] = "text/uri-list";
75
static const char gTextPlainUTF8Type[] = "text/plain;charset=utf-8";
76
77
static void
78
invisibleSourceDragBegin(GtkWidget        *aWidget,
79
                         GdkDragContext   *aContext,
80
                         gpointer          aData);
81
82
static void
83
invisibleSourceDragEnd(GtkWidget        *aWidget,
84
                       GdkDragContext   *aContext,
85
                       gpointer          aData);
86
87
static gboolean
88
invisibleSourceDragFailed(GtkWidget        *aWidget,
89
                          GdkDragContext   *aContext,
90
                          gint              aResult,
91
                          gpointer          aData);
92
93
static void
94
invisibleSourceDragDataGet(GtkWidget        *aWidget,
95
                           GdkDragContext   *aContext,
96
                           GtkSelectionData *aSelectionData,
97
                           guint             aInfo,
98
                           guint32           aTime,
99
                           gpointer          aData);
100
101
nsDragService::nsDragService()
102
    : mScheduledTask(eDragTaskNone)
103
    , mTaskSource(0)
104
#ifdef MOZ_WAYLAND
105
    , mPendingWaylandDragContext(nullptr)
106
    , mTargetWaylandDragContext(nullptr)
107
#endif
108
0
{
109
0
    // We have to destroy the hidden widget before the event loop stops
110
0
    // running.
111
0
    nsCOMPtr<nsIObserverService> obsServ =
112
0
        mozilla::services::GetObserverService();
113
0
    obsServ->AddObserver(this, "quit-application", false);
114
0
115
0
    // our hidden source widget
116
0
    // Using an offscreen window works around bug 983843.
117
0
    mHiddenWidget = gtk_offscreen_window_new();
118
0
    // make sure that the widget is realized so that
119
0
    // we can use it as a drag source.
120
0
    gtk_widget_realize(mHiddenWidget);
121
0
    // hook up our internal signals so that we can get some feedback
122
0
    // from our drag source
123
0
    g_signal_connect(mHiddenWidget, "drag_begin",
124
0
                     G_CALLBACK(invisibleSourceDragBegin), this);
125
0
    g_signal_connect(mHiddenWidget, "drag_data_get",
126
0
                     G_CALLBACK(invisibleSourceDragDataGet), this);
127
0
    g_signal_connect(mHiddenWidget, "drag_end",
128
0
                     G_CALLBACK(invisibleSourceDragEnd), this);
129
0
    // drag-failed is available from GTK+ version 2.12
130
0
    guint dragFailedID = g_signal_lookup("drag-failed",
131
0
                                         G_TYPE_FROM_INSTANCE(mHiddenWidget));
132
0
    if (dragFailedID) {
133
0
        g_signal_connect_closure_by_id(mHiddenWidget, dragFailedID, 0,
134
0
                                       g_cclosure_new(G_CALLBACK(invisibleSourceDragFailed),
135
0
                                                      this, nullptr),
136
0
                                       FALSE);
137
0
    }
138
0
139
0
    // set up our logging module
140
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::nsDragService"));
141
0
    mCanDrop = false;
142
0
    mTargetDragDataReceived = false;
143
0
    mTargetDragData = 0;
144
0
    mTargetDragDataLen = 0;
145
0
}
146
147
nsDragService::~nsDragService()
148
0
{
149
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::~nsDragService"));
150
0
    if (mTaskSource)
151
0
        g_source_remove(mTaskSource);
152
0
153
0
}
154
155
NS_IMPL_ISUPPORTS_INHERITED(nsDragService, nsBaseDragService, nsIObserver)
156
157
mozilla::StaticRefPtr<nsDragService> sDragServiceInstance;
158
/* static */ already_AddRefed<nsDragService>
159
nsDragService::GetInstance()
160
0
{
161
0
  if (gfxPlatform::IsHeadless()) {
162
0
    return nullptr;
163
0
  }
164
0
  if (!sDragServiceInstance) {
165
0
    sDragServiceInstance = new nsDragService();
166
0
    ClearOnShutdown(&sDragServiceInstance);
167
0
  }
168
0
169
0
  RefPtr<nsDragService> service = sDragServiceInstance.get();
170
0
  return service.forget();
171
0
}
172
173
// nsIObserver
174
175
NS_IMETHODIMP
176
nsDragService::Observe(nsISupports *aSubject, const char *aTopic,
177
                       const char16_t *aData)
178
0
{
179
0
  if (!nsCRT::strcmp(aTopic, "quit-application")) {
180
0
    MOZ_LOG(sDragLm, LogLevel::Debug,
181
0
           ("nsDragService::Observe(\"quit-application\")"));
182
0
    if (mHiddenWidget) {
183
0
      gtk_widget_destroy(mHiddenWidget);
184
0
      mHiddenWidget = 0;
185
0
    }
186
0
    TargetResetData();
187
0
  } else {
188
0
    MOZ_ASSERT_UNREACHABLE("unexpected topic");
189
0
    return NS_ERROR_UNEXPECTED;
190
0
  }
191
0
192
0
  return NS_OK;
193
0
}
194
195
// Support for periodic drag events
196
197
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
198
// and the Xdnd protocol both recommend that drag events are sent periodically,
199
// but GTK does not normally provide this.
200
//
201
// Here GTK is periodically stimulated by copies of the most recent mouse
202
// motion events so as to send drag position messages to the destination when
203
// appropriate (after it has received a status event from the previous
204
// message).
205
//
206
// (If events were sent only on the destination side then the destination
207
// would have no message to which it could reply with a drag status.  Without
208
// sending a drag status to the source, the destination would not be able to
209
// change its feedback re whether it could accept the drop, and so the
210
// source's behavior on drop will not be consistent.)
211
212
static gboolean
213
DispatchMotionEventCopy(gpointer aData)
214
0
{
215
0
    // Clear the timer id before OnSourceGrabEventAfter is called during event
216
0
    // dispatch.
217
0
    sMotionEventTimerID = 0;
218
0
219
0
    GdkEvent *event = sMotionEvent;
220
0
    sMotionEvent = nullptr;
221
0
    // If there is no longer a grab on the widget, then the drag is over and
222
0
    // there is no need to continue drag motion.
223
0
    if (gtk_widget_has_grab(sGrabWidget)) {
224
0
        gtk_propagate_event(sGrabWidget, event);
225
0
    }
226
0
    gdk_event_free(event);
227
0
228
0
    // Cancel this timer;
229
0
    // We've already started another if the motion event was dispatched.
230
0
    return FALSE;
231
0
}
232
233
static void
234
OnSourceGrabEventAfter(GtkWidget *widget, GdkEvent *event, gpointer user_data)
235
0
{
236
0
    // If there is no longer a grab on the widget, then the drag motion is
237
0
    // over (though the data may not be fetched yet).
238
0
    if (!gtk_widget_has_grab(sGrabWidget))
239
0
        return;
240
0
241
0
    if (event->type == GDK_MOTION_NOTIFY) {
242
0
        if (sMotionEvent) {
243
0
            gdk_event_free(sMotionEvent);
244
0
        }
245
0
        sMotionEvent = gdk_event_copy(event);
246
0
247
0
        // Update the cursor position.  The last of these recorded gets used for
248
0
        // the eDragEnd event.
249
0
        nsDragService *dragService = static_cast<nsDragService*>(user_data);
250
0
        gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
251
0
        auto p = LayoutDeviceIntPoint::Round(event->motion.x_root * scale,
252
0
                                             event->motion.y_root * scale);
253
0
        dragService->SetDragEndPoint(p);
254
0
    } else if (sMotionEvent && (event->type == GDK_KEY_PRESS ||
255
0
                                event->type == GDK_KEY_RELEASE)) {
256
0
        // Update modifier state from key events.
257
0
        sMotionEvent->motion.state = event->key.state;
258
0
    } else {
259
0
        return;
260
0
    }
261
0
262
0
    if (sMotionEventTimerID) {
263
0
        g_source_remove(sMotionEventTimerID);
264
0
    }
265
0
266
0
    // G_PRIORITY_DEFAULT_IDLE is lower priority than GDK's redraw idle source
267
0
    // and lower than GTK's idle source that sends drag position messages after
268
0
    // motion-notify signals.
269
0
    //
270
0
    // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
271
0
    // recommends an interval of 350ms +/- 200ms.
272
0
    sMotionEventTimerID =
273
0
        g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 350,
274
0
                           DispatchMotionEventCopy, nullptr, nullptr);
275
0
}
276
277
static GtkWindow*
278
GetGtkWindow(nsIDocument *aDocument)
279
0
{
280
0
    if (!aDocument)
281
0
        return nullptr;
282
0
283
0
    nsCOMPtr<nsIPresShell> presShell = aDocument->GetShell();
284
0
    if (!presShell)
285
0
        return nullptr;
286
0
287
0
    RefPtr<nsViewManager> vm = presShell->GetViewManager();
288
0
    if (!vm)
289
0
        return nullptr;
290
0
291
0
    nsCOMPtr<nsIWidget> widget;
292
0
    vm->GetRootWidget(getter_AddRefs(widget));
293
0
    if (!widget)
294
0
        return nullptr;
295
0
296
0
    GtkWidget *gtkWidget =
297
0
        static_cast<nsWindow*>(widget.get())->GetMozContainerWidget();
298
0
    if (!gtkWidget)
299
0
        return nullptr;
300
0
301
0
    GtkWidget *toplevel = nullptr;
302
0
    toplevel = gtk_widget_get_toplevel(gtkWidget);
303
0
    if (!GTK_IS_WINDOW(toplevel))
304
0
        return nullptr;
305
0
306
0
    return GTK_WINDOW(toplevel);
307
0
}
308
309
// nsIDragService
310
311
NS_IMETHODIMP
312
nsDragService::InvokeDragSession(nsINode *aDOMNode,
313
                                 const nsACString& aPrincipalURISpec,
314
                                 nsIArray * aArrayTransferables,
315
                                 uint32_t aActionType,
316
                                 nsContentPolicyType aContentPolicyType =
317
                                   nsIContentPolicy::TYPE_OTHER)
318
0
{
319
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::InvokeDragSession"));
320
0
321
0
    // If the previous source drag has not yet completed, signal handlers need
322
0
    // to be removed from sGrabWidget and dragend needs to be dispatched to
323
0
    // the source node, but we can't call EndDragSession yet because we don't
324
0
    // know whether or not the drag succeeded.
325
0
    if (mSourceNode)
326
0
        return NS_ERROR_NOT_AVAILABLE;
327
0
328
0
    return nsBaseDragService::InvokeDragSession(aDOMNode, aPrincipalURISpec,
329
0
                                                aArrayTransferables,
330
0
                                                aActionType,
331
0
                                                aContentPolicyType);
332
0
}
333
334
// nsBaseDragService
335
nsresult
336
nsDragService::InvokeDragSessionImpl(nsIArray* aArrayTransferables,
337
                                     const Maybe<CSSIntRegion>& aRegion,
338
                                     uint32_t aActionType)
339
0
{
340
0
    // make sure that we have an array of transferables to use
341
0
    if (!aArrayTransferables)
342
0
        return NS_ERROR_INVALID_ARG;
343
0
    // set our reference to the transferables.  this will also addref
344
0
    // the transferables since we're going to hang onto this beyond the
345
0
    // length of this call
346
0
    mSourceDataItems = aArrayTransferables;
347
0
    // get the list of items we offer for drags
348
0
    GtkTargetList *sourceList = GetSourceList();
349
0
350
0
    if (!sourceList)
351
0
        return NS_OK;
352
0
353
0
    // save our action type
354
0
    GdkDragAction action = GDK_ACTION_DEFAULT;
355
0
356
0
    if (aActionType & DRAGDROP_ACTION_COPY)
357
0
        action = (GdkDragAction)(action | GDK_ACTION_COPY);
358
0
    if (aActionType & DRAGDROP_ACTION_MOVE)
359
0
        action = (GdkDragAction)(action | GDK_ACTION_MOVE);
360
0
    if (aActionType & DRAGDROP_ACTION_LINK)
361
0
        action = (GdkDragAction)(action | GDK_ACTION_LINK);
362
0
363
0
    // Create a fake event for the drag so we can pass the time (so to speak).
364
0
    // If we don't do this, then, when the timestamp for the pending button
365
0
    // release event is used for the ungrab, the ungrab can fail due to the
366
0
    // timestamp being _earlier_ than CurrentTime.
367
0
    GdkEvent event;
368
0
    memset(&event, 0, sizeof(GdkEvent));
369
0
    event.type = GDK_BUTTON_PRESS;
370
0
    event.button.window = gtk_widget_get_window(mHiddenWidget);
371
0
    event.button.time = nsWindow::GetLastUserInputTime();
372
0
373
0
    // Put the drag widget in the window group of the source node so that the
374
0
    // gtk_grab_add during gtk_drag_begin is effective.
375
0
    // gtk_window_get_group(nullptr) returns the default window group.
376
0
    GtkWindowGroup *window_group =
377
0
        gtk_window_get_group(GetGtkWindow(mSourceDocument));
378
0
    gtk_window_group_add_window(window_group,
379
0
                                GTK_WINDOW(mHiddenWidget));
380
0
381
0
#ifdef MOZ_WIDGET_GTK
382
0
    // Get device for event source
383
0
    GdkDisplay *display = gdk_display_get_default();
384
0
    GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
385
0
    event.button.device = gdk_device_manager_get_client_pointer(device_manager);
386
0
#endif
387
0
388
0
    // start our drag.
389
0
    GdkDragContext *context = gtk_drag_begin(mHiddenWidget,
390
0
                                             sourceList,
391
0
                                             action,
392
0
                                             1,
393
0
                                             &event);
394
0
395
0
    nsresult rv;
396
0
    if (context) {
397
0
        StartDragSession();
398
0
399
0
        // GTK uses another hidden window for receiving mouse events.
400
0
        sGrabWidget = gtk_window_group_get_current_grab(window_group);
401
0
        if (sGrabWidget) {
402
0
            g_object_ref(sGrabWidget);
403
0
            // Only motion and key events are required but connect to
404
0
            // "event-after" as this is never blocked by other handlers.
405
0
            g_signal_connect(sGrabWidget, "event-after",
406
0
                             G_CALLBACK(OnSourceGrabEventAfter), this);
407
0
        }
408
0
        // We don't have a drag end point yet.
409
0
        mEndDragPoint = LayoutDeviceIntPoint(-1, -1);
410
0
        rv = NS_OK;
411
0
    }
412
0
    else {
413
0
        rv = NS_ERROR_FAILURE;
414
0
    }
415
0
416
0
    gtk_target_list_unref(sourceList);
417
0
418
0
    return rv;
419
0
}
420
421
bool
422
nsDragService::SetAlphaPixmap(SourceSurface *aSurface,
423
                              GdkDragContext *aContext,
424
                              int32_t aXOffset,
425
                              int32_t aYOffset,
426
                              const LayoutDeviceIntRect& dragRect)
427
0
{
428
0
    GdkScreen* screen = gtk_widget_get_screen(mHiddenWidget);
429
0
430
0
    // Transparent drag icons need, like a lot of transparency-related things,
431
0
    // a compositing X window manager
432
0
    if (!gdk_screen_is_composited(screen))
433
0
      return false;
434
0
435
#ifdef cairo_image_surface_create
436
#error "Looks like we're including Mozilla's cairo instead of system cairo"
437
#endif
438
    // Prior to GTK 3.9.12, cairo surfaces passed into gtk_drag_set_icon_surface
439
0
    // had their shape information derived from the alpha channel and used with
440
0
    // the X SHAPE extension instead of being displayed as an ARGB window.
441
0
    // See bug 1249604.
442
0
    if (gtk_check_version(3, 9, 12))
443
0
      return false;
444
0
445
0
    // TODO: grab X11 pixmap or image data instead of expensive readback.
446
0
    cairo_surface_t *surf = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
447
0
                                                       dragRect.width,
448
0
                                                       dragRect.height);
449
0
    if (!surf)
450
0
        return false;
451
0
452
0
    RefPtr<DrawTarget> dt = gfxPlatform::CreateDrawTargetForData(
453
0
                                cairo_image_surface_get_data(surf),
454
0
                                nsIntSize(dragRect.width, dragRect.height),
455
0
                                cairo_image_surface_get_stride(surf),
456
0
                                SurfaceFormat::B8G8R8A8);
457
0
    if (!dt)
458
0
        return false;
459
0
460
0
    dt->ClearRect(Rect(0, 0, dragRect.width, dragRect.height));
461
0
    dt->DrawSurface(aSurface,
462
0
                    Rect(0, 0, dragRect.width, dragRect.height),
463
0
                    Rect(0, 0, dragRect.width, dragRect.height),
464
0
                    DrawSurfaceOptions(),
465
0
                    DrawOptions(DRAG_IMAGE_ALPHA_LEVEL, CompositionOp::OP_SOURCE));
466
0
467
0
    cairo_surface_mark_dirty(surf);
468
0
    cairo_surface_set_device_offset(surf, -aXOffset, -aYOffset);
469
0
470
0
    // Ensure that the surface is drawn at the correct scale on HiDPI displays.
471
0
    static auto sCairoSurfaceSetDeviceScalePtr =
472
0
        (void (*)(cairo_surface_t*,double,double))
473
0
        dlsym(RTLD_DEFAULT, "cairo_surface_set_device_scale");
474
0
    if (sCairoSurfaceSetDeviceScalePtr) {
475
0
        gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
476
0
        sCairoSurfaceSetDeviceScalePtr(surf, scale, scale);
477
0
    }
478
0
479
0
    gtk_drag_set_icon_surface(aContext, surf);
480
0
    cairo_surface_destroy(surf);
481
0
    return true;
482
0
}
483
484
NS_IMETHODIMP
485
nsDragService::StartDragSession()
486
0
{
487
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::StartDragSession"));
488
0
    return nsBaseDragService::StartDragSession();
489
0
}
490
491
NS_IMETHODIMP
492
nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers)
493
0
{
494
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::EndDragSession %d",
495
0
                                   aDoneDrag));
496
0
497
0
    if (sGrabWidget) {
498
0
        g_signal_handlers_disconnect_by_func(sGrabWidget,
499
0
             FuncToGpointer(OnSourceGrabEventAfter), this);
500
0
        g_object_unref(sGrabWidget);
501
0
        sGrabWidget = nullptr;
502
0
503
0
        if (sMotionEventTimerID) {
504
0
            g_source_remove(sMotionEventTimerID);
505
0
            sMotionEventTimerID = 0;
506
0
        }
507
0
        if (sMotionEvent) {
508
0
            gdk_event_free(sMotionEvent);
509
0
            sMotionEvent = nullptr;
510
0
        }
511
0
    }
512
0
513
0
    // unset our drag action
514
0
    SetDragAction(DRAGDROP_ACTION_NONE);
515
0
516
0
    // We're done with the drag context.
517
0
    mTargetDragContextForRemote = nullptr;
518
#ifdef MOZ_WAYLAND
519
    mTargetWaylandDragContextForRemote = nullptr;
520
#endif
521
522
0
    return nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers);
523
0
}
524
525
// nsIDragSession
526
NS_IMETHODIMP
527
nsDragService::SetCanDrop(bool aCanDrop)
528
0
{
529
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::SetCanDrop %d",
530
0
                                   aCanDrop));
531
0
    mCanDrop = aCanDrop;
532
0
    return NS_OK;
533
0
}
534
535
NS_IMETHODIMP
536
nsDragService::GetCanDrop(bool *aCanDrop)
537
0
{
538
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::GetCanDrop"));
539
0
    *aCanDrop = mCanDrop;
540
0
    return NS_OK;
541
0
}
542
543
static void
544
UTF16ToNewUTF8(const char16_t* aUTF16,
545
               uint32_t aUTF16Len,
546
               char** aUTF8,
547
               uint32_t* aUTF8Len)
548
0
{
549
0
  nsDependentSubstring utf16(aUTF16, aUTF16Len);
550
0
  *aUTF8 = ToNewUTF8String(utf16, aUTF8Len);
551
0
}
552
553
static void
554
UTF8ToNewUTF16(const char* aUTF8,
555
               uint32_t aUTF8Len,
556
               char16_t** aUTF16,
557
               uint32_t* aUTF16Len)
558
0
{
559
0
  nsDependentCSubstring utf8(aUTF8, aUTF8Len);
560
0
  *aUTF16 = UTF8ToNewUnicode(utf8, aUTF16Len);
561
0
}
562
563
// count the number of URIs in some text/uri-list format data.
564
static uint32_t
565
CountTextUriListItems(const char *data,
566
                      uint32_t datalen)
567
0
{
568
0
    const char *p = data;
569
0
    const char *endPtr = p + datalen;
570
0
    uint32_t count = 0;
571
0
572
0
    while (p < endPtr) {
573
0
        // skip whitespace (if any)
574
0
        while (p < endPtr && *p != '\0' && isspace(*p))
575
0
            p++;
576
0
        // if we aren't at the end of the line ...
577
0
        if (p != endPtr && *p != '\0' && *p != '\n' && *p != '\r')
578
0
            count++;
579
0
        // skip to the end of the line
580
0
        while (p < endPtr && *p != '\0' && *p != '\n')
581
0
            p++;
582
0
        p++; // skip the actual newline as well.
583
0
    }
584
0
    return count;
585
0
}
586
587
// extract an item from text/uri-list formatted data and convert it to
588
// unicode.
589
static void
590
GetTextUriListItem(const char *data,
591
                   uint32_t datalen,
592
                   uint32_t aItemIndex,
593
                   char16_t **convertedText,
594
                   uint32_t *convertedTextLen)
595
0
{
596
0
    const char *p = data;
597
0
    const char *endPtr = p + datalen;
598
0
    unsigned int count = 0;
599
0
600
0
    *convertedText = nullptr;
601
0
    while (p < endPtr) {
602
0
        // skip whitespace (if any)
603
0
        while (p < endPtr && *p != '\0' && isspace(*p))
604
0
            p++;
605
0
        // if we aren't at the end of the line, we have a url
606
0
        if (p != endPtr && *p != '\0' && *p != '\n' && *p != '\r')
607
0
            count++;
608
0
        // this is the item we are after ...
609
0
        if (aItemIndex + 1 == count) {
610
0
            const char *q = p;
611
0
            while (q < endPtr && *q != '\0' && *q != '\n' && *q != '\r')
612
0
              q++;
613
0
            UTF8ToNewUTF16(p, q - p, convertedText, convertedTextLen);
614
0
            break;
615
0
        }
616
0
        // skip to the end of the line
617
0
        while (p < endPtr && *p != '\0' && *p != '\n')
618
0
            p++;
619
0
        p++; // skip the actual newline as well.
620
0
    }
621
0
622
0
    // didn't find the desired item, so just pass the whole lot
623
0
    if (!*convertedText) {
624
0
        UTF8ToNewUTF16(data, datalen, convertedText, convertedTextLen);
625
0
    }
626
0
}
627
628
NS_IMETHODIMP
629
nsDragService::GetNumDropItems(uint32_t * aNumItems)
630
0
{
631
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::GetNumDropItems"));
632
0
633
0
    if (!mTargetWidget) {
634
0
        MOZ_LOG(sDragLm, LogLevel::Debug,
635
0
               ("*** warning: GetNumDropItems \
636
0
               called without a valid target widget!\n"));
637
0
        *aNumItems = 0;
638
0
        return NS_OK;
639
0
    }
640
0
641
#ifdef MOZ_WAYLAND
642
    // TODO: Wayland implementation of text/uri-list.
643
    if (!mTargetDragContext) {
644
        *aNumItems = 1;
645
        return NS_OK;
646
    }
647
#endif
648
649
0
    bool isList = IsTargetContextList();
650
0
    if (isList)
651
0
        mSourceDataItems->GetLength(aNumItems);
652
0
    else {
653
0
        GdkAtom gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
654
0
        GetTargetDragData(gdkFlavor);
655
0
        if (mTargetDragData) {
656
0
            const char *data = reinterpret_cast<char*>(mTargetDragData);
657
0
            *aNumItems = CountTextUriListItems(data, mTargetDragDataLen);
658
0
        } else
659
0
            *aNumItems = 1;
660
0
    }
661
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("%d items", *aNumItems));
662
0
    return NS_OK;
663
0
}
664
665
666
NS_IMETHODIMP
667
nsDragService::GetData(nsITransferable * aTransferable,
668
                       uint32_t aItemIndex)
669
0
{
670
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::GetData %d", aItemIndex));
671
0
672
0
    // make sure that we have a transferable
673
0
    if (!aTransferable)
674
0
        return NS_ERROR_INVALID_ARG;
675
0
676
0
    if (!mTargetWidget) {
677
0
        MOZ_LOG(sDragLm, LogLevel::Debug,
678
0
               ("*** warning: GetData \
679
0
               called without a valid target widget!\n"));
680
0
        return NS_ERROR_FAILURE;
681
0
    }
682
0
683
0
    // get flavor list that includes all acceptable flavors (including
684
0
    // ones obtained through conversion). Flavors are nsISupportsStrings
685
0
    // so that they can be seen from JS.
686
0
    nsCOMPtr<nsIArray> flavorList;
687
0
    nsresult rv = aTransferable->FlavorsTransferableCanImport(
688
0
                        getter_AddRefs(flavorList));
689
0
    if (NS_FAILED(rv))
690
0
        return rv;
691
0
692
0
    // count the number of flavors
693
0
    uint32_t cnt;
694
0
    flavorList->GetLength(&cnt);
695
0
    unsigned int i;
696
0
697
0
    // check to see if this is an internal list
698
0
    bool isList = IsTargetContextList();
699
0
700
0
    if (isList) {
701
0
        MOZ_LOG(sDragLm, LogLevel::Debug, ("it's a list..."));
702
0
        // find a matching flavor
703
0
        for (i = 0; i < cnt; ++i) {
704
0
            nsCOMPtr<nsISupportsCString> currentFlavor;
705
0
            currentFlavor = do_QueryElementAt(flavorList, i);
706
0
            if (!currentFlavor)
707
0
                continue;
708
0
709
0
            nsCString flavorStr;
710
0
            currentFlavor->ToString(getter_Copies(flavorStr));
711
0
            MOZ_LOG(sDragLm,
712
0
                   LogLevel::Debug,
713
0
                   ("flavor is %s\n", flavorStr.get()));
714
0
            // get the item with the right index
715
0
            nsCOMPtr<nsITransferable> item =
716
0
                do_QueryElementAt(mSourceDataItems, aItemIndex);
717
0
            if (!item)
718
0
                continue;
719
0
720
0
            nsCOMPtr<nsISupports> data;
721
0
            uint32_t tmpDataLen = 0;
722
0
            MOZ_LOG(sDragLm, LogLevel::Debug,
723
0
                   ("trying to get transfer data for %s\n",
724
0
                   flavorStr.get()));
725
0
            rv = item->GetTransferData(flavorStr.get(),
726
0
                                       getter_AddRefs(data),
727
0
                                       &tmpDataLen);
728
0
            if (NS_FAILED(rv)) {
729
0
                MOZ_LOG(sDragLm, LogLevel::Debug, ("failed.\n"));
730
0
                continue;
731
0
            }
732
0
            MOZ_LOG(sDragLm, LogLevel::Debug, ("succeeded.\n"));
733
0
            rv = aTransferable->SetTransferData(flavorStr.get(), data,
734
0
                                                tmpDataLen);
735
0
            if (NS_FAILED(rv)) {
736
0
                MOZ_LOG(sDragLm,
737
0
                       LogLevel::Debug,
738
0
                       ("fail to set transfer data into transferable!\n"));
739
0
                continue;
740
0
            }
741
0
            // ok, we got the data
742
0
            return NS_OK;
743
0
        }
744
0
        // if we got this far, we failed
745
0
        return NS_ERROR_FAILURE;
746
0
    }
747
0
748
0
    // Now walk down the list of flavors. When we find one that is
749
0
    // actually present, copy out the data into the transferable in that
750
0
    // format. SetTransferData() implicitly handles conversions.
751
0
    for ( i = 0; i < cnt; ++i ) {
752
0
        nsCOMPtr<nsISupportsCString> currentFlavor;
753
0
        currentFlavor = do_QueryElementAt(flavorList, i);
754
0
        if (currentFlavor) {
755
0
            // find our gtk flavor
756
0
            nsCString flavorStr;
757
0
            currentFlavor->ToString(getter_Copies(flavorStr));
758
0
            GdkAtom gdkFlavor = gdk_atom_intern(flavorStr.get(), FALSE);
759
0
            MOZ_LOG(sDragLm, LogLevel::Debug,
760
0
                   ("looking for data in type %s, gdk flavor %p\n",
761
0
                   flavorStr.get(), gdkFlavor));
762
0
            bool dataFound = false;
763
0
            if (gdkFlavor) {
764
0
                GetTargetDragData(gdkFlavor);
765
0
            }
766
0
            if (mTargetDragData) {
767
0
                MOZ_LOG(sDragLm, LogLevel::Debug, ("dataFound = true\n"));
768
0
                dataFound = true;
769
0
            }
770
0
            else {
771
0
                MOZ_LOG(sDragLm, LogLevel::Debug, ("dataFound = false\n"));
772
0
773
0
                // Dragging and dropping from the file manager would cause us
774
0
                // to parse the source text as a nsIFile URL.
775
0
                if (flavorStr.EqualsLiteral(kFileMime)) {
776
0
                    gdkFlavor = gdk_atom_intern(kTextMime, FALSE);
777
0
                    GetTargetDragData(gdkFlavor);
778
0
                    if (!mTargetDragData) {
779
0
                        gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
780
0
                        GetTargetDragData(gdkFlavor);
781
0
                    }
782
0
                    if (mTargetDragData) {
783
0
                        const char* text = static_cast<char*>(mTargetDragData);
784
0
                        char16_t* convertedText = nullptr;
785
0
                        uint32_t convertedTextLen = 0;
786
0
787
0
                        GetTextUriListItem(text, mTargetDragDataLen, aItemIndex,
788
0
                                           &convertedText, &convertedTextLen);
789
0
790
0
                        if (convertedText) {
791
0
                            nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
792
0
                            nsCOMPtr<nsIURI> fileURI;
793
0
                            rv = ioService->NewURI(NS_ConvertUTF16toUTF8(convertedText),
794
0
                                                   nullptr, nullptr, getter_AddRefs(fileURI));
795
0
                            if (NS_SUCCEEDED(rv)) {
796
0
                                nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI, &rv);
797
0
                                if (NS_SUCCEEDED(rv)) {
798
0
                                    nsCOMPtr<nsIFile> file;
799
0
                                    rv = fileURL->GetFile(getter_AddRefs(file));
800
0
                                    if (NS_SUCCEEDED(rv)) {
801
0
                                        // The common wrapping code at the end of
802
0
                                        // this function assumes the data is text
803
0
                                        // and calls text-specific operations.
804
0
                                        // Make a secret hideout here for nsIFile
805
0
                                        // objects and return early.
806
0
                                        aTransferable->SetTransferData(flavorStr.get(), file,
807
0
                                                                       convertedTextLen);
808
0
                                        g_free(convertedText);
809
0
                                        return NS_OK;
810
0
                                    }
811
0
                                }
812
0
                            }
813
0
                            g_free(convertedText);
814
0
                        }
815
0
                        continue;
816
0
                    }
817
0
                }
818
0
819
0
                // if we are looking for text/unicode and we fail to find it
820
0
                // on the clipboard first, try again with text/plain. If that
821
0
                // is present, convert it to unicode.
822
0
                if (flavorStr.EqualsLiteral(kUnicodeMime)) {
823
0
                    MOZ_LOG(sDragLm, LogLevel::Debug,
824
0
                           ("we were looking for text/unicode... \
825
0
                           trying with text/plain;charset=utf-8\n"));
826
0
                    gdkFlavor = gdk_atom_intern(gTextPlainUTF8Type, FALSE);
827
0
                    GetTargetDragData(gdkFlavor);
828
0
                    if (mTargetDragData) {
829
0
                        MOZ_LOG(sDragLm, LogLevel::Debug, ("Got textplain data\n"));
830
0
                        const char* castedText =
831
0
                                    reinterpret_cast<char*>(mTargetDragData);
832
0
                        char16_t* convertedText = nullptr;
833
0
                        NS_ConvertUTF8toUTF16 ucs2string(castedText,
834
0
                                                         mTargetDragDataLen);
835
0
                        convertedText = ToNewUnicode(ucs2string);
836
0
                        if ( convertedText ) {
837
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
838
0
                                   ("successfully converted plain text \
839
0
                                   to unicode.\n"));
840
0
                            // out with the old, in with the new
841
0
                            g_free(mTargetDragData);
842
0
                            mTargetDragData = convertedText;
843
0
                            mTargetDragDataLen = ucs2string.Length() * 2;
844
0
                            dataFound = true;
845
0
                        } // if plain text data on clipboard
846
0
                    } else {
847
0
                        MOZ_LOG(sDragLm, LogLevel::Debug,
848
0
                               ("we were looking for text/unicode... \
849
0
                               trying again with text/plain\n"));
850
0
                        gdkFlavor = gdk_atom_intern(kTextMime, FALSE);
851
0
                        GetTargetDragData(gdkFlavor);
852
0
                        if (mTargetDragData) {
853
0
                            MOZ_LOG(sDragLm, LogLevel::Debug, ("Got textplain data\n"));
854
0
                            const char* castedText =
855
0
                                        reinterpret_cast<char*>(mTargetDragData);
856
0
                            char16_t* convertedText = nullptr;
857
0
                            uint32_t convertedTextLen = 0;
858
0
                            UTF8ToNewUTF16(castedText, mTargetDragDataLen,
859
0
                                           &convertedText, &convertedTextLen);
860
0
                            if ( convertedText ) {
861
0
                                MOZ_LOG(sDragLm, LogLevel::Debug,
862
0
                                       ("successfully converted plain text \
863
0
                                       to unicode.\n"));
864
0
                                // out with the old, in with the new
865
0
                                g_free(mTargetDragData);
866
0
                                mTargetDragData = convertedText;
867
0
                                mTargetDragDataLen = convertedTextLen * 2;
868
0
                                dataFound = true;
869
0
                            } // if plain text data on clipboard
870
0
                        } // if plain text flavor present
871
0
                    } // if plain text charset=utf-8 flavor present
872
0
                } // if looking for text/unicode
873
0
874
0
                // if we are looking for text/x-moz-url and we failed to find
875
0
                // it on the clipboard, try again with text/uri-list, and then
876
0
                // _NETSCAPE_URL
877
0
                if (flavorStr.EqualsLiteral(kURLMime)) {
878
0
                    MOZ_LOG(sDragLm, LogLevel::Debug,
879
0
                           ("we were looking for text/x-moz-url...\
880
0
                           trying again with text/uri-list\n"));
881
0
                    gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
882
0
                    GetTargetDragData(gdkFlavor);
883
0
                    if (mTargetDragData) {
884
0
                        MOZ_LOG(sDragLm, LogLevel::Debug,
885
0
                               ("Got text/uri-list data\n"));
886
0
                        const char *data =
887
0
                                   reinterpret_cast<char*>(mTargetDragData);
888
0
                        char16_t* convertedText = nullptr;
889
0
                        uint32_t convertedTextLen = 0;
890
0
891
0
                        GetTextUriListItem(data, mTargetDragDataLen, aItemIndex,
892
0
                                           &convertedText, &convertedTextLen);
893
0
894
0
                        if ( convertedText ) {
895
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
896
0
                                   ("successfully converted \
897
0
                                   _NETSCAPE_URL to unicode.\n"));
898
0
                            // out with the old, in with the new
899
0
                            g_free(mTargetDragData);
900
0
                            mTargetDragData = convertedText;
901
0
                            mTargetDragDataLen = convertedTextLen * 2;
902
0
                            dataFound = true;
903
0
                        }
904
0
                    }
905
0
                    else {
906
0
                        MOZ_LOG(sDragLm, LogLevel::Debug,
907
0
                               ("failed to get text/uri-list data\n"));
908
0
                    }
909
0
                    if (!dataFound) {
910
0
                        MOZ_LOG(sDragLm, LogLevel::Debug,
911
0
                               ("we were looking for text/x-moz-url...\
912
0
                               trying again with _NETSCAP_URL\n"));
913
0
                        gdkFlavor = gdk_atom_intern(gMozUrlType, FALSE);
914
0
                        GetTargetDragData(gdkFlavor);
915
0
                        if (mTargetDragData) {
916
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
917
0
                                   ("Got _NETSCAPE_URL data\n"));
918
0
                            const char* castedText =
919
0
                                  reinterpret_cast<char*>(mTargetDragData);
920
0
                            char16_t* convertedText = nullptr;
921
0
                            uint32_t convertedTextLen = 0;
922
0
                            UTF8ToNewUTF16(castedText, mTargetDragDataLen, &convertedText, &convertedTextLen);
923
0
                            if ( convertedText ) {
924
0
                                MOZ_LOG(sDragLm,
925
0
                                       LogLevel::Debug,
926
0
                                       ("successfully converted _NETSCAPE_URL \
927
0
                                       to unicode.\n"));
928
0
                                // out with the old, in with the new
929
0
                                g_free(mTargetDragData);
930
0
                                mTargetDragData = convertedText;
931
0
                                mTargetDragDataLen = convertedTextLen * 2;
932
0
                                dataFound = true;
933
0
                            }
934
0
                        }
935
0
                        else {
936
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
937
0
                                   ("failed to get _NETSCAPE_URL data\n"));
938
0
                        }
939
0
                    }
940
0
                }
941
0
942
0
            } // else we try one last ditch effort to find our data
943
0
944
0
            if (dataFound) {
945
0
                if (!flavorStr.EqualsLiteral(kCustomTypesMime)) {
946
0
                  // the DOM only wants LF, so convert from MacOS line endings
947
0
                  // to DOM line endings.
948
0
                  nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(
949
0
                               flavorStr,
950
0
                               &mTargetDragData,
951
0
                               reinterpret_cast<int*>(&mTargetDragDataLen));
952
0
                }
953
0
954
0
                // put it into the transferable.
955
0
                nsCOMPtr<nsISupports> genericDataWrapper;
956
0
                nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr,
957
0
                                    mTargetDragData, mTargetDragDataLen,
958
0
                                    getter_AddRefs(genericDataWrapper));
959
0
                aTransferable->SetTransferData(flavorStr.get(),
960
0
                                               genericDataWrapper,
961
0
                                               mTargetDragDataLen);
962
0
                // we found one, get out of this loop!
963
0
                MOZ_LOG(sDragLm, LogLevel::Debug, ("dataFound and converted!\n"));
964
0
                break;
965
0
            }
966
0
        } // if (currentFlavor)
967
0
    } // foreach flavor
968
0
969
0
    return NS_OK;
970
0
971
0
}
972
973
NS_IMETHODIMP
974
nsDragService::IsDataFlavorSupported(const char *aDataFlavor,
975
                                     bool *_retval)
976
0
{
977
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::IsDataFlavorSupported %s",
978
0
                                   aDataFlavor));
979
0
    if (!_retval)
980
0
        return NS_ERROR_INVALID_ARG;
981
0
982
0
    // set this to no by default
983
0
    *_retval = false;
984
0
985
0
    // check to make sure that we have a drag object set, here
986
0
    if (!mTargetWidget) {
987
0
        MOZ_LOG(sDragLm, LogLevel::Debug,
988
0
               ("*** warning: IsDataFlavorSupported \
989
0
               called without a valid target widget!\n"));
990
0
        return NS_OK;
991
0
    }
992
0
993
0
    // check to see if the target context is a list.
994
0
    bool isList = IsTargetContextList();
995
0
    // if it is, just look in the internal data since we are the source
996
0
    // for it.
997
0
    if (isList) {
998
0
        MOZ_LOG(sDragLm, LogLevel::Debug, ("It's a list.."));
999
0
        uint32_t numDragItems = 0;
1000
0
        // if we don't have mDataItems we didn't start this drag so it's
1001
0
        // an external client trying to fool us.
1002
0
        if (!mSourceDataItems)
1003
0
            return NS_OK;
1004
0
        mSourceDataItems->GetLength(&numDragItems);
1005
0
        for (uint32_t itemIndex = 0; itemIndex < numDragItems; ++itemIndex) {
1006
0
            nsCOMPtr<nsITransferable> currItem =
1007
0
                do_QueryElementAt(mSourceDataItems, itemIndex);
1008
0
            if (currItem) {
1009
0
                nsCOMPtr <nsIArray> flavorList;
1010
0
                currItem->FlavorsTransferableCanExport(
1011
0
                          getter_AddRefs(flavorList));
1012
0
                if (flavorList) {
1013
0
                    uint32_t numFlavors;
1014
0
                    flavorList->GetLength( &numFlavors );
1015
0
                    for ( uint32_t flavorIndex = 0;
1016
0
                          flavorIndex < numFlavors ;
1017
0
                          ++flavorIndex ) {
1018
0
                        nsCOMPtr<nsISupportsCString> currentFlavor;
1019
0
                        currentFlavor = do_QueryElementAt(flavorList, flavorIndex);
1020
0
                        if (currentFlavor) {
1021
0
                            nsCString flavorStr;
1022
0
                            currentFlavor->ToString(getter_Copies(flavorStr));
1023
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
1024
0
                                   ("checking %s against %s\n",
1025
0
                                   flavorStr.get(), aDataFlavor));
1026
0
                            if (flavorStr.Equals(aDataFlavor)) {
1027
0
                                MOZ_LOG(sDragLm, LogLevel::Debug,
1028
0
                                       ("boioioioiooioioioing!\n"));
1029
0
                                *_retval = true;
1030
0
                            }
1031
0
                        }
1032
0
                    }
1033
0
                }
1034
0
            }
1035
0
        }
1036
0
        return NS_OK;
1037
0
    }
1038
0
1039
0
    // check the target context vs. this flavor, one at a time
1040
0
    GList *tmp = nullptr;
1041
0
    if (mTargetDragContext) {
1042
0
        tmp = gdk_drag_context_list_targets(mTargetDragContext);
1043
0
    }
1044
#ifdef MOZ_WAYLAND
1045
    else if (mTargetWaylandDragContext) {
1046
        tmp = mTargetWaylandDragContext->GetTargets();
1047
    }
1048
    GList *tmp_head = tmp;
1049
#endif
1050
1051
0
    for (; tmp; tmp = tmp->next) {
1052
0
        /* Bug 331198 */
1053
0
        GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data);
1054
0
        gchar *name = nullptr;
1055
0
        name = gdk_atom_name(atom);
1056
0
        MOZ_LOG(sDragLm, LogLevel::Debug,
1057
0
               ("checking %s against %s\n", name, aDataFlavor));
1058
0
        if (name && (strcmp(name, aDataFlavor) == 0)) {
1059
0
            MOZ_LOG(sDragLm, LogLevel::Debug, ("good!\n"));
1060
0
            *_retval = true;
1061
0
        }
1062
0
        // check for automatic text/uri-list -> text/x-moz-url mapping
1063
0
        if (!*_retval &&
1064
0
            name &&
1065
0
            (strcmp(name, gTextUriListType) == 0) &&
1066
0
            (strcmp(aDataFlavor, kURLMime) == 0 ||
1067
0
             strcmp(aDataFlavor, kFileMime) == 0)) {
1068
0
            MOZ_LOG(sDragLm, LogLevel::Debug,
1069
0
                   ("good! ( it's text/uri-list and \
1070
0
                   we're checking against text/x-moz-url )\n"));
1071
0
            *_retval = true;
1072
0
        }
1073
0
        // check for automatic _NETSCAPE_URL -> text/x-moz-url mapping
1074
0
        if (!*_retval &&
1075
0
            name &&
1076
0
            (strcmp(name, gMozUrlType) == 0) &&
1077
0
            (strcmp(aDataFlavor, kURLMime) == 0)) {
1078
0
            MOZ_LOG(sDragLm, LogLevel::Debug,
1079
0
                   ("good! ( it's _NETSCAPE_URL and \
1080
0
                   we're checking against text/x-moz-url )\n"));
1081
0
            *_retval = true;
1082
0
        }
1083
0
        // check for auto text/plain -> text/unicode mapping
1084
0
        if (!*_retval &&
1085
0
            name &&
1086
0
            (strcmp(name, kTextMime) == 0) &&
1087
0
            ((strcmp(aDataFlavor, kUnicodeMime) == 0) ||
1088
0
             (strcmp(aDataFlavor, kFileMime) == 0))) {
1089
0
            MOZ_LOG(sDragLm, LogLevel::Debug,
1090
0
                   ("good! ( it's text plain and we're checking \
1091
0
                   against text/unicode or application/x-moz-file)\n"));
1092
0
            *_retval = true;
1093
0
        }
1094
0
        g_free(name);
1095
0
    }
1096
0
1097
#ifdef MOZ_WAYLAND
1098
    // mTargetWaylandDragContext->GetTargets allocates the list
1099
    // so we need to free it here.
1100
    if (!mTargetDragContext && tmp_head) {
1101
        g_list_free(tmp_head);
1102
    }
1103
#endif
1104
1105
0
    return NS_OK;
1106
0
}
1107
1108
void
1109
nsDragService::ReplyToDragMotion(GdkDragContext* aDragContext)
1110
0
{
1111
0
    MOZ_LOG(sDragLm, LogLevel::Debug,
1112
0
           ("nsDragService::ReplyToDragMotion %d", mCanDrop));
1113
0
1114
0
    GdkDragAction action = (GdkDragAction)0;
1115
0
    if (mCanDrop) {
1116
0
        // notify the dragger if we can drop
1117
0
        switch (mDragAction) {
1118
0
        case DRAGDROP_ACTION_COPY:
1119
0
          action = GDK_ACTION_COPY;
1120
0
          break;
1121
0
        case DRAGDROP_ACTION_LINK:
1122
0
          action = GDK_ACTION_LINK;
1123
0
          break;
1124
0
        case DRAGDROP_ACTION_NONE:
1125
0
          action = (GdkDragAction)0;
1126
0
          break;
1127
0
        default:
1128
0
          action = GDK_ACTION_MOVE;
1129
0
          break;
1130
0
        }
1131
0
    }
1132
0
1133
0
    gdk_drag_status(aDragContext, action, mTargetTime);
1134
0
}
1135
1136
#ifdef MOZ_WAYLAND
1137
void
1138
nsDragService::ReplyToDragMotion(nsWaylandDragContext* aDragContext)
1139
{
1140
    MOZ_LOG(sDragLm, LogLevel::Debug,
1141
           ("nsDragService::ReplyToDragMotion %d", mCanDrop));
1142
1143
    GdkDragAction action = (GdkDragAction)0;
1144
    if (mCanDrop) {
1145
        // notify the dragger if we can drop
1146
        switch (mDragAction) {
1147
        case DRAGDROP_ACTION_COPY:
1148
          action = GDK_ACTION_COPY;
1149
          break;
1150
        case DRAGDROP_ACTION_LINK:
1151
          action = GDK_ACTION_LINK;
1152
          break;
1153
        case DRAGDROP_ACTION_NONE:
1154
          action = (GdkDragAction)0;
1155
          break;
1156
        default:
1157
          action = GDK_ACTION_MOVE;
1158
          break;
1159
        }
1160
    }
1161
1162
    aDragContext->SetDragStatus(action);
1163
}
1164
#endif
1165
1166
void
1167
nsDragService::TargetDataReceived(GtkWidget         *aWidget,
1168
                                  GdkDragContext    *aContext,
1169
                                  gint               aX,
1170
                                  gint               aY,
1171
                                  GtkSelectionData  *aSelectionData,
1172
                                  guint              aInfo,
1173
                                  guint32            aTime)
1174
0
{
1175
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::TargetDataReceived"));
1176
0
    TargetResetData();
1177
0
    mTargetDragDataReceived = true;
1178
0
    gint len = gtk_selection_data_get_length(aSelectionData);
1179
0
    const guchar* data = gtk_selection_data_get_data(aSelectionData);
1180
0
    if (len > 0 && data) {
1181
0
        mTargetDragDataLen = len;
1182
0
        mTargetDragData = g_malloc(mTargetDragDataLen);
1183
0
        memcpy(mTargetDragData, data, mTargetDragDataLen);
1184
0
    }
1185
0
    else {
1186
0
        MOZ_LOG(sDragLm, LogLevel::Debug,
1187
0
               ("Failed to get data.  selection data len was %d\n",
1188
0
                mTargetDragDataLen));
1189
0
    }
1190
0
}
1191
1192
bool
1193
nsDragService::IsTargetContextList(void)
1194
0
{
1195
0
    bool retval = false;
1196
0
1197
#ifdef MOZ_WAYLAND
1198
    // TODO: We need a wayland implementation here.
1199
    if (!mTargetDragContext)
1200
        return retval;
1201
#endif
1202
1203
0
    // gMimeListType drags only work for drags within a single process. The
1204
0
    // gtk_drag_get_source_widget() function will return nullptr if the source
1205
0
    // of the drag is another app, so we use it to check if a gMimeListType
1206
0
    // drop will work or not.
1207
0
    if (gtk_drag_get_source_widget(mTargetDragContext) == nullptr)
1208
0
        return retval;
1209
0
1210
0
    GList *tmp;
1211
0
1212
0
    // walk the list of context targets and see if one of them is a list
1213
0
    // of items.
1214
0
    for (tmp = gdk_drag_context_list_targets(mTargetDragContext);
1215
0
         tmp; tmp = tmp->next) {
1216
0
        /* Bug 331198 */
1217
0
        GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data);
1218
0
        gchar *name = nullptr;
1219
0
        name = gdk_atom_name(atom);
1220
0
        if (name && strcmp(name, gMimeListType) == 0)
1221
0
            retval = true;
1222
0
        g_free(name);
1223
0
        if (retval)
1224
0
            break;
1225
0
    }
1226
0
    return retval;
1227
0
}
1228
1229
// Maximum time to wait for a "drag_received" arrived, in microseconds
1230
0
#define NS_DND_TIMEOUT 500000
1231
1232
void
1233
nsDragService::GetTargetDragData(GdkAtom aFlavor)
1234
0
{
1235
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("getting data flavor %p\n", aFlavor));
1236
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("mLastWidget is %p and mLastContext is %p\n",
1237
0
                                   mTargetWidget.get(),
1238
0
                                   mTargetDragContext.get()));
1239
0
    // reset our target data areas
1240
0
    TargetResetData();
1241
0
1242
0
    if (mTargetDragContext) {
1243
0
        gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
1244
0
1245
0
        MOZ_LOG(sDragLm, LogLevel::Debug, ("about to start inner iteration."));
1246
0
        PRTime entryTime = PR_Now();
1247
0
        while (!mTargetDragDataReceived && mDoingDrag) {
1248
0
            // check the number of iterations
1249
0
            MOZ_LOG(sDragLm, LogLevel::Debug, ("doing iteration...\n"));
1250
0
            PR_Sleep(20*PR_TicksPerSecond()/1000);  /* sleep for 20 ms/iteration */
1251
0
            if (PR_Now()-entryTime > NS_DND_TIMEOUT) break;
1252
0
            gtk_main_iteration();
1253
0
        }
1254
0
    }
1255
#ifdef MOZ_WAYLAND
1256
    else {
1257
        mTargetDragData =
1258
            mTargetWaylandDragContext->GetData(gdk_atom_name(aFlavor),
1259
                                               &mTargetDragDataLen);
1260
        mTargetDragDataReceived = true;
1261
    }
1262
#endif
1263
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("finished inner iteration\n"));
1264
0
}
1265
1266
void
1267
nsDragService::TargetResetData(void)
1268
0
{
1269
0
    mTargetDragDataReceived = false;
1270
0
    // make sure to free old data if we have to
1271
0
    g_free(mTargetDragData);
1272
0
    mTargetDragData = 0;
1273
0
    mTargetDragDataLen = 0;
1274
0
}
1275
1276
GtkTargetList *
1277
nsDragService::GetSourceList(void)
1278
0
{
1279
0
    if (!mSourceDataItems)
1280
0
        return nullptr;
1281
0
    nsTArray<GtkTargetEntry*> targetArray;
1282
0
    GtkTargetEntry *targets;
1283
0
    GtkTargetList  *targetList = 0;
1284
0
    uint32_t targetCount = 0;
1285
0
    unsigned int numDragItems = 0;
1286
0
1287
0
    mSourceDataItems->GetLength(&numDragItems);
1288
0
1289
0
    // Check to see if we're dragging > 1 item.
1290
0
    if (numDragItems > 1) {
1291
0
        // as the Xdnd protocol only supports a single item (or is it just
1292
0
        // gtk's implementation?), we don't advertise all flavours listed
1293
0
        // in the nsITransferable.
1294
0
1295
0
        // the application/x-moz-internal-item-list format, which preserves
1296
0
        // all information for drags within the same mozilla instance.
1297
0
        GtkTargetEntry *listTarget =
1298
0
            (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
1299
0
        listTarget->target = g_strdup(gMimeListType);
1300
0
        listTarget->flags = 0;
1301
0
        MOZ_LOG(sDragLm, LogLevel::Debug,
1302
0
               ("automatically adding target %s\n", listTarget->target));
1303
0
        targetArray.AppendElement(listTarget);
1304
0
1305
0
        // check what flavours are supported so we can decide what other
1306
0
        // targets to advertise.
1307
0
        nsCOMPtr<nsITransferable> currItem =
1308
0
            do_QueryElementAt(mSourceDataItems, 0);
1309
0
1310
0
        if (currItem) {
1311
0
            nsCOMPtr <nsIArray> flavorList;
1312
0
            currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
1313
0
            if (flavorList) {
1314
0
                uint32_t numFlavors;
1315
0
                flavorList->GetLength( &numFlavors );
1316
0
                for (uint32_t flavorIndex = 0;
1317
0
                     flavorIndex < numFlavors ;
1318
0
                     ++flavorIndex ) {
1319
0
                    nsCOMPtr<nsISupportsCString> currentFlavor;
1320
0
                    currentFlavor = do_QueryElementAt(flavorList, flavorIndex);
1321
0
                    if (currentFlavor) {
1322
0
                        nsCString flavorStr;
1323
0
                        currentFlavor->ToString(getter_Copies(flavorStr));
1324
0
1325
0
                        // check if text/x-moz-url is supported.
1326
0
                        // If so, advertise
1327
0
                        // text/uri-list.
1328
0
                        if (flavorStr.EqualsLiteral(kURLMime)) {
1329
0
                            listTarget =
1330
0
                             (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
1331
0
                            listTarget->target = g_strdup(gTextUriListType);
1332
0
                            listTarget->flags = 0;
1333
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
1334
0
                                   ("automatically adding target %s\n",
1335
0
                                    listTarget->target));
1336
0
                            targetArray.AppendElement(listTarget);
1337
0
                        }
1338
0
                    }
1339
0
                } // foreach flavor in item
1340
0
            } // if valid flavor list
1341
0
        } // if item is a transferable
1342
0
    } else if (numDragItems == 1) {
1343
0
        nsCOMPtr<nsITransferable> currItem =
1344
0
            do_QueryElementAt(mSourceDataItems, 0);
1345
0
        if (currItem) {
1346
0
            nsCOMPtr <nsIArray> flavorList;
1347
0
            currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
1348
0
            if (flavorList) {
1349
0
                uint32_t numFlavors;
1350
0
                flavorList->GetLength( &numFlavors );
1351
0
                for (uint32_t flavorIndex = 0;
1352
0
                     flavorIndex < numFlavors ;
1353
0
                     ++flavorIndex ) {
1354
0
                    nsCOMPtr<nsISupportsCString> currentFlavor;
1355
0
                    currentFlavor = do_QueryElementAt(flavorList, flavorIndex);
1356
0
                    if (currentFlavor) {
1357
0
                        nsCString flavorStr;
1358
0
                        currentFlavor->ToString(getter_Copies(flavorStr));
1359
0
                        GtkTargetEntry *target =
1360
0
                          (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
1361
0
                        target->target = g_strdup(flavorStr.get());
1362
0
                        target->flags = 0;
1363
0
                        MOZ_LOG(sDragLm, LogLevel::Debug,
1364
0
                               ("adding target %s\n", target->target));
1365
0
                        targetArray.AppendElement(target);
1366
0
1367
0
                        // If there is a file, add the text/uri-list type.
1368
0
                        if (flavorStr.EqualsLiteral(kFileMime)) {
1369
0
                            GtkTargetEntry *urilistTarget =
1370
0
                             (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
1371
0
                            urilistTarget->target = g_strdup(gTextUriListType);
1372
0
                            urilistTarget->flags = 0;
1373
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
1374
0
                                   ("automatically adding target %s\n",
1375
0
                                    urilistTarget->target));
1376
0
                            targetArray.AppendElement(urilistTarget);
1377
0
                        }
1378
0
                        // Check to see if this is text/unicode.
1379
0
                        // If it is, add text/plain
1380
0
                        // since we automatically support text/plain
1381
0
                        // if we support text/unicode.
1382
0
                        else if (flavorStr.EqualsLiteral(kUnicodeMime)) {
1383
0
                            GtkTargetEntry *plainUTF8Target =
1384
0
                             (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
1385
0
                            plainUTF8Target->target = g_strdup(gTextPlainUTF8Type);
1386
0
                            plainUTF8Target->flags = 0;
1387
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
1388
0
                                   ("automatically adding target %s\n",
1389
0
                                    plainUTF8Target->target));
1390
0
                            targetArray.AppendElement(plainUTF8Target);
1391
0
1392
0
                            GtkTargetEntry *plainTarget =
1393
0
                             (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
1394
0
                            plainTarget->target = g_strdup(kTextMime);
1395
0
                            plainTarget->flags = 0;
1396
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
1397
0
                                   ("automatically adding target %s\n",
1398
0
                                    plainTarget->target));
1399
0
                            targetArray.AppendElement(plainTarget);
1400
0
                        }
1401
0
                        // Check to see if this is the x-moz-url type.
1402
0
                        // If it is, add _NETSCAPE_URL
1403
0
                        // this is a type used by everybody.
1404
0
                        else if (flavorStr.EqualsLiteral(kURLMime)) {
1405
0
                            GtkTargetEntry *urlTarget =
1406
0
                             (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
1407
0
                            urlTarget->target = g_strdup(gMozUrlType);
1408
0
                            urlTarget->flags = 0;
1409
0
                            MOZ_LOG(sDragLm, LogLevel::Debug,
1410
0
                                   ("automatically adding target %s\n",
1411
0
                                    urlTarget->target));
1412
0
                            targetArray.AppendElement(urlTarget);
1413
0
                        }
1414
0
                    }
1415
0
                } // foreach flavor in item
1416
0
            } // if valid flavor list
1417
0
        } // if item is a transferable
1418
0
    } // if it is a single item drag
1419
0
1420
0
    // get all the elements that we created.
1421
0
    targetCount = targetArray.Length();
1422
0
    if (targetCount) {
1423
0
        // allocate space to create the list of valid targets
1424
0
        targets =
1425
0
          (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry) * targetCount);
1426
0
        uint32_t targetIndex;
1427
0
        for ( targetIndex = 0; targetIndex < targetCount; ++targetIndex) {
1428
0
            GtkTargetEntry *disEntry = targetArray.ElementAt(targetIndex);
1429
0
            // this is a string reference but it will be freed later.
1430
0
            targets[targetIndex].target = disEntry->target;
1431
0
            targets[targetIndex].flags = disEntry->flags;
1432
0
            targets[targetIndex].info = 0;
1433
0
        }
1434
0
        targetList = gtk_target_list_new(targets, targetCount);
1435
0
        // clean up the target list
1436
0
        for (uint32_t cleanIndex = 0; cleanIndex < targetCount; ++cleanIndex) {
1437
0
            GtkTargetEntry *thisTarget = targetArray.ElementAt(cleanIndex);
1438
0
            g_free(thisTarget->target);
1439
0
            g_free(thisTarget);
1440
0
        }
1441
0
        g_free(targets);
1442
0
    }
1443
0
    return targetList;
1444
0
}
1445
1446
void
1447
nsDragService::SourceEndDragSession(GdkDragContext *aContext,
1448
                                    gint            aResult)
1449
0
{
1450
0
    // this just releases the list of data items that we provide
1451
0
    mSourceDataItems = nullptr;
1452
0
1453
0
    if (!mDoingDrag || mScheduledTask == eDragTaskSourceEnd)
1454
0
        // EndDragSession() was already called on drop
1455
0
        // or SourceEndDragSession on drag-failed
1456
0
        return;
1457
0
1458
0
    if (mEndDragPoint.x < 0) {
1459
0
        // We don't have a drag end point, so guess
1460
0
        gint x, y;
1461
0
        GdkDisplay* display = gdk_display_get_default();
1462
0
        if (display) {
1463
0
            gint scale = ScreenHelperGTK::GetGTKMonitorScaleFactor();
1464
0
            gdk_display_get_pointer(display, nullptr, &x, &y, nullptr);
1465
0
            SetDragEndPoint(LayoutDeviceIntPoint(x * scale, y * scale));
1466
0
        }
1467
0
    }
1468
0
1469
0
    // Either the drag was aborted or the drop occurred outside the app.
1470
0
    // The dropEffect of mDataTransfer is not updated for motion outside the
1471
0
    // app, but is needed for the dragend event, so set it now.
1472
0
1473
0
    uint32_t dropEffect;
1474
0
1475
0
    if (aResult == MOZ_GTK_DRAG_RESULT_SUCCESS) {
1476
0
1477
0
        // With GTK+ versions 2.10.x and prior the drag may have been
1478
0
        // cancelled (but no drag-failed signal would have been sent).
1479
0
        // aContext->dest_window will be non-nullptr only if the drop was
1480
0
        // sent.
1481
0
        GdkDragAction action =
1482
0
            gdk_drag_context_get_dest_window(aContext) ?
1483
0
                gdk_drag_context_get_actions(aContext) : (GdkDragAction)0;
1484
0
1485
0
        // Only one bit of action should be set, but, just in case someone
1486
0
        // does something funny, erring away from MOVE, and not recording
1487
0
        // unusual action combinations as NONE.
1488
0
        if (!action)
1489
0
            dropEffect = DRAGDROP_ACTION_NONE;
1490
0
        else if (action & GDK_ACTION_COPY)
1491
0
            dropEffect = DRAGDROP_ACTION_COPY;
1492
0
        else if (action & GDK_ACTION_LINK)
1493
0
            dropEffect = DRAGDROP_ACTION_LINK;
1494
0
        else if (action & GDK_ACTION_MOVE)
1495
0
            dropEffect = DRAGDROP_ACTION_MOVE;
1496
0
        else
1497
0
            dropEffect = DRAGDROP_ACTION_COPY;
1498
0
1499
0
    } else {
1500
0
1501
0
        dropEffect = DRAGDROP_ACTION_NONE;
1502
0
1503
0
        if (aResult != MOZ_GTK_DRAG_RESULT_NO_TARGET) {
1504
0
            mUserCancelled = true;
1505
0
        }
1506
0
    }
1507
0
1508
0
    if (mDataTransfer) {
1509
0
        mDataTransfer->SetDropEffectInt(dropEffect);
1510
0
    }
1511
0
1512
0
    // Schedule the appropriate drag end dom events.
1513
0
    Schedule(eDragTaskSourceEnd, nullptr, nullptr, nullptr, LayoutDeviceIntPoint(), 0);
1514
0
}
1515
1516
static void
1517
CreateUriList(nsIArray *items, gchar **text, gint *length)
1518
0
{
1519
0
    uint32_t i, count;
1520
0
    GString *uriList = g_string_new(nullptr);
1521
0
1522
0
    items->GetLength(&count);
1523
0
    for (i = 0; i < count; i++) {
1524
0
        nsCOMPtr<nsITransferable> item;
1525
0
        item = do_QueryElementAt(items, i);
1526
0
1527
0
        if (item) {
1528
0
            uint32_t tmpDataLen = 0;
1529
0
            void    *tmpData = nullptr;
1530
0
            nsresult rv = NS_OK;
1531
0
            nsCOMPtr<nsISupports> data;
1532
0
            rv = item->GetTransferData(kURLMime,
1533
0
                                       getter_AddRefs(data),
1534
0
                                       &tmpDataLen);
1535
0
1536
0
            if (NS_SUCCEEDED(rv)) {
1537
0
                nsPrimitiveHelpers::CreateDataFromPrimitive(
1538
0
                    nsDependentCString(kURLMime), data, &tmpData, tmpDataLen);
1539
0
                char* plainTextData = nullptr;
1540
0
                char16_t* castedUnicode = reinterpret_cast<char16_t*>
1541
0
                                                           (tmpData);
1542
0
                uint32_t plainTextLen = 0;
1543
0
                UTF16ToNewUTF8(castedUnicode,
1544
0
                               tmpDataLen / 2,
1545
0
                               &plainTextData,
1546
0
                               &plainTextLen);
1547
0
                if (plainTextData) {
1548
0
                    uint32_t j;
1549
0
1550
0
                    // text/x-moz-url is of form url + "\n" + title.
1551
0
                    // We just want the url.
1552
0
                    for (j = 0; j < plainTextLen; j++)
1553
0
                        if (plainTextData[j] == '\n' ||
1554
0
                            plainTextData[j] == '\r') {
1555
0
                            plainTextData[j] = '\0';
1556
0
                            break;
1557
0
                        }
1558
0
                    g_string_append(uriList, plainTextData);
1559
0
                    g_string_append(uriList, "\r\n");
1560
0
                    // this wasn't allocated with glib
1561
0
                    free(plainTextData);
1562
0
                }
1563
0
                if (tmpData) {
1564
0
                    // this wasn't allocated with glib
1565
0
                    free(tmpData);
1566
0
                }
1567
0
            } else {
1568
0
                // There is no uri available.  If there is a file available,
1569
0
                // create a uri from the file.
1570
0
                nsCOMPtr<nsISupports> data;
1571
0
                rv = item->GetTransferData(kFileMime,
1572
0
                                           getter_AddRefs(data),
1573
0
                                           &tmpDataLen);
1574
0
                if (NS_SUCCEEDED(rv)) {
1575
0
                    nsCOMPtr<nsIFile> file = do_QueryInterface(data);
1576
0
                    if (!file) {
1577
0
                        // Sometimes the file is wrapped in a
1578
0
                        // nsISupportsInterfacePointer. See bug 1310193 for
1579
0
                        // removing this distinction.
1580
0
                        nsCOMPtr<nsISupportsInterfacePointer> ptr =
1581
0
                          do_QueryInterface(data);
1582
0
                        if (ptr) {
1583
0
                            ptr->GetData(getter_AddRefs(data));
1584
0
                            file = do_QueryInterface(data);
1585
0
                        }
1586
0
                    }
1587
0
1588
0
                    if (file) {
1589
0
                        nsCOMPtr<nsIURI> fileURI;
1590
0
                        NS_NewFileURI(getter_AddRefs(fileURI), file);
1591
0
                        if (fileURI) {
1592
0
                            nsAutoCString uristring;
1593
0
                            fileURI->GetSpec(uristring);
1594
0
                            g_string_append(uriList, uristring.get());
1595
0
                            g_string_append(uriList, "\r\n");
1596
0
                        }
1597
0
                    }
1598
0
                }
1599
0
            }
1600
0
        }
1601
0
    }
1602
0
    *text = uriList->str;
1603
0
    *length = uriList->len + 1;
1604
0
    g_string_free(uriList, FALSE); // don't free the data
1605
0
}
1606
1607
1608
void
1609
nsDragService::SourceDataGet(GtkWidget        *aWidget,
1610
                             GdkDragContext   *aContext,
1611
                             GtkSelectionData *aSelectionData,
1612
                             guint32           aTime)
1613
0
{
1614
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("nsDragService::SourceDataGet"));
1615
0
    GdkAtom target = gtk_selection_data_get_target(aSelectionData);
1616
0
    nsCString mimeFlavor;
1617
0
    gchar *typeName = 0;
1618
0
    typeName = gdk_atom_name(target);
1619
0
    if (!typeName) {
1620
0
        MOZ_LOG(sDragLm, LogLevel::Debug, ("failed to get atom name.\n"));
1621
0
        return;
1622
0
    }
1623
0
1624
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("Type is %s\n", typeName));
1625
0
    // make a copy since |nsCString| won't use |g_free|...
1626
0
    mimeFlavor.Adopt(strdup(typeName));
1627
0
    g_free(typeName);
1628
0
    // check to make sure that we have data items to return.
1629
0
    if (!mSourceDataItems) {
1630
0
        MOZ_LOG(sDragLm, LogLevel::Debug, ("Failed to get our data items\n"));
1631
0
        return;
1632
0
    }
1633
0
1634
0
    nsCOMPtr<nsITransferable> item;
1635
0
    item = do_QueryElementAt(mSourceDataItems, 0);
1636
0
    if (item) {
1637
0
        // if someone was asking for text/plain, lookup unicode instead so
1638
0
        // we can convert it.
1639
0
        bool needToDoConversionToPlainText = false;
1640
0
        const char* actualFlavor;
1641
0
        if (mimeFlavor.EqualsLiteral(kTextMime) ||
1642
0
            mimeFlavor.EqualsLiteral(gTextPlainUTF8Type)) {
1643
0
            actualFlavor = kUnicodeMime;
1644
0
            needToDoConversionToPlainText = true;
1645
0
        }
1646
0
        // if someone was asking for _NETSCAPE_URL we need to convert to
1647
0
        // plain text but we also need to look for x-moz-url
1648
0
        else if (mimeFlavor.EqualsLiteral(gMozUrlType)) {
1649
0
            actualFlavor = kURLMime;
1650
0
            needToDoConversionToPlainText = true;
1651
0
        }
1652
0
        // if someone was asking for text/uri-list we need to convert to
1653
0
        // plain text.
1654
0
        else if (mimeFlavor.EqualsLiteral(gTextUriListType)) {
1655
0
            actualFlavor = gTextUriListType;
1656
0
            needToDoConversionToPlainText = true;
1657
0
        }
1658
0
        else
1659
0
            actualFlavor = mimeFlavor.get();
1660
0
1661
0
        uint32_t tmpDataLen = 0;
1662
0
        void    *tmpData = nullptr;
1663
0
        nsresult rv;
1664
0
        nsCOMPtr<nsISupports> data;
1665
0
        rv = item->GetTransferData(actualFlavor,
1666
0
                                   getter_AddRefs(data),
1667
0
                                   &tmpDataLen);
1668
0
        if (NS_SUCCEEDED(rv)) {
1669
0
            nsPrimitiveHelpers::CreateDataFromPrimitive(
1670
0
                nsDependentCString(actualFlavor), data, &tmpData, tmpDataLen);
1671
0
            // if required, do the extra work to convert unicode to plain
1672
0
            // text and replace the output values with the plain text.
1673
0
            if (needToDoConversionToPlainText) {
1674
0
                char* plainTextData = nullptr;
1675
0
                char16_t* castedUnicode = reinterpret_cast<char16_t*>
1676
0
                                                           (tmpData);
1677
0
                uint32_t plainTextLen = 0;
1678
0
                UTF16ToNewUTF8(castedUnicode,
1679
0
                               tmpDataLen / 2,
1680
0
                               &plainTextData,
1681
0
                               &plainTextLen);
1682
0
                if (tmpData) {
1683
0
                    // this was not allocated using glib
1684
0
                    free(tmpData);
1685
0
                    tmpData = plainTextData;
1686
0
                    tmpDataLen = plainTextLen;
1687
0
                }
1688
0
            }
1689
0
            if (tmpData) {
1690
0
                // this copies the data
1691
0
                gtk_selection_data_set(aSelectionData, target,
1692
0
                                       8,
1693
0
                                       (guchar *)tmpData, tmpDataLen);
1694
0
                // this wasn't allocated with glib
1695
0
                free(tmpData);
1696
0
            }
1697
0
        } else {
1698
0
            if (mimeFlavor.EqualsLiteral(gTextUriListType)) {
1699
0
                // fall back for text/uri-list
1700
0
                gchar *uriList;
1701
0
                gint length;
1702
0
                CreateUriList(mSourceDataItems, &uriList, &length);
1703
0
                gtk_selection_data_set(aSelectionData, target,
1704
0
                                       8, (guchar *)uriList, length);
1705
0
                g_free(uriList);
1706
0
                return;
1707
0
            }
1708
0
        }
1709
0
    }
1710
0
}
1711
1712
void nsDragService::SetDragIcon(GdkDragContext* aContext)
1713
0
{
1714
0
    if (!mHasImage && !mSelection)
1715
0
        return;
1716
0
1717
0
    LayoutDeviceIntRect dragRect;
1718
0
    nsPresContext* pc;
1719
0
    RefPtr<SourceSurface> surface;
1720
0
    DrawDrag(mSourceNode, mRegion,
1721
0
             mScreenPosition, &dragRect, &surface, &pc);
1722
0
    if (!pc)
1723
0
        return;
1724
0
1725
0
    LayoutDeviceIntPoint screenPoint =
1726
0
      ConvertToUnscaledDevPixels(pc, mScreenPosition);
1727
0
    int32_t offsetX = screenPoint.x - dragRect.x;
1728
0
    int32_t offsetY = screenPoint.y - dragRect.y;
1729
0
1730
0
    // If a popup is set as the drag image, use its widget. Otherwise, use
1731
0
    // the surface that DrawDrag created.
1732
0
    //
1733
0
    // XXX: Disable drag popups on GTK 3.19.4 and above: see bug 1264454.
1734
0
    //      Fix this once a new GTK version ships that does not destroy our
1735
0
    //      widget in gtk_drag_set_icon_widget.
1736
0
    if (mDragPopup && gtk_check_version(3, 19, 4)) {
1737
0
        GtkWidget* gtkWidget = nullptr;
1738
0
        nsIFrame* frame = mDragPopup->GetPrimaryFrame();
1739
0
        if (frame) {
1740
0
            // DrawDrag ensured that this is a popup frame.
1741
0
            nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget();
1742
0
            if (widget) {
1743
0
                gtkWidget = (GtkWidget *)widget->GetNativeData(NS_NATIVE_SHELLWIDGET);
1744
0
                if (gtkWidget) {
1745
0
                    OpenDragPopup();
1746
0
                    gtk_drag_set_icon_widget(aContext, gtkWidget, offsetX, offsetY);
1747
0
                }
1748
0
            }
1749
0
        }
1750
0
    }
1751
0
    else if (surface) {
1752
0
        if (!SetAlphaPixmap(surface, aContext, offsetX, offsetY, dragRect)) {
1753
0
            GdkPixbuf* dragPixbuf =
1754
0
              nsImageToPixbuf::SourceSurfaceToPixbuf(surface, dragRect.width, dragRect.height);
1755
0
            if (dragPixbuf) {
1756
0
                gtk_drag_set_icon_pixbuf(aContext, dragPixbuf, offsetX, offsetY);
1757
0
                g_object_unref(dragPixbuf);
1758
0
            }
1759
0
        }
1760
0
    }
1761
0
}
1762
1763
static void
1764
invisibleSourceDragBegin(GtkWidget        *aWidget,
1765
                         GdkDragContext   *aContext,
1766
                         gpointer          aData)
1767
0
{
1768
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("invisibleSourceDragBegin"));
1769
0
    nsDragService *dragService = (nsDragService *)aData;
1770
0
1771
0
    dragService->SetDragIcon(aContext);
1772
0
}
1773
1774
static void
1775
invisibleSourceDragDataGet(GtkWidget        *aWidget,
1776
                           GdkDragContext   *aContext,
1777
                           GtkSelectionData *aSelectionData,
1778
                           guint             aInfo,
1779
                           guint32           aTime,
1780
                           gpointer          aData)
1781
0
{
1782
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("invisibleSourceDragDataGet"));
1783
0
    nsDragService *dragService = (nsDragService *)aData;
1784
0
    dragService->SourceDataGet(aWidget, aContext,
1785
0
                               aSelectionData, aTime);
1786
0
}
1787
1788
static gboolean
1789
invisibleSourceDragFailed(GtkWidget        *aWidget,
1790
                          GdkDragContext   *aContext,
1791
                          gint              aResult,
1792
                          gpointer          aData)
1793
0
{
1794
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("invisibleSourceDragFailed %i", aResult));
1795
0
    nsDragService *dragService = (nsDragService *)aData;
1796
0
    // End the drag session now (rather than waiting for the drag-end signal)
1797
0
    // so that operations performed on dropEffect == none can start immediately
1798
0
    // rather than waiting for the drag-failed animation to finish.
1799
0
    dragService->SourceEndDragSession(aContext, aResult);
1800
0
1801
0
    // We should return TRUE to disable the drag-failed animation iff the
1802
0
    // source performed an operation when dropEffect was none, but the handler
1803
0
    // of the dragend DOM event doesn't provide this information.
1804
0
    return FALSE;
1805
0
}
1806
1807
static void
1808
invisibleSourceDragEnd(GtkWidget        *aWidget,
1809
                       GdkDragContext   *aContext,
1810
                       gpointer          aData)
1811
0
{
1812
0
    MOZ_LOG(sDragLm, LogLevel::Debug, ("invisibleSourceDragEnd"));
1813
0
    nsDragService *dragService = (nsDragService *)aData;
1814
0
1815
0
    // The drag has ended.  Release the hostages!
1816
0
    dragService->SourceEndDragSession(aContext, MOZ_GTK_DRAG_RESULT_SUCCESS);
1817
0
}
1818
1819
// The following methods handle responding to GTK drag signals and
1820
// tracking state between these signals.
1821
//
1822
// In general, GTK does not expect us to run the event loop while handling its
1823
// drag signals, however our drag event handlers may run the
1824
// event loop, most often to fetch information about the drag data.
1825
//
1826
// GTK, for example, uses the return value from drag-motion signals to
1827
// determine whether drag-leave signals should be sent.  If an event loop is
1828
// run during drag-motion the XdndLeave message can get processed but when GTK
1829
// receives the message it does not yet know that it needs to send the
1830
// drag-leave signal to our widget.
1831
//
1832
// After a drag-drop signal, we need to reply with gtk_drag_finish().
1833
// However, gtk_drag_finish should happen after the drag-drop signal handler
1834
// returns so that when the Motif drag protocol is used, the
1835
// XmTRANSFER_SUCCESS during gtk_drag_finish is sent after the XmDROP_START
1836
// reply sent on return from the drag-drop signal handler.
1837
//
1838
// Similarly drag-end for a successful drag and drag-failed are not good
1839
// times to run a nested event loop as gtk_drag_drop_finished() and
1840
// gtk_drag_source_info_destroy() don't gtk_drag_clear_source_info() or remove
1841
// drop_timeout until after at least the first of these signals is sent.
1842
// Processing other events (e.g. a slow GDK_DROP_FINISHED reply, or the drop
1843
// timeout) could cause gtk_drag_drop_finished to be called again with the
1844
// same GtkDragSourceInfo, which won't like being destroyed twice.
1845
//
1846
// Therefore we reply to the signals immediately and schedule a task to
1847
// dispatch the Gecko events, which may run the event loop.
1848
//
1849
// Action in response to drag-leave signals is also delayed until the event
1850
// loop runs again so that we find out whether a drag-drop signal follows.
1851
//
1852
// A single task is scheduled to manage responses to all three GTK signals.
1853
// If further signals are received while the task is scheduled, the scheduled
1854
// response is updated, sometimes effectively compressing successive signals.
1855
//
1856
// No Gecko drag events are dispatched (during nested event loops) while other
1857
// Gecko drag events are in flight.  This helps event handlers that may not
1858
// expect nested events, while accessing an event's dataTransfer for example.
1859
1860
gboolean
1861
nsDragService::ScheduleMotionEvent(nsWindow *aWindow,
1862
                                   GdkDragContext *aDragContext,
1863
                                   nsWaylandDragContext *aWaylandDragContext,
1864
                                   LayoutDeviceIntPoint aWindowPoint, guint aTime)
1865
0
{
1866
0
    if (aDragContext && mScheduledTask == eDragTaskMotion) {
1867
0
        // The drag source has sent another motion message before we've
1868
0
        // replied to the previous.  That shouldn't happen with Xdnd.  The
1869
0
        // spec for Motif drags is less clear, but we'll just update the
1870
0
        // scheduled task with the new position reply only to the most
1871
0
        // recent message.
1872
0
        NS_WARNING("Drag Motion message received before previous reply was sent");
1873
0
    }
1874
0
1875
0
    // Returning TRUE means we'll reply with a status message, unless we first
1876
0
    // get a leave.
1877
0
    return Schedule(eDragTaskMotion, aWindow, aDragContext, aWaylandDragContext,
1878
0
                    aWindowPoint, aTime);
1879
0
}
1880
1881
void
1882
nsDragService::ScheduleLeaveEvent()
1883
0
{
1884
0
    // We don't know at this stage whether a drop signal will immediately
1885
0
    // follow.  If the drop signal gets sent it will happen before we return
1886
0
    // to the main loop and the scheduled leave task will be replaced.
1887
0
    if (!Schedule(eDragTaskLeave, nullptr, nullptr, nullptr,
1888
0
        LayoutDeviceIntPoint(), 0)) {
1889
0
        NS_WARNING("Drag leave after drop");
1890
0
    }
1891
0
}
1892
1893
gboolean
1894
nsDragService::ScheduleDropEvent(nsWindow *aWindow,
1895
                                 GdkDragContext *aDragContext,
1896
                                 nsWaylandDragContext *aWaylandDragContext,
1897
                                 LayoutDeviceIntPoint aWindowPoint, guint aTime)
1898
0
{
1899
0
    if (!Schedule(eDragTaskDrop, aWindow,
1900
0
                  aDragContext, aWaylandDragContext, aWindowPoint, aTime)) {
1901
0
        NS_WARNING("Additional drag drop ignored");
1902
0
        return FALSE;
1903
0
    }
1904
0
1905
0
    SetDragEndPoint(aWindowPoint + aWindow->WidgetToScreenOffset());
1906
0
1907
0
    // We'll reply with gtk_drag_finish().
1908
0
    return TRUE;
1909
0
}
1910
1911
gboolean
1912
nsDragService::Schedule(DragTask aTask, nsWindow *aWindow,
1913
                        GdkDragContext *aDragContext,
1914
                        nsWaylandDragContext *aWaylandDragContext,
1915
                        LayoutDeviceIntPoint aWindowPoint, guint aTime)
1916
0
{
1917
0
    // If there is an existing leave or motion task scheduled, then that
1918
0
    // will be replaced.  When the new task is run, it will dispatch
1919
0
    // any necessary leave or motion events.
1920
0
1921
0
    // If aTask is eDragTaskSourceEnd, then it will replace even a scheduled
1922
0
    // drop event (which could happen if the drop event has not been processed
1923
0
    // within the allowed time).  Otherwise, if we haven't yet run a scheduled
1924
0
    // drop or end task, just say that we are not ready to receive another
1925
0
    // drop.
1926
0
    if (mScheduledTask == eDragTaskSourceEnd ||
1927
0
        (mScheduledTask == eDragTaskDrop && aTask != eDragTaskSourceEnd))
1928
0
        return FALSE;
1929
0
1930
0
    mScheduledTask = aTask;
1931
0
    mPendingWindow = aWindow;
1932
0
    mPendingDragContext = aDragContext;
1933
#ifdef MOZ_WAYLAND
1934
    mPendingWaylandDragContext = aWaylandDragContext;
1935
#endif
1936
    mPendingWindowPoint = aWindowPoint;
1937
0
    mPendingTime = aTime;
1938
0
1939
0
    if (!mTaskSource) {
1940
0
        // High priority is used here because the native events involved have
1941
0
        // already waited at default priority.  Perhaps a lower than default
1942
0
        // priority could be used for motion tasks because there is a chance
1943
0
        // that a leave or drop is waiting, but managing different priorities
1944
0
        // may not be worth the effort.  Motion tasks shouldn't queue up as
1945
0
        // they should be throttled based on replies.
1946
0
        mTaskSource = g_idle_add_full(G_PRIORITY_HIGH, TaskDispatchCallback,
1947
0
                                      this, nullptr);
1948
0
    }
1949
0
    return TRUE;
1950
0
}
1951
1952
gboolean
1953
nsDragService::TaskDispatchCallback(gpointer data)
1954
0
{
1955
0
    RefPtr<nsDragService> dragService = static_cast<nsDragService*>(data);
1956
0
    return dragService->RunScheduledTask();
1957
0
}
1958
1959
gboolean
1960
nsDragService::RunScheduledTask()
1961
0
{
1962
0
    if (mTargetWindow && mTargetWindow != mPendingWindow) {
1963
0
        MOZ_LOG(sDragLm, LogLevel::Debug,
1964
0
               ("nsDragService: dispatch drag leave (%p)\n",
1965
0
                mTargetWindow.get()));
1966
0
        mTargetWindow->DispatchDragEvent(eDragExit, mTargetWindowPoint, 0);
1967
0
1968
0
        if (!mSourceNode) {
1969
0
            // The drag that was initiated in a different app. End the drag
1970
0
            // session, since we're done with it for now (until the user drags
1971
0
            // back into this app).
1972
0
            EndDragSession(false, GetCurrentModifiers());
1973
0
        }
1974
0
    }
1975
0
1976
0
    // It is possible that the pending state has been updated during dispatch
1977
0
    // of the leave event.  That's fine.
1978
0
1979
0
    // Now we collect the pending state because, from this point on, we want
1980
0
    // to use the same state for all events dispatched.  All state is updated
1981
0
    // so that when other tasks are scheduled during dispatch here, this
1982
0
    // task is considered to have already been run.
1983
0
    bool positionHasChanged =
1984
0
        mPendingWindow != mTargetWindow ||
1985
0
        mPendingWindowPoint != mTargetWindowPoint;
1986
0
    DragTask task = mScheduledTask;
1987
0
    mScheduledTask = eDragTaskNone;
1988
0
    mTargetWindow = mPendingWindow.forget();
1989
0
    mTargetWindowPoint = mPendingWindowPoint;
1990
0
1991
0
    if (task == eDragTaskLeave || task == eDragTaskSourceEnd) {
1992
0
        if (task == eDragTaskSourceEnd) {
1993
0
            // Dispatch drag end events.
1994
0
            EndDragSession(true, GetCurrentModifiers());
1995
0
        }
1996
0
1997
0
        // Nothing more to do
1998
0
        // Returning false removes the task source from the event loop.
1999
0
        mTaskSource = 0;
2000
0
        return FALSE;
2001
0
    }
2002
0
2003
0
    // This may be the start of a destination drag session.
2004
0
    StartDragSession();
2005
0
2006
0
    // mTargetWidget may be nullptr if the window has been destroyed.
2007
0
    // (The leave event is not scheduled if a drop task is still scheduled.)
2008
0
    // We still reply appropriately to indicate that the drop will or didn't
2009
0
    // succeeed.
2010
0
    mTargetWidget = mTargetWindow->GetMozContainerWidget();
2011
0
    mTargetDragContext.steal(mPendingDragContext);
2012
#ifdef MOZ_WAYLAND
2013
    mTargetWaylandDragContext = mPendingWaylandDragContext.forget();
2014
#endif
2015
    mTargetTime = mPendingTime;
2016
0
2017
0
    // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
2018
0
    // (as at 27 December 2010) indicates that a "drop" event should only be
2019
0
    // fired (at the current target element) if the current drag operation is
2020
0
    // not none.  The current drag operation will only be set to a non-none
2021
0
    // value during a "dragover" event.
2022
0
    //
2023
0
    // If the user has ended the drag before any dragover events have been
2024
0
    // sent, then the spec recommends skipping the drop (because the current
2025
0
    // drag operation is none).  However, here we assume that, by releasing
2026
0
    // the mouse button, the user has indicated that they want to drop, so we
2027
0
    // proceed with the drop where possible.
2028
0
    //
2029
0
    // In order to make the events appear to content in the same way as if the
2030
0
    // spec is being followed we make sure to dispatch a "dragover" event with
2031
0
    // appropriate coordinates and check canDrop before the "drop" event.
2032
0
    //
2033
0
    // When the Xdnd protocol is used for source/destination communication (as
2034
0
    // should be the case with GTK source applications) a dragover event
2035
0
    // should have already been sent during the drag-motion signal, which
2036
0
    // would have already been received because XdndDrop messages do not
2037
0
    // contain a position.  However, we can't assume the same when the Motif
2038
0
    // protocol is used.
2039
0
    if (task == eDragTaskMotion || positionHasChanged) {
2040
0
        UpdateDragAction();
2041
0
        TakeDragEventDispatchedToChildProcess(); // Clear the old value.
2042
0
        DispatchMotionEvents();
2043
0
        if (task == eDragTaskMotion) {
2044
0
          if (TakeDragEventDispatchedToChildProcess()) {
2045
0
              mTargetDragContextForRemote = mTargetDragContext;
2046
#ifdef MOZ_WAYLAND
2047
              mTargetWaylandDragContextForRemote = mTargetWaylandDragContext;
2048
#endif
2049
0
          } else {
2050
0
              // Reply to tell the source whether we can drop and what
2051
0
              // action would be taken.
2052
0
              if (mTargetDragContext) {
2053
0
                  ReplyToDragMotion(mTargetDragContext);
2054
0
              }
2055
#ifdef MOZ_WAYLAND
2056
              else if (mTargetWaylandDragContext) {
2057
                  ReplyToDragMotion(mTargetWaylandDragContext);
2058
              }
2059
#endif
2060
          }
2061
0
        }
2062
0
    }
2063
0
2064
0
    if (task == eDragTaskDrop) {
2065
0
        gboolean success = DispatchDropEvent();
2066
0
2067
0
        // Perhaps we should set the del parameter to TRUE when the drag
2068
0
        // action is move, but we don't know whether the data was successfully
2069
0
        // transferred.
2070
0
        if (mTargetDragContext) {
2071
0
            gtk_drag_finish(mTargetDragContext, success,
2072
0
                            /* del = */ FALSE, mTargetTime);
2073
0
        }
2074
0
2075
0
        // This drag is over, so clear out our reference to the previous
2076
0
        // window.
2077
0
        mTargetWindow = nullptr;
2078
0
        // Make sure to end the drag session. If this drag started in a
2079
0
        // different app, we won't get a drag_end signal to end it from.
2080
0
        EndDragSession(true, GetCurrentModifiers());
2081
0
    }
2082
0
2083
0
    // We're done with the drag context.
2084
0
    mTargetWidget = nullptr;
2085
0
    mTargetDragContext = nullptr;
2086
#ifdef MOZ_WAYLAND
2087
    mTargetWaylandDragContext = nullptr;
2088
#endif
2089
2090
0
    // If we got another drag signal while running the sheduled task, that
2091
0
    // must have happened while running a nested event loop.  Leave the task
2092
0
    // source on the event loop.
2093
0
    if (mScheduledTask != eDragTaskNone)
2094
0
        return TRUE;
2095
0
2096
0
    // We have no task scheduled.
2097
0
    // Returning false removes the task source from the event loop.
2098
0
    mTaskSource = 0;
2099
0
    return FALSE;
2100
0
}
2101
2102
// This will update the drag action based on the information in the
2103
// drag context.  Gtk gets this from a combination of the key settings
2104
// and what the source is offering.
2105
2106
void
2107
nsDragService::UpdateDragAction()
2108
0
{
2109
0
    // This doesn't look right.  dragSession.dragAction is used by
2110
0
    // nsContentUtils::SetDataTransferInEvent() to set the initial
2111
0
    // dataTransfer.dropEffect, so GdkDragContext::suggested_action would be
2112
0
    // more appropriate.  GdkDragContext::actions should be used to set
2113
0
    // dataTransfer.effectAllowed, which doesn't currently happen with
2114
0
    // external sources.
2115
0
2116
0
    // default is to do nothing
2117
0
    int action = nsIDragService::DRAGDROP_ACTION_NONE;
2118
0
    GdkDragAction gdkAction = GDK_ACTION_DEFAULT;
2119
0
    if (mTargetDragContext) {
2120
0
        gdkAction = gdk_drag_context_get_actions(mTargetDragContext);
2121
0
    }
2122
#ifdef MOZ_WAYLAND
2123
    else if (mTargetWaylandDragContext) {
2124
        // We got the selected D&D action from compositor on Wayland.
2125
        gdkAction = mTargetWaylandDragContext->GetSelectedDragAction();
2126
    }
2127
#endif
2128
2129
0
    // set the default just in case nothing matches below
2130
0
    if (gdkAction & GDK_ACTION_DEFAULT)
2131
0
        action = nsIDragService::DRAGDROP_ACTION_MOVE;
2132
0
2133
0
    // first check to see if move is set
2134
0
    if (gdkAction & GDK_ACTION_MOVE)
2135
0
        action = nsIDragService::DRAGDROP_ACTION_MOVE;
2136
0
2137
0
    // then fall to the others
2138
0
    else if (gdkAction & GDK_ACTION_LINK)
2139
0
        action = nsIDragService::DRAGDROP_ACTION_LINK;
2140
0
2141
0
    // copy is ctrl
2142
0
    else if (gdkAction & GDK_ACTION_COPY)
2143
0
        action = nsIDragService::DRAGDROP_ACTION_COPY;
2144
0
2145
0
    // update the drag information
2146
0
    SetDragAction(action);
2147
0
}
2148
2149
NS_IMETHODIMP
2150
nsDragService::UpdateDragEffect()
2151
0
{
2152
0
  if (mTargetDragContextForRemote) {
2153
0
    ReplyToDragMotion(mTargetDragContextForRemote);
2154
0
    mTargetDragContextForRemote = nullptr;
2155
0
  }
2156
#ifdef MOZ_WAYLAND
2157
  else if (mTargetWaylandDragContextForRemote) {
2158
    ReplyToDragMotion(mTargetWaylandDragContextForRemote);
2159
    mTargetWaylandDragContextForRemote = nullptr;
2160
  }
2161
#endif
2162
  return NS_OK;
2163
0
}
2164
2165
void
2166
nsDragService::DispatchMotionEvents()
2167
0
{
2168
0
    mCanDrop = false;
2169
0
2170
0
    FireDragEventAtSource(eDrag, GetCurrentModifiers());
2171
0
2172
0
    mTargetWindow->DispatchDragEvent(eDragOver, mTargetWindowPoint,
2173
0
                                     mTargetTime);
2174
0
}
2175
2176
// Returns true if the drop was successful
2177
gboolean
2178
nsDragService::DispatchDropEvent()
2179
0
{
2180
0
    // We need to check IsDestroyed here because the nsRefPtr
2181
0
    // only protects this from being deleted, it does NOT protect
2182
0
    // against nsView::~nsView() calling Destroy() on it, bug 378273.
2183
0
    if (mTargetWindow->IsDestroyed())
2184
0
        return FALSE;
2185
0
2186
0
    EventMessage msg = mCanDrop ? eDrop : eDragExit;
2187
0
2188
0
    mTargetWindow->DispatchDragEvent(msg, mTargetWindowPoint, mTargetTime);
2189
0
2190
0
    return mCanDrop;
2191
0
}
2192
2193
/* static */ uint32_t
2194
nsDragService::GetCurrentModifiers()
2195
0
{
2196
0
  return mozilla::widget::KeymapWrapper::ComputeCurrentKeyModifiers();
2197
0
}