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