LCOV - code coverage report
Current view: top level - widget/gtk - nsDragService.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 32 941 3.4 %
Date: 2017-07-14 16:53:18 Functions: 5 50 10.0 %
Legend: Lines: hit not hit

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

Generated by: LCOV version 1.13