widget/gtk/nsDragService.cpp

Thu, 15 Jan 2015 15:59:08 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 15 Jan 2015 15:59:08 +0100
branch
TOR_BUG_9701
changeset 10
ac0c01689b40
permissions
-rw-r--r--

Implement a real Private Browsing Mode condition by changing the API/ABI;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.

     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;
  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;
  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;
  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;
  1034         g_free(name);
  1036     return NS_OK;
  1039 void
  1040 nsDragService::ReplyToDragMotion()
  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;
  1061     gdk_drag_status(mTargetDragContext, action, mTargetTime);
  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)
  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);
  1083     else {
  1084         PR_LOG(sDragLm, PR_LOG_DEBUG,
  1085                ("Failed to get data.  selection data len was %d\n",
  1086                 mTargetDragDataLen));
  1090 bool
  1091 nsDragService::IsTargetContextList(void)
  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;
  1118     return retval;
  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)
  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();
  1144     PR_LOG(sDragLm, PR_LOG_DEBUG, ("finished inner iteration\n"));
  1147 void
  1148 nsDragService::TargetResetData(void)
  1150     mTargetDragDataReceived = false;
  1151     // make sure to free old data if we have to
  1152     g_free(mTargetDragData);
  1153     mTargetDragData = 0;
  1154     mTargetDragDataLen = 0;
  1157 GtkTargetList *
  1158 nsDragService::GetSourceList(void)
  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);
  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);
  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);
  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;
  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);
  1318         g_free(targets);
  1320     return targetList;
  1323 void
  1324 nsDragService::SourceEndDragSession(GdkDragContext *aContext,
  1325                                     gint            aResult)
  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));
  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;
  1384     if (mDataTransfer) {
  1385         mDataTransfer->SetDropEffectInt(dropEffect);
  1388     // Schedule the appropriate drag end dom events.
  1389     Schedule(eDragTaskSourceEnd, nullptr, nullptr, nsIntPoint(), 0);
  1392 static void
  1393 CreateUriList(nsISupportsArray *items, gchar **text, gint *length)
  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;
  1439                     g_string_append(uriList, plainTextData);
  1440                     g_string_append(uriList, "\r\n");
  1441                     // this wasn't allocated with glib
  1442                     free(plainTextData);
  1444                 if (tmpData) {
  1445                     // this wasn't allocated with glib
  1446                     free(tmpData);
  1451     *text = uriList->str;
  1452     *length = uriList->len + 1;
  1453     g_string_free(uriList, FALSE); // don't free the data
  1457 void
  1458 nsDragService::SourceDataGet(GtkWidget        *aWidget,
  1459                              GdkDragContext   *aContext,
  1460                              GtkSelectionData *aSelectionData,
  1461                              guint32           aTime)
  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;
  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;
  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;
  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;
  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;
  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);
  1541                 if (tmpData) {
  1542                     // this was not allocated using glib
  1543                     free(tmpData);
  1544                     tmpData = plainTextData;
  1545                     tmpDataLen = plainTextLen;
  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);
  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;
  1571 void nsDragService::SetDragIcon(GdkDragContext* aContext)
  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);
  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);
  1619 static void
  1620 invisibleSourceDragBegin(GtkWidget        *aWidget,
  1621                          GdkDragContext   *aContext,
  1622                          gpointer          aData)
  1624     PR_LOG(sDragLm, PR_LOG_DEBUG, ("invisibleSourceDragBegin"));
  1625     nsDragService *dragService = (nsDragService *)aData;
  1627     dragService->SetDragIcon(aContext);
  1630 static void
  1631 invisibleSourceDragDataGet(GtkWidget        *aWidget,
  1632                            GdkDragContext   *aContext,
  1633                            GtkSelectionData *aSelectionData,
  1634                            guint             aInfo,
  1635                            guint32           aTime,
  1636                            gpointer          aData)
  1638     PR_LOG(sDragLm, PR_LOG_DEBUG, ("invisibleSourceDragDataGet"));
  1639     nsDragService *dragService = (nsDragService *)aData;
  1640     dragService->SourceDataGet(aWidget, aContext,
  1641                                aSelectionData, aTime);
  1644 static gboolean
  1645 invisibleSourceDragFailed(GtkWidget        *aWidget,
  1646                           GdkDragContext   *aContext,
  1647                           gint              aResult,
  1648                           gpointer          aData)
  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;
  1663 static void
  1664 invisibleSourceDragEnd(GtkWidget        *aWidget,
  1665                        GdkDragContext   *aContext,
  1666                        gpointer          aData)
  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);
  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)
  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");
  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);
  1736 void
  1737 nsDragService::ScheduleLeaveEvent()
  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");
  1747 gboolean
  1748 nsDragService::ScheduleDropEvent(nsWindow *aWindow,
  1749                                  GdkDragContext *aDragContext,
  1750                                  nsIntPoint aWindowPoint, guint aTime)
  1752     if (!Schedule(eDragTaskDrop, aWindow,
  1753                   aDragContext, aWindowPoint, aTime)) {
  1754         NS_WARNING("Additional drag drop ignored");
  1755         return FALSE;        
  1758     SetDragEndPoint(aWindowPoint + aWindow->WidgetToScreenOffset());
  1760     // We'll reply with gtk_drag_finish().
  1761     return TRUE;
  1764 gboolean
  1765 nsDragService::Schedule(DragTask aTask, nsWindow *aWindow,
  1766                         GdkDragContext *aDragContext,
  1767                         nsIntPoint aWindowPoint, guint aTime)
  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);
  1798     return TRUE;
  1801 gboolean
  1802 nsDragService::TaskDispatchCallback(gpointer data)
  1804     nsRefPtr<nsDragService> dragService = static_cast<nsDragService*>(data);
  1805     return dragService->RunScheduledTask();
  1808 gboolean
  1809 nsDragService::RunScheduledTask()
  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);
  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);
  1847         // Nothing more to do
  1848         // Returning false removes the task source from the event loop.
  1849         mTaskSource = 0;
  1850         return FALSE;
  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();
  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);
  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;
  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()
  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);
  1968 void
  1969 nsDragService::DispatchMotionEvents()
  1971     mCanDrop = false;
  1973     FireDragEventAtSource(NS_DRAGDROP_DRAG);
  1975     mTargetWindow->
  1976         DispatchDragEvent(NS_DRAGDROP_OVER, mTargetWindowPoint, mTargetTime);
  1979 // Returns true if the drop was successful
  1980 gboolean
  1981 nsDragService::DispatchDropEvent()
  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;

mercurial