widget/gtk/nsDragService.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/widget/gtk/nsDragService.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,1994 @@
     1.4 +/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
     1.5 +/* vim: set ts=4 et sw=4 tw=80: */
     1.6 +/* This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +#include "nsDragService.h"
    1.11 +#include "nsIObserverService.h"
    1.12 +#include "nsWidgetsCID.h"
    1.13 +#include "nsWindow.h"
    1.14 +#include "nsIServiceManager.h"
    1.15 +#include "nsXPCOM.h"
    1.16 +#include "nsISupportsPrimitives.h"
    1.17 +#include "nsIIOService.h"
    1.18 +#include "nsIFileURL.h"
    1.19 +#include "nsNetUtil.h"
    1.20 +#include "prlog.h"
    1.21 +#include "nsTArray.h"
    1.22 +#include "nsPrimitiveHelpers.h"
    1.23 +#include "prtime.h"
    1.24 +#include "prthread.h"
    1.25 +#include <gtk/gtk.h>
    1.26 +#include <gdk/gdkx.h>
    1.27 +#include "nsCRT.h"
    1.28 +#include "mozilla/BasicEvents.h"
    1.29 +#include "mozilla/Services.h"
    1.30 +
    1.31 +#include "gfxASurface.h"
    1.32 +#include "gfxXlibSurface.h"
    1.33 +#include "gfxContext.h"
    1.34 +#include "nsImageToPixbuf.h"
    1.35 +#include "nsPresContext.h"
    1.36 +#include "nsIContent.h"
    1.37 +#include "nsIDocument.h"
    1.38 +#include "nsISelection.h"
    1.39 +#include "nsViewManager.h"
    1.40 +#include "nsIFrame.h"
    1.41 +#include "nsGtkUtils.h"
    1.42 +#include "mozilla/gfx/2D.h"
    1.43 +#include "gfxPlatform.h"
    1.44 +
    1.45 +using namespace mozilla;
    1.46 +using namespace mozilla::gfx;
    1.47 +
    1.48 +// This sets how opaque the drag image is
    1.49 +#define DRAG_IMAGE_ALPHA_LEVEL 0.5
    1.50 +
    1.51 +// These values are copied from GtkDragResult (rather than using GtkDragResult
    1.52 +// directly) so that this code can be compiled against versions of GTK+ that
    1.53 +// do not have GtkDragResult.
    1.54 +// GtkDragResult is available from GTK+ version 2.12.
    1.55 +enum {
    1.56 +  MOZ_GTK_DRAG_RESULT_SUCCESS,
    1.57 +  MOZ_GTK_DRAG_RESULT_NO_TARGET
    1.58 +};
    1.59 +
    1.60 +static PRLogModuleInfo *sDragLm = nullptr;
    1.61 +
    1.62 +// data used for synthetic periodic motion events sent to the source widget
    1.63 +// grabbing real events for the drag.
    1.64 +static guint sMotionEventTimerID;
    1.65 +static GdkEvent *sMotionEvent;
    1.66 +static GtkWidget *sGrabWidget;
    1.67 +
    1.68 +static const char gMimeListType[] = "application/x-moz-internal-item-list";
    1.69 +static const char gMozUrlType[] = "_NETSCAPE_URL";
    1.70 +static const char gTextUriListType[] = "text/uri-list";
    1.71 +static const char gTextPlainUTF8Type[] = "text/plain;charset=utf-8";
    1.72 +
    1.73 +static void
    1.74 +invisibleSourceDragBegin(GtkWidget        *aWidget,
    1.75 +                         GdkDragContext   *aContext,
    1.76 +                         gpointer          aData);
    1.77 +
    1.78 +static void
    1.79 +invisibleSourceDragEnd(GtkWidget        *aWidget,
    1.80 +                       GdkDragContext   *aContext,
    1.81 +                       gpointer          aData);
    1.82 +
    1.83 +static gboolean
    1.84 +invisibleSourceDragFailed(GtkWidget        *aWidget,
    1.85 +                          GdkDragContext   *aContext,
    1.86 +                          gint              aResult,
    1.87 +                          gpointer          aData);
    1.88 +
    1.89 +static void
    1.90 +invisibleSourceDragDataGet(GtkWidget        *aWidget,
    1.91 +                           GdkDragContext   *aContext,
    1.92 +                           GtkSelectionData *aSelectionData,
    1.93 +                           guint             aInfo,
    1.94 +                           guint32           aTime,
    1.95 +                           gpointer          aData);
    1.96 +
    1.97 +nsDragService::nsDragService()
    1.98 +    : mScheduledTask(eDragTaskNone)
    1.99 +    , mTaskSource(0)
   1.100 +{
   1.101 +    // We have to destroy the hidden widget before the event loop stops
   1.102 +    // running.
   1.103 +    nsCOMPtr<nsIObserverService> obsServ =
   1.104 +        mozilla::services::GetObserverService();
   1.105 +    obsServ->AddObserver(this, "quit-application", false);
   1.106 +
   1.107 +    // our hidden source widget
   1.108 +    mHiddenWidget = gtk_window_new(GTK_WINDOW_POPUP);
   1.109 +    // make sure that the widget is realized so that
   1.110 +    // we can use it as a drag source.
   1.111 +    gtk_widget_realize(mHiddenWidget);
   1.112 +    // hook up our internal signals so that we can get some feedback
   1.113 +    // from our drag source
   1.114 +    g_signal_connect(mHiddenWidget, "drag_begin",
   1.115 +                     G_CALLBACK(invisibleSourceDragBegin), this);
   1.116 +    g_signal_connect(mHiddenWidget, "drag_data_get",
   1.117 +                     G_CALLBACK(invisibleSourceDragDataGet), this);
   1.118 +    g_signal_connect(mHiddenWidget, "drag_end",
   1.119 +                     G_CALLBACK(invisibleSourceDragEnd), this);
   1.120 +    // drag-failed is available from GTK+ version 2.12
   1.121 +    guint dragFailedID = g_signal_lookup("drag-failed",
   1.122 +                                         G_TYPE_FROM_INSTANCE(mHiddenWidget));
   1.123 +    if (dragFailedID) {
   1.124 +        g_signal_connect_closure_by_id(mHiddenWidget, dragFailedID, 0,
   1.125 +                                       g_cclosure_new(G_CALLBACK(invisibleSourceDragFailed),
   1.126 +                                                      this, nullptr),
   1.127 +                                       FALSE);
   1.128 +    }
   1.129 +
   1.130 +    // set up our logging module
   1.131 +    if (!sDragLm)
   1.132 +        sDragLm = PR_NewLogModule("nsDragService");
   1.133 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::nsDragService"));
   1.134 +    mCanDrop = false;
   1.135 +    mTargetDragDataReceived = false;
   1.136 +    mTargetDragData = 0;
   1.137 +    mTargetDragDataLen = 0;
   1.138 +}
   1.139 +
   1.140 +nsDragService::~nsDragService()
   1.141 +{
   1.142 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::~nsDragService"));
   1.143 +    if (mTaskSource)
   1.144 +        g_source_remove(mTaskSource);
   1.145 +
   1.146 +}
   1.147 +
   1.148 +NS_IMPL_ISUPPORTS_INHERITED(nsDragService, nsBaseDragService, nsIObserver)
   1.149 +
   1.150 +/* static */ nsDragService*
   1.151 +nsDragService::GetInstance()
   1.152 +{
   1.153 +    static const nsIID iid = NS_DRAGSERVICE_CID;
   1.154 +    nsCOMPtr<nsIDragService> dragService = do_GetService(iid);
   1.155 +    return static_cast<nsDragService*>(dragService.get());
   1.156 +    // We rely on XPCOM keeping a reference to the service.
   1.157 +}
   1.158 +
   1.159 +// nsIObserver
   1.160 +
   1.161 +NS_IMETHODIMP
   1.162 +nsDragService::Observe(nsISupports *aSubject, const char *aTopic,
   1.163 +                       const char16_t *aData)
   1.164 +{
   1.165 +  if (!nsCRT::strcmp(aTopic, "quit-application")) {
   1.166 +    PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.167 +           ("nsDragService::Observe(\"quit-application\")"));
   1.168 +    if (mHiddenWidget) {
   1.169 +      gtk_widget_destroy(mHiddenWidget);
   1.170 +      mHiddenWidget = 0;
   1.171 +    }
   1.172 +    TargetResetData();
   1.173 +  } else {
   1.174 +    NS_NOTREACHED("unexpected topic");
   1.175 +    return NS_ERROR_UNEXPECTED;
   1.176 +  }
   1.177 +
   1.178 +  return NS_OK;
   1.179 +}
   1.180 +
   1.181 +// Support for periodic drag events
   1.182 +
   1.183 +// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
   1.184 +// and the Xdnd protocol both recommend that drag events are sent periodically,
   1.185 +// but GTK does not normally provide this.
   1.186 +//
   1.187 +// Here GTK is periodically stimulated by copies of the most recent mouse
   1.188 +// motion events so as to send drag position messages to the destination when
   1.189 +// appropriate (after it has received a status event from the previous
   1.190 +// message).
   1.191 +//
   1.192 +// (If events were sent only on the destination side then the destination
   1.193 +// would have no message to which it could reply with a drag status.  Without
   1.194 +// sending a drag status to the source, the destination would not be able to
   1.195 +// change its feedback re whether it could accept the drop, and so the
   1.196 +// source's behavior on drop will not be consistent.)
   1.197 +
   1.198 +static gboolean
   1.199 +DispatchMotionEventCopy(gpointer aData)
   1.200 +{
   1.201 +    // Clear the timer id before OnSourceGrabEventAfter is called during event
   1.202 +    // dispatch.
   1.203 +    sMotionEventTimerID = 0;
   1.204 +
   1.205 +    GdkEvent *event = sMotionEvent;
   1.206 +    sMotionEvent = nullptr;
   1.207 +    // If there is no longer a grab on the widget, then the drag is over and
   1.208 +    // there is no need to continue drag motion.
   1.209 +    if (gtk_widget_has_grab(sGrabWidget)) {
   1.210 +        gtk_propagate_event(sGrabWidget, event);
   1.211 +    }
   1.212 +    gdk_event_free(event);
   1.213 +
   1.214 +    // Cancel this timer;
   1.215 +    // We've already started another if the motion event was dispatched.
   1.216 +    return FALSE;
   1.217 +}
   1.218 +
   1.219 +static void
   1.220 +OnSourceGrabEventAfter(GtkWidget *widget, GdkEvent *event, gpointer user_data)
   1.221 +{
   1.222 +    // If there is no longer a grab on the widget, then the drag motion is
   1.223 +    // over (though the data may not be fetched yet).
   1.224 +    if (!gtk_widget_has_grab(sGrabWidget))
   1.225 +        return;
   1.226 +
   1.227 +    if (event->type == GDK_MOTION_NOTIFY) {
   1.228 +        if (sMotionEvent) {
   1.229 +            gdk_event_free(sMotionEvent);
   1.230 +        }
   1.231 +        sMotionEvent = gdk_event_copy(event);
   1.232 +
   1.233 +        // Update the cursor position.  The last of these recorded gets used for
   1.234 +        // the NS_DRAGDROP_END event.
   1.235 +        nsDragService *dragService = static_cast<nsDragService*>(user_data);
   1.236 +        dragService->SetDragEndPoint(nsIntPoint(event->motion.x_root,
   1.237 +                                                event->motion.y_root));
   1.238 +    } else if (sMotionEvent && (event->type == GDK_KEY_PRESS ||
   1.239 +                                event->type == GDK_KEY_RELEASE)) {
   1.240 +        // Update modifier state from key events.
   1.241 +        sMotionEvent->motion.state = event->key.state;
   1.242 +    } else {
   1.243 +        return;
   1.244 +    }
   1.245 +
   1.246 +    if (sMotionEventTimerID) {
   1.247 +        g_source_remove(sMotionEventTimerID);
   1.248 +    }
   1.249 +
   1.250 +    // G_PRIORITY_DEFAULT_IDLE is lower priority than GDK's redraw idle source
   1.251 +    // and lower than GTK's idle source that sends drag position messages after
   1.252 +    // motion-notify signals.
   1.253 +    //
   1.254 +    // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
   1.255 +    // recommends an interval of 350ms +/- 200ms.
   1.256 +    sMotionEventTimerID = 
   1.257 +        g_timeout_add_full(G_PRIORITY_DEFAULT_IDLE, 350,
   1.258 +                           DispatchMotionEventCopy, nullptr, nullptr);
   1.259 +}
   1.260 +
   1.261 +static GtkWindow*
   1.262 +GetGtkWindow(nsIDOMDocument *aDocument)
   1.263 +{
   1.264 +    nsCOMPtr<nsIDocument> doc = do_QueryInterface(aDocument);
   1.265 +    if (!doc)
   1.266 +        return nullptr;
   1.267 +
   1.268 +    nsCOMPtr<nsIPresShell> presShell = doc->GetShell();
   1.269 +    if (!presShell)
   1.270 +        return nullptr;
   1.271 +
   1.272 +    nsRefPtr<nsViewManager> vm = presShell->GetViewManager();
   1.273 +    if (!vm)
   1.274 +        return nullptr;
   1.275 +
   1.276 +    nsCOMPtr<nsIWidget> widget;
   1.277 +    vm->GetRootWidget(getter_AddRefs(widget));
   1.278 +    if (!widget)
   1.279 +        return nullptr;
   1.280 +
   1.281 +    GtkWidget *gtkWidget =
   1.282 +        static_cast<nsWindow*>(widget.get())->GetMozContainerWidget();
   1.283 +    if (!gtkWidget)
   1.284 +        return nullptr;
   1.285 +
   1.286 +    GtkWidget *toplevel = nullptr;
   1.287 +    toplevel = gtk_widget_get_toplevel(gtkWidget);
   1.288 +    if (!GTK_IS_WINDOW(toplevel))
   1.289 +        return nullptr;
   1.290 +
   1.291 +    return GTK_WINDOW(toplevel);
   1.292 +}   
   1.293 +
   1.294 +// nsIDragService
   1.295 +
   1.296 +NS_IMETHODIMP
   1.297 +nsDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
   1.298 +                                 nsISupportsArray * aArrayTransferables,
   1.299 +                                 nsIScriptableRegion * aRegion,
   1.300 +                                 uint32_t aActionType)
   1.301 +{
   1.302 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::InvokeDragSession"));
   1.303 +
   1.304 +    // If the previous source drag has not yet completed, signal handlers need
   1.305 +    // to be removed from sGrabWidget and dragend needs to be dispatched to
   1.306 +    // the source node, but we can't call EndDragSession yet because we don't
   1.307 +    // know whether or not the drag succeeded.
   1.308 +    if (mSourceNode)
   1.309 +        return NS_ERROR_NOT_AVAILABLE;
   1.310 +
   1.311 +    nsresult rv = nsBaseDragService::InvokeDragSession(aDOMNode,
   1.312 +                                                       aArrayTransferables,
   1.313 +                                                       aRegion, aActionType);
   1.314 +    NS_ENSURE_SUCCESS(rv, rv);
   1.315 +
   1.316 +    // make sure that we have an array of transferables to use
   1.317 +    if (!aArrayTransferables)
   1.318 +        return NS_ERROR_INVALID_ARG;
   1.319 +    // set our reference to the transferables.  this will also addref
   1.320 +    // the transferables since we're going to hang onto this beyond the
   1.321 +    // length of this call
   1.322 +    mSourceDataItems = aArrayTransferables;
   1.323 +    // get the list of items we offer for drags
   1.324 +    GtkTargetList *sourceList = GetSourceList();
   1.325 +
   1.326 +    if (!sourceList)
   1.327 +        return NS_OK;
   1.328 +
   1.329 +    // stored temporarily until the drag-begin signal has been received
   1.330 +    mSourceRegion = aRegion;
   1.331 +
   1.332 +    // save our action type
   1.333 +    GdkDragAction action = GDK_ACTION_DEFAULT;
   1.334 +
   1.335 +    if (aActionType & DRAGDROP_ACTION_COPY)
   1.336 +        action = (GdkDragAction)(action | GDK_ACTION_COPY);
   1.337 +    if (aActionType & DRAGDROP_ACTION_MOVE)
   1.338 +        action = (GdkDragAction)(action | GDK_ACTION_MOVE);
   1.339 +    if (aActionType & DRAGDROP_ACTION_LINK)
   1.340 +        action = (GdkDragAction)(action | GDK_ACTION_LINK);
   1.341 +
   1.342 +    // Create a fake event for the drag so we can pass the time (so to speak).
   1.343 +    // If we don't do this, then, when the timestamp for the pending button
   1.344 +    // release event is used for the ungrab, the ungrab can fail due to the
   1.345 +    // timestamp being _earlier_ than CurrentTime.
   1.346 +    GdkEvent event;
   1.347 +    memset(&event, 0, sizeof(GdkEvent));
   1.348 +    event.type = GDK_BUTTON_PRESS;
   1.349 +    event.button.window = gtk_widget_get_window(mHiddenWidget);
   1.350 +    event.button.time = nsWindow::GetLastUserInputTime();
   1.351 +
   1.352 +    // Put the drag widget in the window group of the source node so that the
   1.353 +    // gtk_grab_add during gtk_drag_begin is effective.
   1.354 +    // gtk_window_get_group(nullptr) returns the default window group.
   1.355 +    GtkWindowGroup *window_group =
   1.356 +        gtk_window_get_group(GetGtkWindow(mSourceDocument));
   1.357 +    gtk_window_group_add_window(window_group,
   1.358 +                                GTK_WINDOW(mHiddenWidget));
   1.359 +
   1.360 +#if (MOZ_WIDGET_GTK == 3)
   1.361 +    // Get device for event source
   1.362 +    GdkDisplay *display = gdk_display_get_default();
   1.363 +    GdkDeviceManager *device_manager = gdk_display_get_device_manager(display);
   1.364 +    event.button.device = gdk_device_manager_get_client_pointer(device_manager);
   1.365 +#endif
   1.366 +  
   1.367 +    // start our drag.
   1.368 +    GdkDragContext *context = gtk_drag_begin(mHiddenWidget,
   1.369 +                                             sourceList,
   1.370 +                                             action,
   1.371 +                                             1,
   1.372 +                                             &event);
   1.373 +
   1.374 +    mSourceRegion = nullptr;
   1.375 +
   1.376 +    if (context) {
   1.377 +        StartDragSession();
   1.378 +
   1.379 +        // GTK uses another hidden window for receiving mouse events.
   1.380 +        sGrabWidget = gtk_window_group_get_current_grab(window_group);
   1.381 +        if (sGrabWidget) {
   1.382 +            g_object_ref(sGrabWidget);
   1.383 +            // Only motion and key events are required but connect to
   1.384 +            // "event-after" as this is never blocked by other handlers.
   1.385 +            g_signal_connect(sGrabWidget, "event-after",
   1.386 +                             G_CALLBACK(OnSourceGrabEventAfter), this);
   1.387 +        }
   1.388 +        // We don't have a drag end point yet.
   1.389 +        mEndDragPoint = nsIntPoint(-1, -1);
   1.390 +    }
   1.391 +    else {
   1.392 +        rv = NS_ERROR_FAILURE;
   1.393 +    }
   1.394 +
   1.395 +    gtk_target_list_unref(sourceList);
   1.396 +
   1.397 +    return rv;
   1.398 +}
   1.399 +
   1.400 +bool
   1.401 +nsDragService::SetAlphaPixmap(SourceSurface *aSurface,
   1.402 +                              GdkDragContext *aContext,
   1.403 +                              int32_t aXOffset,
   1.404 +                              int32_t aYOffset,
   1.405 +                              const nsIntRect& dragRect)
   1.406 +{
   1.407 +#if (MOZ_WIDGET_GTK == 2)
   1.408 +    GdkScreen* screen = gtk_widget_get_screen(mHiddenWidget);
   1.409 +
   1.410 +    // Transparent drag icons need, like a lot of transparency-related things,
   1.411 +    // a compositing X window manager
   1.412 +    if (!gdk_screen_is_composited(screen))
   1.413 +      return false;
   1.414 +
   1.415 +    GdkColormap* alphaColormap = gdk_screen_get_rgba_colormap(screen);
   1.416 +    if (!alphaColormap)
   1.417 +      return false;
   1.418 +
   1.419 +    GdkPixmap* pixmap = gdk_pixmap_new(nullptr, dragRect.width, dragRect.height,
   1.420 +                                       gdk_colormap_get_visual(alphaColormap)->depth);
   1.421 +    if (!pixmap)
   1.422 +      return false;
   1.423 +
   1.424 +    gdk_drawable_set_colormap(GDK_DRAWABLE(pixmap), alphaColormap);
   1.425 +
   1.426 +    // Make a gfxXlibSurface wrapped around the pixmap to render on
   1.427 +    nsRefPtr<gfxASurface> xPixmapSurface =
   1.428 +         nsWindow::GetSurfaceForGdkDrawable(GDK_DRAWABLE(pixmap),
   1.429 +                                            dragRect.Size());
   1.430 +    if (!xPixmapSurface)
   1.431 +      return false;
   1.432 +
   1.433 +    RefPtr<DrawTarget> dt =
   1.434 +    gfxPlatform::GetPlatform()->
   1.435 +      CreateDrawTargetForSurface(xPixmapSurface, IntSize(dragRect.width, dragRect.height));
   1.436 +    if (!dt)
   1.437 +      return false;
   1.438 +
   1.439 +    // Clear it...
   1.440 +    dt->ClearRect(Rect(0, 0, dragRect.width, dragRect.height));
   1.441 +
   1.442 +    // ...and paint the drag image with translucency
   1.443 +    dt->DrawSurface(aSurface,
   1.444 +                    Rect(0, 0, dragRect.width, dragRect.height),
   1.445 +                    Rect(0, 0, dragRect.width, dragRect.height),
   1.446 +                    DrawSurfaceOptions(),
   1.447 +                    DrawOptions(DRAG_IMAGE_ALPHA_LEVEL, CompositionOp::OP_SOURCE));
   1.448 +
   1.449 +    // The drag transaction addrefs the pixmap, so we can just unref it from us here
   1.450 +    gtk_drag_set_icon_pixmap(aContext, alphaColormap, pixmap, nullptr,
   1.451 +                             aXOffset, aYOffset);
   1.452 +    g_object_unref(pixmap);
   1.453 +    return true;
   1.454 +#else
   1.455 +    // TODO GTK3
   1.456 +    return false;
   1.457 +#endif
   1.458 +}
   1.459 +
   1.460 +NS_IMETHODIMP
   1.461 +nsDragService::StartDragSession()
   1.462 +{
   1.463 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::StartDragSession"));
   1.464 +    return nsBaseDragService::StartDragSession();
   1.465 +}
   1.466 + 
   1.467 +NS_IMETHODIMP
   1.468 +nsDragService::EndDragSession(bool aDoneDrag)
   1.469 +{
   1.470 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::EndDragSession %d",
   1.471 +                                   aDoneDrag));
   1.472 +
   1.473 +    if (sGrabWidget) {
   1.474 +        g_signal_handlers_disconnect_by_func(sGrabWidget,
   1.475 +             FuncToGpointer(OnSourceGrabEventAfter), this);
   1.476 +        g_object_unref(sGrabWidget);
   1.477 +        sGrabWidget = nullptr;
   1.478 +
   1.479 +        if (sMotionEventTimerID) {
   1.480 +            g_source_remove(sMotionEventTimerID);
   1.481 +            sMotionEventTimerID = 0;
   1.482 +        }
   1.483 +        if (sMotionEvent) {
   1.484 +            gdk_event_free(sMotionEvent);
   1.485 +            sMotionEvent = nullptr;
   1.486 +        }
   1.487 +    }
   1.488 +
   1.489 +    // unset our drag action
   1.490 +    SetDragAction(DRAGDROP_ACTION_NONE);
   1.491 +    return nsBaseDragService::EndDragSession(aDoneDrag);
   1.492 +}
   1.493 +
   1.494 +// nsIDragSession
   1.495 +NS_IMETHODIMP
   1.496 +nsDragService::SetCanDrop(bool aCanDrop)
   1.497 +{
   1.498 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::SetCanDrop %d",
   1.499 +                                   aCanDrop));
   1.500 +    mCanDrop = aCanDrop;
   1.501 +    return NS_OK;
   1.502 +}
   1.503 +
   1.504 +NS_IMETHODIMP
   1.505 +nsDragService::GetCanDrop(bool *aCanDrop)
   1.506 +{
   1.507 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::GetCanDrop"));
   1.508 +    *aCanDrop = mCanDrop;
   1.509 +    return NS_OK;
   1.510 +}
   1.511 +
   1.512 +// count the number of URIs in some text/uri-list format data.
   1.513 +static uint32_t
   1.514 +CountTextUriListItems(const char *data,
   1.515 +                      uint32_t datalen)
   1.516 +{
   1.517 +    const char *p = data;
   1.518 +    const char *endPtr = p + datalen;
   1.519 +    uint32_t count = 0;
   1.520 +
   1.521 +    while (p < endPtr) {
   1.522 +        // skip whitespace (if any)
   1.523 +        while (p < endPtr && *p != '\0' && isspace(*p))
   1.524 +            p++;
   1.525 +        // if we aren't at the end of the line ...
   1.526 +        if (p != endPtr && *p != '\0' && *p != '\n' && *p != '\r')
   1.527 +            count++;
   1.528 +        // skip to the end of the line
   1.529 +        while (p < endPtr && *p != '\0' && *p != '\n')
   1.530 +            p++;
   1.531 +        p++; // skip the actual newline as well.
   1.532 +    }
   1.533 +    return count;
   1.534 +}
   1.535 +
   1.536 +// extract an item from text/uri-list formatted data and convert it to
   1.537 +// unicode.
   1.538 +static void
   1.539 +GetTextUriListItem(const char *data,
   1.540 +                   uint32_t datalen,
   1.541 +                   uint32_t aItemIndex,
   1.542 +                   char16_t **convertedText,
   1.543 +                   int32_t *convertedTextLen)
   1.544 +{
   1.545 +    const char *p = data;
   1.546 +    const char *endPtr = p + datalen;
   1.547 +    unsigned int count = 0;
   1.548 +
   1.549 +    *convertedText = nullptr;
   1.550 +    while (p < endPtr) {
   1.551 +        // skip whitespace (if any)
   1.552 +        while (p < endPtr && *p != '\0' && isspace(*p))
   1.553 +            p++;
   1.554 +        // if we aren't at the end of the line, we have a url
   1.555 +        if (p != endPtr && *p != '\0' && *p != '\n' && *p != '\r')
   1.556 +            count++;
   1.557 +        // this is the item we are after ...
   1.558 +        if (aItemIndex + 1 == count) {
   1.559 +            const char *q = p;
   1.560 +            while (q < endPtr && *q != '\0' && *q != '\n' && *q != '\r')
   1.561 +              q++;
   1.562 +            nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode(
   1.563 +                                p, q - p, convertedText, convertedTextLen);
   1.564 +            break;
   1.565 +        }
   1.566 +        // skip to the end of the line
   1.567 +        while (p < endPtr && *p != '\0' && *p != '\n')
   1.568 +            p++;
   1.569 +        p++; // skip the actual newline as well.
   1.570 +    }
   1.571 +
   1.572 +    // didn't find the desired item, so just pass the whole lot
   1.573 +    if (!*convertedText) {
   1.574 +        nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode(
   1.575 +                            data, datalen, convertedText, convertedTextLen);
   1.576 +    }
   1.577 +}
   1.578 +
   1.579 +NS_IMETHODIMP
   1.580 +nsDragService::GetNumDropItems(uint32_t * aNumItems)
   1.581 +{
   1.582 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::GetNumDropItems"));
   1.583 +
   1.584 +    if (!mTargetWidget) {
   1.585 +        PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.586 +               ("*** warning: GetNumDropItems \
   1.587 +               called without a valid target widget!\n"));
   1.588 +        *aNumItems = 0;
   1.589 +        return NS_OK;
   1.590 +    }
   1.591 +
   1.592 +    bool isList = IsTargetContextList();
   1.593 +    if (isList)
   1.594 +        mSourceDataItems->Count(aNumItems);
   1.595 +    else {
   1.596 +        GdkAtom gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
   1.597 +        GetTargetDragData(gdkFlavor);
   1.598 +        if (mTargetDragData) {
   1.599 +            const char *data = reinterpret_cast<char*>(mTargetDragData);
   1.600 +            *aNumItems = CountTextUriListItems(data, mTargetDragDataLen);
   1.601 +        } else
   1.602 +            *aNumItems = 1;
   1.603 +    }
   1.604 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("%d items", *aNumItems));
   1.605 +    return NS_OK;
   1.606 +}
   1.607 +
   1.608 +
   1.609 +NS_IMETHODIMP
   1.610 +nsDragService::GetData(nsITransferable * aTransferable,
   1.611 +                       uint32_t aItemIndex)
   1.612 +{
   1.613 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::GetData %d", aItemIndex));
   1.614 +
   1.615 +    // make sure that we have a transferable
   1.616 +    if (!aTransferable)
   1.617 +        return NS_ERROR_INVALID_ARG;
   1.618 +
   1.619 +    if (!mTargetWidget) {
   1.620 +        PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.621 +               ("*** warning: GetData \
   1.622 +               called without a valid target widget!\n"));
   1.623 +        return NS_ERROR_FAILURE;
   1.624 +    }
   1.625 +
   1.626 +    // get flavor list that includes all acceptable flavors (including
   1.627 +    // ones obtained through conversion). Flavors are nsISupportsStrings
   1.628 +    // so that they can be seen from JS.
   1.629 +    nsCOMPtr<nsISupportsArray> flavorList;
   1.630 +    nsresult rv = aTransferable->FlavorsTransferableCanImport(
   1.631 +                        getter_AddRefs(flavorList));
   1.632 +    if (NS_FAILED(rv))
   1.633 +        return rv;
   1.634 +
   1.635 +    // count the number of flavors
   1.636 +    uint32_t cnt;
   1.637 +    flavorList->Count(&cnt);
   1.638 +    unsigned int i;
   1.639 +
   1.640 +    // check to see if this is an internal list
   1.641 +    bool isList = IsTargetContextList();
   1.642 +
   1.643 +    if (isList) {
   1.644 +        PR_LOG(sDragLm, PR_LOG_DEBUG, ("it's a list..."));
   1.645 +        // find a matching flavor
   1.646 +        for (i = 0; i < cnt; ++i) {
   1.647 +            nsCOMPtr<nsISupports> genericWrapper;
   1.648 +            flavorList->GetElementAt(i, getter_AddRefs(genericWrapper));
   1.649 +            nsCOMPtr<nsISupportsCString> currentFlavor;
   1.650 +            currentFlavor = do_QueryInterface(genericWrapper);
   1.651 +            if (!currentFlavor)
   1.652 +                continue;
   1.653 +
   1.654 +            nsXPIDLCString flavorStr;
   1.655 +            currentFlavor->ToString(getter_Copies(flavorStr));
   1.656 +            PR_LOG(sDragLm,
   1.657 +                   PR_LOG_DEBUG,
   1.658 +                   ("flavor is %s\n", (const char *)flavorStr));
   1.659 +            // get the item with the right index
   1.660 +            nsCOMPtr<nsISupports> genericItem;
   1.661 +            mSourceDataItems->GetElementAt(aItemIndex,
   1.662 +                                           getter_AddRefs(genericItem));
   1.663 +            nsCOMPtr<nsITransferable> item(do_QueryInterface(genericItem));
   1.664 +            if (!item)
   1.665 +                continue;
   1.666 +
   1.667 +            nsCOMPtr<nsISupports> data;
   1.668 +            uint32_t tmpDataLen = 0;
   1.669 +            PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.670 +                   ("trying to get transfer data for %s\n",
   1.671 +                   (const char *)flavorStr));
   1.672 +            rv = item->GetTransferData(flavorStr,
   1.673 +                                       getter_AddRefs(data),
   1.674 +                                       &tmpDataLen);
   1.675 +            if (NS_FAILED(rv)) {
   1.676 +                PR_LOG(sDragLm, PR_LOG_DEBUG, ("failed.\n"));
   1.677 +                continue;
   1.678 +            }
   1.679 +            PR_LOG(sDragLm, PR_LOG_DEBUG, ("succeeded.\n"));
   1.680 +            rv = aTransferable->SetTransferData(flavorStr,data,tmpDataLen);
   1.681 +            if (NS_FAILED(rv)) {
   1.682 +                PR_LOG(sDragLm,
   1.683 +                       PR_LOG_DEBUG,
   1.684 +                       ("fail to set transfer data into transferable!\n"));
   1.685 +                continue;
   1.686 +            }
   1.687 +            // ok, we got the data
   1.688 +            return NS_OK;
   1.689 +        }
   1.690 +        // if we got this far, we failed
   1.691 +        return NS_ERROR_FAILURE;
   1.692 +    }
   1.693 +
   1.694 +    // Now walk down the list of flavors. When we find one that is
   1.695 +    // actually present, copy out the data into the transferable in that
   1.696 +    // format. SetTransferData() implicitly handles conversions.
   1.697 +    for ( i = 0; i < cnt; ++i ) {
   1.698 +        nsCOMPtr<nsISupports> genericWrapper;
   1.699 +        flavorList->GetElementAt(i,getter_AddRefs(genericWrapper));
   1.700 +        nsCOMPtr<nsISupportsCString> currentFlavor;
   1.701 +        currentFlavor = do_QueryInterface(genericWrapper);
   1.702 +        if (currentFlavor) {
   1.703 +            // find our gtk flavor
   1.704 +            nsXPIDLCString flavorStr;
   1.705 +            currentFlavor->ToString(getter_Copies(flavorStr));
   1.706 +            GdkAtom gdkFlavor = gdk_atom_intern(flavorStr, FALSE);
   1.707 +            PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.708 +                   ("looking for data in type %s, gdk flavor %ld\n",
   1.709 +                   static_cast<const char*>(flavorStr), gdkFlavor));
   1.710 +            bool dataFound = false;
   1.711 +            if (gdkFlavor) {
   1.712 +                GetTargetDragData(gdkFlavor);
   1.713 +            }
   1.714 +            if (mTargetDragData) {
   1.715 +                PR_LOG(sDragLm, PR_LOG_DEBUG, ("dataFound = true\n"));
   1.716 +                dataFound = true;
   1.717 +            }
   1.718 +            else {
   1.719 +                PR_LOG(sDragLm, PR_LOG_DEBUG, ("dataFound = false\n"));
   1.720 +
   1.721 +                // Dragging and dropping from the file manager would cause us 
   1.722 +                // to parse the source text as a nsIFile URL.
   1.723 +                if ( strcmp(flavorStr, kFileMime) == 0 ) {
   1.724 +                    gdkFlavor = gdk_atom_intern(kTextMime, FALSE);
   1.725 +                    GetTargetDragData(gdkFlavor);
   1.726 +                    if (!mTargetDragData) {
   1.727 +                        gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
   1.728 +                        GetTargetDragData(gdkFlavor);
   1.729 +                    }
   1.730 +                    if (mTargetDragData) {
   1.731 +                        const char* text = static_cast<char*>(mTargetDragData);
   1.732 +                        char16_t* convertedText = nullptr;
   1.733 +                        int32_t convertedTextLen = 0;
   1.734 +
   1.735 +                        GetTextUriListItem(text, mTargetDragDataLen, aItemIndex,
   1.736 +                                           &convertedText, &convertedTextLen);
   1.737 +
   1.738 +                        if (convertedText) {
   1.739 +                            nsCOMPtr<nsIIOService> ioService = do_GetIOService(&rv);
   1.740 +                            nsCOMPtr<nsIURI> fileURI;
   1.741 +                            nsresult rv = ioService->NewURI(NS_ConvertUTF16toUTF8(convertedText),
   1.742 +                                                            nullptr, nullptr, getter_AddRefs(fileURI));
   1.743 +                            if (NS_SUCCEEDED(rv)) {
   1.744 +                                nsCOMPtr<nsIFileURL> fileURL = do_QueryInterface(fileURI, &rv);
   1.745 +                                if (NS_SUCCEEDED(rv)) {
   1.746 +                                    nsCOMPtr<nsIFile> file;
   1.747 +                                    rv = fileURL->GetFile(getter_AddRefs(file));
   1.748 +                                    if (NS_SUCCEEDED(rv)) {
   1.749 +                                        // The common wrapping code at the end of 
   1.750 +                                        // this function assumes the data is text
   1.751 +                                        // and calls text-specific operations.
   1.752 +                                        // Make a secret hideout here for nsIFile
   1.753 +                                        // objects and return early.
   1.754 +                                        aTransferable->SetTransferData(flavorStr, file,
   1.755 +                                                                       convertedTextLen);
   1.756 +                                        g_free(convertedText);
   1.757 +                                        return NS_OK;
   1.758 +                                    }
   1.759 +                                }
   1.760 +                            }
   1.761 +                            g_free(convertedText);
   1.762 +                        }
   1.763 +                        continue;
   1.764 +                    }
   1.765 +                }
   1.766 +
   1.767 +                // if we are looking for text/unicode and we fail to find it
   1.768 +                // on the clipboard first, try again with text/plain. If that
   1.769 +                // is present, convert it to unicode.
   1.770 +                if ( strcmp(flavorStr, kUnicodeMime) == 0 ) {
   1.771 +                    PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.772 +                           ("we were looking for text/unicode... \
   1.773 +                           trying with text/plain;charset=utf-8\n"));
   1.774 +                    gdkFlavor = gdk_atom_intern(gTextPlainUTF8Type, FALSE);
   1.775 +                    GetTargetDragData(gdkFlavor);
   1.776 +                    if (mTargetDragData) {
   1.777 +                        PR_LOG(sDragLm, PR_LOG_DEBUG, ("Got textplain data\n"));
   1.778 +                        const char* castedText =
   1.779 +                                    reinterpret_cast<char*>(mTargetDragData);
   1.780 +                        char16_t* convertedText = nullptr;
   1.781 +                        NS_ConvertUTF8toUTF16 ucs2string(castedText,
   1.782 +                                                         mTargetDragDataLen);
   1.783 +                        convertedText = ToNewUnicode(ucs2string);
   1.784 +                        if ( convertedText ) {
   1.785 +                            PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.786 +                                   ("successfully converted plain text \
   1.787 +                                   to unicode.\n"));
   1.788 +                            // out with the old, in with the new
   1.789 +                            g_free(mTargetDragData);
   1.790 +                            mTargetDragData = convertedText;
   1.791 +                            mTargetDragDataLen = ucs2string.Length() * 2;
   1.792 +                            dataFound = true;
   1.793 +                        } // if plain text data on clipboard
   1.794 +                    } else {
   1.795 +                        PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.796 +                               ("we were looking for text/unicode... \
   1.797 +                               trying again with text/plain\n"));
   1.798 +                        gdkFlavor = gdk_atom_intern(kTextMime, FALSE);
   1.799 +                        GetTargetDragData(gdkFlavor);
   1.800 +                        if (mTargetDragData) {
   1.801 +                            PR_LOG(sDragLm, PR_LOG_DEBUG, ("Got textplain data\n"));
   1.802 +                            const char* castedText =
   1.803 +                                        reinterpret_cast<char*>(mTargetDragData);
   1.804 +                            char16_t* convertedText = nullptr;
   1.805 +                            int32_t convertedTextLen = 0;
   1.806 +                            nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode(
   1.807 +                                                castedText, mTargetDragDataLen,
   1.808 +                                                &convertedText, &convertedTextLen);
   1.809 +                            if ( convertedText ) {
   1.810 +                                PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.811 +                                       ("successfully converted plain text \
   1.812 +                                       to unicode.\n"));
   1.813 +                                // out with the old, in with the new
   1.814 +                                g_free(mTargetDragData);
   1.815 +                                mTargetDragData = convertedText;
   1.816 +                                mTargetDragDataLen = convertedTextLen * 2;
   1.817 +                                dataFound = true;
   1.818 +                            } // if plain text data on clipboard
   1.819 +                        } // if plain text flavor present
   1.820 +                    } // if plain text charset=utf-8 flavor present
   1.821 +                } // if looking for text/unicode
   1.822 +
   1.823 +                // if we are looking for text/x-moz-url and we failed to find
   1.824 +                // it on the clipboard, try again with text/uri-list, and then
   1.825 +                // _NETSCAPE_URL
   1.826 +                if (strcmp(flavorStr, kURLMime) == 0) {
   1.827 +                    PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.828 +                           ("we were looking for text/x-moz-url...\
   1.829 +                           trying again with text/uri-list\n"));
   1.830 +                    gdkFlavor = gdk_atom_intern(gTextUriListType, FALSE);
   1.831 +                    GetTargetDragData(gdkFlavor);
   1.832 +                    if (mTargetDragData) {
   1.833 +                        PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.834 +                               ("Got text/uri-list data\n"));
   1.835 +                        const char *data =
   1.836 +                                   reinterpret_cast<char*>(mTargetDragData);
   1.837 +                        char16_t* convertedText = nullptr;
   1.838 +                        int32_t convertedTextLen = 0;
   1.839 +
   1.840 +                        GetTextUriListItem(data, mTargetDragDataLen, aItemIndex,
   1.841 +                                           &convertedText, &convertedTextLen);
   1.842 +
   1.843 +                        if ( convertedText ) {
   1.844 +                            PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.845 +                                   ("successfully converted \
   1.846 +                                   _NETSCAPE_URL to unicode.\n"));
   1.847 +                            // out with the old, in with the new
   1.848 +                            g_free(mTargetDragData);
   1.849 +                            mTargetDragData = convertedText;
   1.850 +                            mTargetDragDataLen = convertedTextLen * 2;
   1.851 +                            dataFound = true;
   1.852 +                        }
   1.853 +                    }
   1.854 +                    else {
   1.855 +                        PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.856 +                               ("failed to get text/uri-list data\n"));
   1.857 +                    }
   1.858 +                    if (!dataFound) {
   1.859 +                        PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.860 +                               ("we were looking for text/x-moz-url...\
   1.861 +                               trying again with _NETSCAP_URL\n"));
   1.862 +                        gdkFlavor = gdk_atom_intern(gMozUrlType, FALSE);
   1.863 +                        GetTargetDragData(gdkFlavor);
   1.864 +                        if (mTargetDragData) {
   1.865 +                            PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.866 +                                   ("Got _NETSCAPE_URL data\n"));
   1.867 +                            const char* castedText =
   1.868 +                                  reinterpret_cast<char*>(mTargetDragData);
   1.869 +                            char16_t* convertedText = nullptr;
   1.870 +                            int32_t convertedTextLen = 0;
   1.871 +                            nsPrimitiveHelpers::ConvertPlatformPlainTextToUnicode(castedText, mTargetDragDataLen, &convertedText, &convertedTextLen);
   1.872 +                            if ( convertedText ) {
   1.873 +                                PR_LOG(sDragLm,
   1.874 +                                       PR_LOG_DEBUG,
   1.875 +                                       ("successfully converted _NETSCAPE_URL \
   1.876 +                                       to unicode.\n"));
   1.877 +                                // out with the old, in with the new
   1.878 +                                g_free(mTargetDragData);
   1.879 +                                mTargetDragData = convertedText;
   1.880 +                                mTargetDragDataLen = convertedTextLen * 2;
   1.881 +                                dataFound = true;
   1.882 +                            }
   1.883 +                        }
   1.884 +                        else {
   1.885 +                            PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.886 +                                   ("failed to get _NETSCAPE_URL data\n"));
   1.887 +                        }
   1.888 +                    }
   1.889 +                }
   1.890 +
   1.891 +            } // else we try one last ditch effort to find our data
   1.892 +
   1.893 +            if (dataFound) {
   1.894 +                // the DOM only wants LF, so convert from MacOS line endings
   1.895 +                // to DOM line endings.
   1.896 +                nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(
   1.897 +                             flavorStr,
   1.898 +                             &mTargetDragData,
   1.899 +                             reinterpret_cast<int*>(&mTargetDragDataLen));
   1.900 +        
   1.901 +                // put it into the transferable.
   1.902 +                nsCOMPtr<nsISupports> genericDataWrapper;
   1.903 +                nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr,
   1.904 +                                    mTargetDragData, mTargetDragDataLen,
   1.905 +                                    getter_AddRefs(genericDataWrapper));
   1.906 +                aTransferable->SetTransferData(flavorStr,
   1.907 +                                               genericDataWrapper,
   1.908 +                                               mTargetDragDataLen);
   1.909 +                // we found one, get out of this loop!
   1.910 +                PR_LOG(sDragLm, PR_LOG_DEBUG, ("dataFound and converted!\n"));
   1.911 +                break;
   1.912 +            }
   1.913 +        } // if (currentFlavor)
   1.914 +    } // foreach flavor
   1.915 +
   1.916 +    return NS_OK;
   1.917 +  
   1.918 +}
   1.919 +
   1.920 +NS_IMETHODIMP
   1.921 +nsDragService::IsDataFlavorSupported(const char *aDataFlavor,
   1.922 +                                     bool *_retval)
   1.923 +{
   1.924 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::IsDataFlavorSupported %s",
   1.925 +                                   aDataFlavor));
   1.926 +    if (!_retval)
   1.927 +        return NS_ERROR_INVALID_ARG;
   1.928 +
   1.929 +    // set this to no by default
   1.930 +    *_retval = false;
   1.931 +
   1.932 +    // check to make sure that we have a drag object set, here
   1.933 +    if (!mTargetWidget) {
   1.934 +        PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.935 +               ("*** warning: IsDataFlavorSupported \
   1.936 +               called without a valid target widget!\n"));
   1.937 +        return NS_OK;
   1.938 +    }
   1.939 +
   1.940 +    // check to see if the target context is a list.
   1.941 +    bool isList = IsTargetContextList();
   1.942 +    // if it is, just look in the internal data since we are the source
   1.943 +    // for it.
   1.944 +    if (isList) {
   1.945 +        PR_LOG(sDragLm, PR_LOG_DEBUG, ("It's a list.."));
   1.946 +        uint32_t numDragItems = 0;
   1.947 +        // if we don't have mDataItems we didn't start this drag so it's
   1.948 +        // an external client trying to fool us.
   1.949 +        if (!mSourceDataItems)
   1.950 +            return NS_OK;
   1.951 +        mSourceDataItems->Count(&numDragItems);
   1.952 +        for (uint32_t itemIndex = 0; itemIndex < numDragItems; ++itemIndex) {
   1.953 +            nsCOMPtr<nsISupports> genericItem;
   1.954 +            mSourceDataItems->GetElementAt(itemIndex,
   1.955 +                                           getter_AddRefs(genericItem));
   1.956 +            nsCOMPtr<nsITransferable> currItem(do_QueryInterface(genericItem));
   1.957 +            if (currItem) {
   1.958 +                nsCOMPtr <nsISupportsArray> flavorList;
   1.959 +                currItem->FlavorsTransferableCanExport(
   1.960 +                          getter_AddRefs(flavorList));
   1.961 +                if (flavorList) {
   1.962 +                    uint32_t numFlavors;
   1.963 +                    flavorList->Count( &numFlavors );
   1.964 +                    for ( uint32_t flavorIndex = 0;
   1.965 +                          flavorIndex < numFlavors ;
   1.966 +                          ++flavorIndex ) {
   1.967 +                        nsCOMPtr<nsISupports> genericWrapper;
   1.968 +                        flavorList->GetElementAt(flavorIndex,
   1.969 +                                                getter_AddRefs(genericWrapper));
   1.970 +                        nsCOMPtr<nsISupportsCString> currentFlavor;
   1.971 +                        currentFlavor = do_QueryInterface(genericWrapper);
   1.972 +                        if (currentFlavor) {
   1.973 +                            nsXPIDLCString flavorStr;
   1.974 +                            currentFlavor->ToString(getter_Copies(flavorStr));
   1.975 +                            PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.976 +                                   ("checking %s against %s\n",
   1.977 +                                   (const char *)flavorStr, aDataFlavor));
   1.978 +                            if (strcmp(flavorStr, aDataFlavor) == 0) {
   1.979 +                                PR_LOG(sDragLm, PR_LOG_DEBUG,
   1.980 +                                       ("boioioioiooioioioing!\n"));
   1.981 +                                *_retval = true;
   1.982 +                            }
   1.983 +                        }
   1.984 +                    }
   1.985 +                }
   1.986 +            }
   1.987 +        }
   1.988 +        return NS_OK;
   1.989 +    }
   1.990 +
   1.991 +    // check the target context vs. this flavor, one at a time
   1.992 +    GList *tmp;
   1.993 +    for (tmp = gdk_drag_context_list_targets(mTargetDragContext); 
   1.994 +         tmp; tmp = tmp->next) {
   1.995 +        /* Bug 331198 */
   1.996 +        GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data);
   1.997 +        gchar *name = nullptr;
   1.998 +        name = gdk_atom_name(atom);
   1.999 +        PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1000 +               ("checking %s against %s\n", name, aDataFlavor));
  1.1001 +        if (name && (strcmp(name, aDataFlavor) == 0)) {
  1.1002 +            PR_LOG(sDragLm, PR_LOG_DEBUG, ("good!\n"));
  1.1003 +            *_retval = true;
  1.1004 +        }
  1.1005 +        // check for automatic text/uri-list -> text/x-moz-url mapping
  1.1006 +        if (!*_retval && 
  1.1007 +            name &&
  1.1008 +            (strcmp(name, gTextUriListType) == 0) &&
  1.1009 +            (strcmp(aDataFlavor, kURLMime) == 0 ||
  1.1010 +             strcmp(aDataFlavor, kFileMime) == 0)) {
  1.1011 +            PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1012 +                   ("good! ( it's text/uri-list and \
  1.1013 +                   we're checking against text/x-moz-url )\n"));
  1.1014 +            *_retval = true;
  1.1015 +        }
  1.1016 +        // check for automatic _NETSCAPE_URL -> text/x-moz-url mapping
  1.1017 +        if (!*_retval && 
  1.1018 +            name &&
  1.1019 +            (strcmp(name, gMozUrlType) == 0) &&
  1.1020 +            (strcmp(aDataFlavor, kURLMime) == 0)) {
  1.1021 +            PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1022 +                   ("good! ( it's _NETSCAPE_URL and \
  1.1023 +                   we're checking against text/x-moz-url )\n"));
  1.1024 +            *_retval = true;
  1.1025 +        }
  1.1026 +        // check for auto text/plain -> text/unicode mapping
  1.1027 +        if (!*_retval && 
  1.1028 +            name &&
  1.1029 +            (strcmp(name, kTextMime) == 0) &&
  1.1030 +            ((strcmp(aDataFlavor, kUnicodeMime) == 0) ||
  1.1031 +             (strcmp(aDataFlavor, kFileMime) == 0))) {
  1.1032 +            PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1033 +                   ("good! ( it's text plain and we're checking \
  1.1034 +                   against text/unicode or application/x-moz-file)\n"));
  1.1035 +            *_retval = true;
  1.1036 +        }
  1.1037 +        g_free(name);
  1.1038 +    }
  1.1039 +    return NS_OK;
  1.1040 +}
  1.1041 +
  1.1042 +void
  1.1043 +nsDragService::ReplyToDragMotion()
  1.1044 +{
  1.1045 +    PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1046 +           ("nsDragService::ReplyToDragMotion %d", mCanDrop));
  1.1047 +
  1.1048 +    GdkDragAction action = (GdkDragAction)0;
  1.1049 +    if (mCanDrop) {
  1.1050 +        // notify the dragger if we can drop
  1.1051 +        switch (mDragAction) {
  1.1052 +        case DRAGDROP_ACTION_COPY:
  1.1053 +          action = GDK_ACTION_COPY;
  1.1054 +          break;
  1.1055 +        case DRAGDROP_ACTION_LINK:
  1.1056 +          action = GDK_ACTION_LINK;
  1.1057 +          break;
  1.1058 +        default:
  1.1059 +          action = GDK_ACTION_MOVE;
  1.1060 +          break;
  1.1061 +        }
  1.1062 +    }
  1.1063 +
  1.1064 +    gdk_drag_status(mTargetDragContext, action, mTargetTime);
  1.1065 +}
  1.1066 +
  1.1067 +void
  1.1068 +nsDragService::TargetDataReceived(GtkWidget         *aWidget,
  1.1069 +                                  GdkDragContext    *aContext,
  1.1070 +                                  gint               aX,
  1.1071 +                                  gint               aY,
  1.1072 +                                  GtkSelectionData  *aSelectionData,
  1.1073 +                                  guint              aInfo,
  1.1074 +                                  guint32            aTime)
  1.1075 +{
  1.1076 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::TargetDataReceived"));
  1.1077 +    TargetResetData();
  1.1078 +    mTargetDragDataReceived = true;
  1.1079 +    gint len = gtk_selection_data_get_length(aSelectionData);
  1.1080 +    const guchar* data = gtk_selection_data_get_data(aSelectionData);
  1.1081 +    if (len > 0 && data) {
  1.1082 +        mTargetDragDataLen = len;
  1.1083 +        mTargetDragData = g_malloc(mTargetDragDataLen);
  1.1084 +        memcpy(mTargetDragData, data, mTargetDragDataLen);
  1.1085 +    }
  1.1086 +    else {
  1.1087 +        PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1088 +               ("Failed to get data.  selection data len was %d\n",
  1.1089 +                mTargetDragDataLen));
  1.1090 +    }
  1.1091 +}
  1.1092 +
  1.1093 +bool
  1.1094 +nsDragService::IsTargetContextList(void)
  1.1095 +{
  1.1096 +    bool retval = false;
  1.1097 +
  1.1098 +    // gMimeListType drags only work for drags within a single process. The
  1.1099 +    // gtk_drag_get_source_widget() function will return nullptr if the source
  1.1100 +    // of the drag is another app, so we use it to check if a gMimeListType
  1.1101 +    // drop will work or not.
  1.1102 +    if (gtk_drag_get_source_widget(mTargetDragContext) == nullptr)
  1.1103 +        return retval;
  1.1104 +
  1.1105 +    GList *tmp;
  1.1106 +
  1.1107 +    // walk the list of context targets and see if one of them is a list
  1.1108 +    // of items.
  1.1109 +    for (tmp = gdk_drag_context_list_targets(mTargetDragContext); 
  1.1110 +         tmp; tmp = tmp->next) {
  1.1111 +        /* Bug 331198 */
  1.1112 +        GdkAtom atom = GDK_POINTER_TO_ATOM(tmp->data);
  1.1113 +        gchar *name = nullptr;
  1.1114 +        name = gdk_atom_name(atom);
  1.1115 +        if (name && strcmp(name, gMimeListType) == 0)
  1.1116 +            retval = true;
  1.1117 +        g_free(name);
  1.1118 +        if (retval)
  1.1119 +            break;
  1.1120 +    }
  1.1121 +    return retval;
  1.1122 +}
  1.1123 +
  1.1124 +// Maximum time to wait for a "drag_received" arrived, in microseconds
  1.1125 +#define NS_DND_TIMEOUT 500000
  1.1126 +
  1.1127 +void
  1.1128 +nsDragService::GetTargetDragData(GdkAtom aFlavor)
  1.1129 +{
  1.1130 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("getting data flavor %d\n", aFlavor));
  1.1131 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("mLastWidget is %p and mLastContext is %p\n",
  1.1132 +                                   mTargetWidget.get(),
  1.1133 +                                   mTargetDragContext.get()));
  1.1134 +    // reset our target data areas
  1.1135 +    TargetResetData();
  1.1136 +    gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
  1.1137 +    
  1.1138 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("about to start inner iteration."));
  1.1139 +    PRTime entryTime = PR_Now();
  1.1140 +    while (!mTargetDragDataReceived && mDoingDrag) {
  1.1141 +        // check the number of iterations
  1.1142 +        PR_LOG(sDragLm, PR_LOG_DEBUG, ("doing iteration...\n"));
  1.1143 +        PR_Sleep(20*PR_TicksPerSecond()/1000);  /* sleep for 20 ms/iteration */
  1.1144 +        if (PR_Now()-entryTime > NS_DND_TIMEOUT) break;
  1.1145 +        gtk_main_iteration();
  1.1146 +    }
  1.1147 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("finished inner iteration\n"));
  1.1148 +}
  1.1149 +
  1.1150 +void
  1.1151 +nsDragService::TargetResetData(void)
  1.1152 +{
  1.1153 +    mTargetDragDataReceived = false;
  1.1154 +    // make sure to free old data if we have to
  1.1155 +    g_free(mTargetDragData);
  1.1156 +    mTargetDragData = 0;
  1.1157 +    mTargetDragDataLen = 0;
  1.1158 +}
  1.1159 +
  1.1160 +GtkTargetList *
  1.1161 +nsDragService::GetSourceList(void)
  1.1162 +{
  1.1163 +    if (!mSourceDataItems)
  1.1164 +        return nullptr;
  1.1165 +    nsTArray<GtkTargetEntry*> targetArray;
  1.1166 +    GtkTargetEntry *targets;
  1.1167 +    GtkTargetList  *targetList = 0;
  1.1168 +    uint32_t targetCount = 0;
  1.1169 +    unsigned int numDragItems = 0;
  1.1170 +
  1.1171 +    mSourceDataItems->Count(&numDragItems);
  1.1172 +
  1.1173 +    // Check to see if we're dragging > 1 item.
  1.1174 +    if (numDragItems > 1) {
  1.1175 +        // as the Xdnd protocol only supports a single item (or is it just
  1.1176 +        // gtk's implementation?), we don't advertise all flavours listed
  1.1177 +        // in the nsITransferable.
  1.1178 +
  1.1179 +        // the application/x-moz-internal-item-list format, which preserves
  1.1180 +        // all information for drags within the same mozilla instance.
  1.1181 +        GtkTargetEntry *listTarget =
  1.1182 +            (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
  1.1183 +        listTarget->target = g_strdup(gMimeListType);
  1.1184 +        listTarget->flags = 0;
  1.1185 +        PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1186 +               ("automatically adding target %s\n", listTarget->target));
  1.1187 +        targetArray.AppendElement(listTarget);
  1.1188 +
  1.1189 +        // check what flavours are supported so we can decide what other
  1.1190 +        // targets to advertise.
  1.1191 +        nsCOMPtr<nsISupports> genericItem;
  1.1192 +        mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem));
  1.1193 +        nsCOMPtr<nsITransferable> currItem(do_QueryInterface(genericItem));
  1.1194 +
  1.1195 +        if (currItem) {
  1.1196 +            nsCOMPtr <nsISupportsArray> flavorList;
  1.1197 +            currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
  1.1198 +            if (flavorList) {
  1.1199 +                uint32_t numFlavors;
  1.1200 +                flavorList->Count( &numFlavors );
  1.1201 +                for (uint32_t flavorIndex = 0;
  1.1202 +                     flavorIndex < numFlavors ;
  1.1203 +                     ++flavorIndex ) {
  1.1204 +                    nsCOMPtr<nsISupports> genericWrapper;
  1.1205 +                    flavorList->GetElementAt(flavorIndex,
  1.1206 +                                           getter_AddRefs(genericWrapper));
  1.1207 +                    nsCOMPtr<nsISupportsCString> currentFlavor;
  1.1208 +                    currentFlavor = do_QueryInterface(genericWrapper);
  1.1209 +                    if (currentFlavor) {
  1.1210 +                        nsXPIDLCString flavorStr;
  1.1211 +                        currentFlavor->ToString(getter_Copies(flavorStr));
  1.1212 +
  1.1213 +                        // check if text/x-moz-url is supported.
  1.1214 +                        // If so, advertise
  1.1215 +                        // text/uri-list.
  1.1216 +                        if (strcmp(flavorStr, kURLMime) == 0) {
  1.1217 +                            listTarget =
  1.1218 +                             (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
  1.1219 +                            listTarget->target = g_strdup(gTextUriListType);
  1.1220 +                            listTarget->flags = 0;
  1.1221 +                            PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1222 +                                   ("automatically adding target %s\n",
  1.1223 +                                    listTarget->target));
  1.1224 +                            targetArray.AppendElement(listTarget);
  1.1225 +                        }
  1.1226 +                    }
  1.1227 +                } // foreach flavor in item
  1.1228 +            } // if valid flavor list
  1.1229 +        } // if item is a transferable
  1.1230 +    } else if (numDragItems == 1) {
  1.1231 +        nsCOMPtr<nsISupports> genericItem;
  1.1232 +        mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem));
  1.1233 +        nsCOMPtr<nsITransferable> currItem(do_QueryInterface(genericItem));
  1.1234 +        if (currItem) {
  1.1235 +            nsCOMPtr <nsISupportsArray> flavorList;
  1.1236 +            currItem->FlavorsTransferableCanExport(getter_AddRefs(flavorList));
  1.1237 +            if (flavorList) {
  1.1238 +                uint32_t numFlavors;
  1.1239 +                flavorList->Count( &numFlavors );
  1.1240 +                for (uint32_t flavorIndex = 0;
  1.1241 +                     flavorIndex < numFlavors ;
  1.1242 +                     ++flavorIndex ) {
  1.1243 +                    nsCOMPtr<nsISupports> genericWrapper;
  1.1244 +                    flavorList->GetElementAt(flavorIndex,
  1.1245 +                                             getter_AddRefs(genericWrapper));
  1.1246 +                    nsCOMPtr<nsISupportsCString> currentFlavor;
  1.1247 +                    currentFlavor = do_QueryInterface(genericWrapper);
  1.1248 +                    if (currentFlavor) {
  1.1249 +                        nsXPIDLCString flavorStr;
  1.1250 +                        currentFlavor->ToString(getter_Copies(flavorStr));
  1.1251 +                        GtkTargetEntry *target =
  1.1252 +                          (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
  1.1253 +                        target->target = g_strdup(flavorStr);
  1.1254 +                        target->flags = 0;
  1.1255 +                        PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1256 +                               ("adding target %s\n", target->target));
  1.1257 +                        targetArray.AppendElement(target);
  1.1258 +                        // Check to see if this is text/unicode.
  1.1259 +                        // If it is, add text/plain
  1.1260 +                        // since we automatically support text/plain
  1.1261 +                        // if we support text/unicode.
  1.1262 +                        if (strcmp(flavorStr, kUnicodeMime) == 0) {
  1.1263 +                            GtkTargetEntry *plainUTF8Target =
  1.1264 +                             (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
  1.1265 +                            plainUTF8Target->target = g_strdup(gTextPlainUTF8Type);
  1.1266 +                            plainUTF8Target->flags = 0;
  1.1267 +                            PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1268 +                                   ("automatically adding target %s\n",
  1.1269 +                                    plainUTF8Target->target));
  1.1270 +                            targetArray.AppendElement(plainUTF8Target);
  1.1271 +
  1.1272 +                            GtkTargetEntry *plainTarget =
  1.1273 +                             (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
  1.1274 +                            plainTarget->target = g_strdup(kTextMime);
  1.1275 +                            plainTarget->flags = 0;
  1.1276 +                            PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1277 +                                   ("automatically adding target %s\n",
  1.1278 +                                    plainTarget->target));
  1.1279 +                            targetArray.AppendElement(plainTarget);
  1.1280 +                        }
  1.1281 +                        // Check to see if this is the x-moz-url type.
  1.1282 +                        // If it is, add _NETSCAPE_URL
  1.1283 +                        // this is a type used by everybody.
  1.1284 +                        if (strcmp(flavorStr, kURLMime) == 0) {
  1.1285 +                            GtkTargetEntry *urlTarget =
  1.1286 +                             (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry));
  1.1287 +                            urlTarget->target = g_strdup(gMozUrlType);
  1.1288 +                            urlTarget->flags = 0;
  1.1289 +                            PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1290 +                                   ("automatically adding target %s\n",
  1.1291 +                                    urlTarget->target));
  1.1292 +                            targetArray.AppendElement(urlTarget);
  1.1293 +                        }
  1.1294 +                    }
  1.1295 +                } // foreach flavor in item
  1.1296 +            } // if valid flavor list
  1.1297 +        } // if item is a transferable
  1.1298 +    } // if it is a single item drag
  1.1299 +
  1.1300 +    // get all the elements that we created.
  1.1301 +    targetCount = targetArray.Length();
  1.1302 +    if (targetCount) {
  1.1303 +        // allocate space to create the list of valid targets
  1.1304 +        targets =
  1.1305 +          (GtkTargetEntry *)g_malloc(sizeof(GtkTargetEntry) * targetCount);
  1.1306 +        uint32_t targetIndex;
  1.1307 +        for ( targetIndex = 0; targetIndex < targetCount; ++targetIndex) {
  1.1308 +            GtkTargetEntry *disEntry = targetArray.ElementAt(targetIndex);
  1.1309 +            // this is a string reference but it will be freed later.
  1.1310 +            targets[targetIndex].target = disEntry->target;
  1.1311 +            targets[targetIndex].flags = disEntry->flags;
  1.1312 +            targets[targetIndex].info = 0;
  1.1313 +        }
  1.1314 +        targetList = gtk_target_list_new(targets, targetCount);
  1.1315 +        // clean up the target list
  1.1316 +        for (uint32_t cleanIndex = 0; cleanIndex < targetCount; ++cleanIndex) {
  1.1317 +            GtkTargetEntry *thisTarget = targetArray.ElementAt(cleanIndex);
  1.1318 +            g_free(thisTarget->target);
  1.1319 +            g_free(thisTarget);
  1.1320 +        }
  1.1321 +        g_free(targets);
  1.1322 +    }
  1.1323 +    return targetList;
  1.1324 +}
  1.1325 +
  1.1326 +void
  1.1327 +nsDragService::SourceEndDragSession(GdkDragContext *aContext,
  1.1328 +                                    gint            aResult)
  1.1329 +{
  1.1330 +    // this just releases the list of data items that we provide
  1.1331 +    mSourceDataItems = nullptr;
  1.1332 +
  1.1333 +    if (!mDoingDrag || mScheduledTask == eDragTaskSourceEnd)
  1.1334 +        // EndDragSession() was already called on drop
  1.1335 +        // or SourceEndDragSession on drag-failed
  1.1336 +        return;
  1.1337 +
  1.1338 +    if (mEndDragPoint.x < 0) {
  1.1339 +        // We don't have a drag end point, so guess
  1.1340 +        gint x, y;
  1.1341 +        GdkDisplay* display = gdk_display_get_default();
  1.1342 +        if (display) {
  1.1343 +            gdk_display_get_pointer(display, nullptr, &x, &y, nullptr);
  1.1344 +            SetDragEndPoint(nsIntPoint(x, y));
  1.1345 +        }
  1.1346 +    }
  1.1347 +
  1.1348 +    // Either the drag was aborted or the drop occurred outside the app.
  1.1349 +    // The dropEffect of mDataTransfer is not updated for motion outside the
  1.1350 +    // app, but is needed for the dragend event, so set it now.
  1.1351 +
  1.1352 +    uint32_t dropEffect;
  1.1353 +
  1.1354 +    if (aResult == MOZ_GTK_DRAG_RESULT_SUCCESS) {
  1.1355 +
  1.1356 +        // With GTK+ versions 2.10.x and prior the drag may have been
  1.1357 +        // cancelled (but no drag-failed signal would have been sent).
  1.1358 +        // aContext->dest_window will be non-nullptr only if the drop was
  1.1359 +        // sent.
  1.1360 +        GdkDragAction action =
  1.1361 +            gdk_drag_context_get_dest_window(aContext) ? 
  1.1362 +                gdk_drag_context_get_actions(aContext) : (GdkDragAction)0;
  1.1363 +
  1.1364 +        // Only one bit of action should be set, but, just in case someone
  1.1365 +        // does something funny, erring away from MOVE, and not recording
  1.1366 +        // unusual action combinations as NONE.
  1.1367 +        if (!action)
  1.1368 +            dropEffect = DRAGDROP_ACTION_NONE;
  1.1369 +        else if (action & GDK_ACTION_COPY)
  1.1370 +            dropEffect = DRAGDROP_ACTION_COPY;
  1.1371 +        else if (action & GDK_ACTION_LINK)
  1.1372 +            dropEffect = DRAGDROP_ACTION_LINK;
  1.1373 +        else if (action & GDK_ACTION_MOVE)
  1.1374 +            dropEffect = DRAGDROP_ACTION_MOVE;
  1.1375 +        else
  1.1376 +            dropEffect = DRAGDROP_ACTION_COPY;
  1.1377 +
  1.1378 +    } else {
  1.1379 +
  1.1380 +        dropEffect = DRAGDROP_ACTION_NONE;
  1.1381 +
  1.1382 +        if (aResult != MOZ_GTK_DRAG_RESULT_NO_TARGET) {
  1.1383 +            mUserCancelled = true;
  1.1384 +        }
  1.1385 +    }
  1.1386 +
  1.1387 +    if (mDataTransfer) {
  1.1388 +        mDataTransfer->SetDropEffectInt(dropEffect);
  1.1389 +    }
  1.1390 +
  1.1391 +    // Schedule the appropriate drag end dom events.
  1.1392 +    Schedule(eDragTaskSourceEnd, nullptr, nullptr, nsIntPoint(), 0);
  1.1393 +}
  1.1394 +
  1.1395 +static void
  1.1396 +CreateUriList(nsISupportsArray *items, gchar **text, gint *length)
  1.1397 +{
  1.1398 +    uint32_t i, count;
  1.1399 +    GString *uriList = g_string_new(nullptr);
  1.1400 +
  1.1401 +    items->Count(&count);
  1.1402 +    for (i = 0; i < count; i++) {
  1.1403 +        nsCOMPtr<nsISupports> genericItem;
  1.1404 +        items->GetElementAt(i, getter_AddRefs(genericItem));
  1.1405 +        nsCOMPtr<nsITransferable> item;
  1.1406 +        item = do_QueryInterface(genericItem);
  1.1407 +
  1.1408 +        if (item) {
  1.1409 +            uint32_t tmpDataLen = 0;
  1.1410 +            void    *tmpData = nullptr;
  1.1411 +            nsresult rv = NS_OK;
  1.1412 +            nsCOMPtr<nsISupports> data;
  1.1413 +            rv = item->GetTransferData(kURLMime,
  1.1414 +                                       getter_AddRefs(data),
  1.1415 +                                       &tmpDataLen);
  1.1416 +
  1.1417 +            if (NS_SUCCEEDED(rv)) {
  1.1418 +                nsPrimitiveHelpers::CreateDataFromPrimitive(kURLMime,
  1.1419 +                                                            data,
  1.1420 +                                                            &tmpData,
  1.1421 +                                                            tmpDataLen);
  1.1422 +                char* plainTextData = nullptr;
  1.1423 +                char16_t* castedUnicode = reinterpret_cast<char16_t*>
  1.1424 +                                                           (tmpData);
  1.1425 +                int32_t plainTextLen = 0;
  1.1426 +                nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText(
  1.1427 +                                    castedUnicode,
  1.1428 +                                    tmpDataLen / 2,
  1.1429 +                                    &plainTextData,
  1.1430 +                                    &plainTextLen);
  1.1431 +                if (plainTextData) {
  1.1432 +                    int32_t j;
  1.1433 +
  1.1434 +                    // text/x-moz-url is of form url + "\n" + title.
  1.1435 +                    // We just want the url.
  1.1436 +                    for (j = 0; j < plainTextLen; j++)
  1.1437 +                        if (plainTextData[j] == '\n' ||
  1.1438 +                            plainTextData[j] == '\r') {
  1.1439 +                            plainTextData[j] = '\0';
  1.1440 +                            break;
  1.1441 +                        }
  1.1442 +                    g_string_append(uriList, plainTextData);
  1.1443 +                    g_string_append(uriList, "\r\n");
  1.1444 +                    // this wasn't allocated with glib
  1.1445 +                    free(plainTextData);
  1.1446 +                }
  1.1447 +                if (tmpData) {
  1.1448 +                    // this wasn't allocated with glib
  1.1449 +                    free(tmpData);
  1.1450 +                }
  1.1451 +            }
  1.1452 +        }
  1.1453 +    }
  1.1454 +    *text = uriList->str;
  1.1455 +    *length = uriList->len + 1;
  1.1456 +    g_string_free(uriList, FALSE); // don't free the data
  1.1457 +}
  1.1458 +
  1.1459 +
  1.1460 +void
  1.1461 +nsDragService::SourceDataGet(GtkWidget        *aWidget,
  1.1462 +                             GdkDragContext   *aContext,
  1.1463 +                             GtkSelectionData *aSelectionData,
  1.1464 +                             guint32           aTime)
  1.1465 +{
  1.1466 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::SourceDataGet"));
  1.1467 +    GdkAtom target = gtk_selection_data_get_target(aSelectionData);
  1.1468 +    nsXPIDLCString mimeFlavor;
  1.1469 +    gchar *typeName = 0;
  1.1470 +    typeName = gdk_atom_name(target);
  1.1471 +    if (!typeName) {
  1.1472 +        PR_LOG(sDragLm, PR_LOG_DEBUG, ("failed to get atom name.\n"));
  1.1473 +        return;
  1.1474 +    }
  1.1475 +
  1.1476 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("Type is %s\n", typeName));
  1.1477 +    // make a copy since |nsXPIDLCString| won't use |g_free|...
  1.1478 +    mimeFlavor.Adopt(strdup(typeName));
  1.1479 +    g_free(typeName);
  1.1480 +    // check to make sure that we have data items to return.
  1.1481 +    if (!mSourceDataItems) {
  1.1482 +        PR_LOG(sDragLm, PR_LOG_DEBUG, ("Failed to get our data items\n"));
  1.1483 +        return;
  1.1484 +    }
  1.1485 +
  1.1486 +    nsCOMPtr<nsISupports> genericItem;
  1.1487 +    mSourceDataItems->GetElementAt(0, getter_AddRefs(genericItem));
  1.1488 +    nsCOMPtr<nsITransferable> item;
  1.1489 +    item = do_QueryInterface(genericItem);
  1.1490 +    if (item) {
  1.1491 +        // if someone was asking for text/plain, lookup unicode instead so
  1.1492 +        // we can convert it.
  1.1493 +        bool needToDoConversionToPlainText = false;
  1.1494 +        const char* actualFlavor = mimeFlavor;
  1.1495 +        if (strcmp(mimeFlavor, kTextMime) == 0 ||
  1.1496 +            strcmp(mimeFlavor, gTextPlainUTF8Type) == 0) {
  1.1497 +            actualFlavor = kUnicodeMime;
  1.1498 +            needToDoConversionToPlainText = true;
  1.1499 +        }
  1.1500 +        // if someone was asking for _NETSCAPE_URL we need to convert to
  1.1501 +        // plain text but we also need to look for x-moz-url
  1.1502 +        else if (strcmp(mimeFlavor, gMozUrlType) == 0) {
  1.1503 +            actualFlavor = kURLMime;
  1.1504 +            needToDoConversionToPlainText = true;
  1.1505 +        }
  1.1506 +        // if someone was asking for text/uri-list we need to convert to
  1.1507 +        // plain text.
  1.1508 +        else if (strcmp(mimeFlavor, gTextUriListType) == 0) {
  1.1509 +            actualFlavor = gTextUriListType;
  1.1510 +            needToDoConversionToPlainText = true;
  1.1511 +        }
  1.1512 +        else
  1.1513 +            actualFlavor = mimeFlavor;
  1.1514 +
  1.1515 +        uint32_t tmpDataLen = 0;
  1.1516 +        void    *tmpData = nullptr;
  1.1517 +        nsresult rv;
  1.1518 +        nsCOMPtr<nsISupports> data;
  1.1519 +        rv = item->GetTransferData(actualFlavor,
  1.1520 +                                   getter_AddRefs(data),
  1.1521 +                                   &tmpDataLen);
  1.1522 +        if (NS_SUCCEEDED(rv)) {
  1.1523 +            nsPrimitiveHelpers::CreateDataFromPrimitive (actualFlavor, data,
  1.1524 +                                                         &tmpData, tmpDataLen);
  1.1525 +            // if required, do the extra work to convert unicode to plain
  1.1526 +            // text and replace the output values with the plain text.
  1.1527 +            if (needToDoConversionToPlainText) {
  1.1528 +                char* plainTextData = nullptr;
  1.1529 +                char16_t* castedUnicode = reinterpret_cast<char16_t*>
  1.1530 +                                                           (tmpData);
  1.1531 +                int32_t plainTextLen = 0;
  1.1532 +                if (strcmp(mimeFlavor, gTextPlainUTF8Type) == 0) {
  1.1533 +                    plainTextData =
  1.1534 +                        ToNewUTF8String(
  1.1535 +                            nsDependentString(castedUnicode, tmpDataLen / 2),
  1.1536 +                            (uint32_t*)&plainTextLen);
  1.1537 +                } else {
  1.1538 +                    nsPrimitiveHelpers::ConvertUnicodeToPlatformPlainText(
  1.1539 +                                        castedUnicode,
  1.1540 +                                        tmpDataLen / 2,
  1.1541 +                                        &plainTextData,
  1.1542 +                                        &plainTextLen);
  1.1543 +                }
  1.1544 +                if (tmpData) {
  1.1545 +                    // this was not allocated using glib
  1.1546 +                    free(tmpData);
  1.1547 +                    tmpData = plainTextData;
  1.1548 +                    tmpDataLen = plainTextLen;
  1.1549 +                }
  1.1550 +            }
  1.1551 +            if (tmpData) {
  1.1552 +                // this copies the data
  1.1553 +                gtk_selection_data_set(aSelectionData, target,
  1.1554 +                                       8,
  1.1555 +                                       (guchar *)tmpData, tmpDataLen);
  1.1556 +                // this wasn't allocated with glib
  1.1557 +                free(tmpData);
  1.1558 +            }
  1.1559 +        } else {
  1.1560 +            if (strcmp(mimeFlavor, gTextUriListType) == 0) {
  1.1561 +                // fall back for text/uri-list
  1.1562 +                gchar *uriList;
  1.1563 +                gint length;
  1.1564 +                CreateUriList(mSourceDataItems, &uriList, &length);
  1.1565 +                gtk_selection_data_set(aSelectionData, target,
  1.1566 +                                       8, (guchar *)uriList, length);
  1.1567 +                g_free(uriList);
  1.1568 +                return;
  1.1569 +            }
  1.1570 +        }
  1.1571 +    }
  1.1572 +}
  1.1573 +
  1.1574 +void nsDragService::SetDragIcon(GdkDragContext* aContext)
  1.1575 +{
  1.1576 +    if (!mHasImage && !mSelection)
  1.1577 +        return;
  1.1578 +
  1.1579 +    nsIntRect dragRect;
  1.1580 +    nsPresContext* pc;
  1.1581 +    RefPtr<SourceSurface> surface;
  1.1582 +    DrawDrag(mSourceNode, mSourceRegion, mScreenX, mScreenY,
  1.1583 +             &dragRect, &surface, &pc);
  1.1584 +    if (!pc)
  1.1585 +        return;
  1.1586 +
  1.1587 +    int32_t sx = mScreenX, sy = mScreenY;
  1.1588 +    ConvertToUnscaledDevPixels(pc, &sx, &sy);
  1.1589 +
  1.1590 +    int32_t offsetX = sx - dragRect.x;
  1.1591 +    int32_t offsetY = sy - dragRect.y;
  1.1592 +
  1.1593 +    // If a popup is set as the drag image, use its widget. Otherwise, use
  1.1594 +    // the surface that DrawDrag created.
  1.1595 +    if (mDragPopup) {
  1.1596 +        GtkWidget* gtkWidget = nullptr;
  1.1597 +        nsIFrame* frame = mDragPopup->GetPrimaryFrame();
  1.1598 +        if (frame) {
  1.1599 +            // DrawDrag ensured that this is a popup frame.
  1.1600 +            nsCOMPtr<nsIWidget> widget = frame->GetNearestWidget();
  1.1601 +            if (widget) {
  1.1602 +                gtkWidget = (GtkWidget *)widget->GetNativeData(NS_NATIVE_SHELLWIDGET);
  1.1603 +                if (gtkWidget) {
  1.1604 +                    OpenDragPopup();
  1.1605 +                    gtk_drag_set_icon_widget(aContext, gtkWidget, offsetX, offsetY);
  1.1606 +                }
  1.1607 +            }
  1.1608 +        }
  1.1609 +    }
  1.1610 +    else if (surface) {
  1.1611 +        if (!SetAlphaPixmap(surface, aContext, offsetX, offsetY, dragRect)) {
  1.1612 +            GdkPixbuf* dragPixbuf =
  1.1613 +              nsImageToPixbuf::SourceSurfaceToPixbuf(surface, dragRect.width, dragRect.height);
  1.1614 +            if (dragPixbuf) {
  1.1615 +                gtk_drag_set_icon_pixbuf(aContext, dragPixbuf, offsetX, offsetY);
  1.1616 +                g_object_unref(dragPixbuf);
  1.1617 +            }
  1.1618 +        }
  1.1619 +    }
  1.1620 +}
  1.1621 +
  1.1622 +static void
  1.1623 +invisibleSourceDragBegin(GtkWidget        *aWidget,
  1.1624 +                         GdkDragContext   *aContext,
  1.1625 +                         gpointer          aData)
  1.1626 +{
  1.1627 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("invisibleSourceDragBegin"));
  1.1628 +    nsDragService *dragService = (nsDragService *)aData;
  1.1629 +
  1.1630 +    dragService->SetDragIcon(aContext);
  1.1631 +}
  1.1632 +
  1.1633 +static void
  1.1634 +invisibleSourceDragDataGet(GtkWidget        *aWidget,
  1.1635 +                           GdkDragContext   *aContext,
  1.1636 +                           GtkSelectionData *aSelectionData,
  1.1637 +                           guint             aInfo,
  1.1638 +                           guint32           aTime,
  1.1639 +                           gpointer          aData)
  1.1640 +{
  1.1641 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("invisibleSourceDragDataGet"));
  1.1642 +    nsDragService *dragService = (nsDragService *)aData;
  1.1643 +    dragService->SourceDataGet(aWidget, aContext,
  1.1644 +                               aSelectionData, aTime);
  1.1645 +}
  1.1646 +
  1.1647 +static gboolean
  1.1648 +invisibleSourceDragFailed(GtkWidget        *aWidget,
  1.1649 +                          GdkDragContext   *aContext,
  1.1650 +                          gint              aResult,
  1.1651 +                          gpointer          aData)
  1.1652 +{
  1.1653 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("invisibleSourceDragFailed %i", aResult));
  1.1654 +    nsDragService *dragService = (nsDragService *)aData;
  1.1655 +    // End the drag session now (rather than waiting for the drag-end signal)
  1.1656 +    // so that operations performed on dropEffect == none can start immediately
  1.1657 +    // rather than waiting for the drag-failed animation to finish.
  1.1658 +    dragService->SourceEndDragSession(aContext, aResult);
  1.1659 +
  1.1660 +    // We should return TRUE to disable the drag-failed animation iff the
  1.1661 +    // source performed an operation when dropEffect was none, but the handler
  1.1662 +    // of the dragend DOM event doesn't provide this information.
  1.1663 +    return FALSE;
  1.1664 +}
  1.1665 +
  1.1666 +static void
  1.1667 +invisibleSourceDragEnd(GtkWidget        *aWidget,
  1.1668 +                       GdkDragContext   *aContext,
  1.1669 +                       gpointer          aData)
  1.1670 +{
  1.1671 +    PR_LOG(sDragLm, PR_LOG_DEBUG, ("invisibleSourceDragEnd"));
  1.1672 +    nsDragService *dragService = (nsDragService *)aData;
  1.1673 +
  1.1674 +    // The drag has ended.  Release the hostages!
  1.1675 +    dragService->SourceEndDragSession(aContext, MOZ_GTK_DRAG_RESULT_SUCCESS);
  1.1676 +}
  1.1677 +
  1.1678 +// The following methods handle responding to GTK drag signals and
  1.1679 +// tracking state between these signals.
  1.1680 +//
  1.1681 +// In general, GTK does not expect us to run the event loop while handling its
  1.1682 +// drag signals, however our drag event handlers may run the
  1.1683 +// event loop, most often to fetch information about the drag data.
  1.1684 +// 
  1.1685 +// GTK, for example, uses the return value from drag-motion signals to
  1.1686 +// determine whether drag-leave signals should be sent.  If an event loop is
  1.1687 +// run during drag-motion the XdndLeave message can get processed but when GTK
  1.1688 +// receives the message it does not yet know that it needs to send the
  1.1689 +// drag-leave signal to our widget.
  1.1690 +//
  1.1691 +// After a drag-drop signal, we need to reply with gtk_drag_finish().
  1.1692 +// However, gtk_drag_finish should happen after the drag-drop signal handler
  1.1693 +// returns so that when the Motif drag protocol is used, the
  1.1694 +// XmTRANSFER_SUCCESS during gtk_drag_finish is sent after the XmDROP_START
  1.1695 +// reply sent on return from the drag-drop signal handler.
  1.1696 +//
  1.1697 +// Similarly drag-end for a successful drag and drag-failed are not good
  1.1698 +// times to run a nested event loop as gtk_drag_drop_finished() and
  1.1699 +// gtk_drag_source_info_destroy() don't gtk_drag_clear_source_info() or remove
  1.1700 +// drop_timeout until after at least the first of these signals is sent.
  1.1701 +// Processing other events (e.g. a slow GDK_DROP_FINISHED reply, or the drop
  1.1702 +// timeout) could cause gtk_drag_drop_finished to be called again with the
  1.1703 +// same GtkDragSourceInfo, which won't like being destroyed twice.
  1.1704 +//
  1.1705 +// Therefore we reply to the signals immediately and schedule a task to
  1.1706 +// dispatch the Gecko events, which may run the event loop.
  1.1707 +//
  1.1708 +// Action in response to drag-leave signals is also delayed until the event
  1.1709 +// loop runs again so that we find out whether a drag-drop signal follows.
  1.1710 +//
  1.1711 +// A single task is scheduled to manage responses to all three GTK signals.
  1.1712 +// If further signals are received while the task is scheduled, the scheduled
  1.1713 +// response is updated, sometimes effectively compressing successive signals.
  1.1714 +//
  1.1715 +// No Gecko drag events are dispatched (during nested event loops) while other
  1.1716 +// Gecko drag events are in flight.  This helps event handlers that may not
  1.1717 +// expect nested events, while accessing an event's dataTransfer for example.
  1.1718 +
  1.1719 +gboolean
  1.1720 +nsDragService::ScheduleMotionEvent(nsWindow *aWindow,
  1.1721 +                                   GdkDragContext *aDragContext,
  1.1722 +                                   nsIntPoint aWindowPoint, guint aTime)
  1.1723 +{
  1.1724 +    if (mScheduledTask == eDragTaskMotion) {
  1.1725 +        // The drag source has sent another motion message before we've
  1.1726 +        // replied to the previous.  That shouldn't happen with Xdnd.  The
  1.1727 +        // spec for Motif drags is less clear, but we'll just update the
  1.1728 +        // scheduled task with the new position reply only to the most
  1.1729 +        // recent message.
  1.1730 +        NS_WARNING("Drag Motion message received before previous reply was sent");
  1.1731 +    }
  1.1732 +
  1.1733 +    // Returning TRUE means we'll reply with a status message, unless we first
  1.1734 +    // get a leave.
  1.1735 +    return Schedule(eDragTaskMotion, aWindow, aDragContext,
  1.1736 +                    aWindowPoint, aTime);
  1.1737 +}
  1.1738 +
  1.1739 +void
  1.1740 +nsDragService::ScheduleLeaveEvent()
  1.1741 +{
  1.1742 +    // We don't know at this stage whether a drop signal will immediately
  1.1743 +    // follow.  If the drop signal gets sent it will happen before we return
  1.1744 +    // to the main loop and the scheduled leave task will be replaced.
  1.1745 +    if (!Schedule(eDragTaskLeave, nullptr, nullptr, nsIntPoint(), 0)) {
  1.1746 +        NS_WARNING("Drag leave after drop");
  1.1747 +    }        
  1.1748 +}
  1.1749 +
  1.1750 +gboolean
  1.1751 +nsDragService::ScheduleDropEvent(nsWindow *aWindow,
  1.1752 +                                 GdkDragContext *aDragContext,
  1.1753 +                                 nsIntPoint aWindowPoint, guint aTime)
  1.1754 +{
  1.1755 +    if (!Schedule(eDragTaskDrop, aWindow,
  1.1756 +                  aDragContext, aWindowPoint, aTime)) {
  1.1757 +        NS_WARNING("Additional drag drop ignored");
  1.1758 +        return FALSE;        
  1.1759 +    }
  1.1760 +
  1.1761 +    SetDragEndPoint(aWindowPoint + aWindow->WidgetToScreenOffset());
  1.1762 +
  1.1763 +    // We'll reply with gtk_drag_finish().
  1.1764 +    return TRUE;
  1.1765 +}
  1.1766 +
  1.1767 +gboolean
  1.1768 +nsDragService::Schedule(DragTask aTask, nsWindow *aWindow,
  1.1769 +                        GdkDragContext *aDragContext,
  1.1770 +                        nsIntPoint aWindowPoint, guint aTime)
  1.1771 +{
  1.1772 +    // If there is an existing leave or motion task scheduled, then that
  1.1773 +    // will be replaced.  When the new task is run, it will dispatch
  1.1774 +    // any necessary leave or motion events.
  1.1775 +
  1.1776 +    // If aTask is eDragTaskSourceEnd, then it will replace even a scheduled
  1.1777 +    // drop event (which could happen if the drop event has not been processed
  1.1778 +    // within the allowed time).  Otherwise, if we haven't yet run a scheduled
  1.1779 +    // drop or end task, just say that we are not ready to receive another
  1.1780 +    // drop.
  1.1781 +    if (mScheduledTask == eDragTaskSourceEnd ||
  1.1782 +        (mScheduledTask == eDragTaskDrop && aTask != eDragTaskSourceEnd))
  1.1783 +        return FALSE;
  1.1784 +
  1.1785 +    mScheduledTask = aTask;
  1.1786 +    mPendingWindow = aWindow;
  1.1787 +    mPendingDragContext = aDragContext;
  1.1788 +    mPendingWindowPoint = aWindowPoint;
  1.1789 +    mPendingTime = aTime;
  1.1790 +
  1.1791 +    if (!mTaskSource) {
  1.1792 +        // High priority is used here because the native events involved have
  1.1793 +        // already waited at default priority.  Perhaps a lower than default
  1.1794 +        // priority could be used for motion tasks because there is a chance
  1.1795 +        // that a leave or drop is waiting, but managing different priorities
  1.1796 +        // may not be worth the effort.  Motion tasks shouldn't queue up as
  1.1797 +        // they should be throttled based on replies.
  1.1798 +        mTaskSource = g_idle_add_full(G_PRIORITY_HIGH, TaskDispatchCallback,
  1.1799 +                                      this, nullptr);
  1.1800 +    }
  1.1801 +    return TRUE;
  1.1802 +}
  1.1803 +
  1.1804 +gboolean
  1.1805 +nsDragService::TaskDispatchCallback(gpointer data)
  1.1806 +{
  1.1807 +    nsRefPtr<nsDragService> dragService = static_cast<nsDragService*>(data);
  1.1808 +    return dragService->RunScheduledTask();
  1.1809 +}
  1.1810 +
  1.1811 +gboolean
  1.1812 +nsDragService::RunScheduledTask()
  1.1813 +{
  1.1814 +    if (mTargetWindow && mTargetWindow != mPendingWindow) {
  1.1815 +        PR_LOG(sDragLm, PR_LOG_DEBUG,
  1.1816 +               ("nsDragService: dispatch drag leave (%p)\n",
  1.1817 +                mTargetWindow.get()));
  1.1818 +        mTargetWindow->
  1.1819 +            DispatchDragEvent(NS_DRAGDROP_EXIT, mTargetWindowPoint, 0);
  1.1820 +
  1.1821 +        if (!mSourceNode) {
  1.1822 +            // The drag that was initiated in a different app. End the drag
  1.1823 +            // session, since we're done with it for now (until the user drags
  1.1824 +            // back into this app).
  1.1825 +            EndDragSession(false);
  1.1826 +        }
  1.1827 +    }
  1.1828 +
  1.1829 +    // It is possible that the pending state has been updated during dispatch
  1.1830 +    // of the leave event.  That's fine.
  1.1831 +
  1.1832 +    // Now we collect the pending state because, from this point on, we want
  1.1833 +    // to use the same state for all events dispatched.  All state is updated
  1.1834 +    // so that when other tasks are scheduled during dispatch here, this
  1.1835 +    // task is considered to have already been run.
  1.1836 +    bool positionHasChanged =
  1.1837 +        mPendingWindow != mTargetWindow ||
  1.1838 +        mPendingWindowPoint != mTargetWindowPoint;
  1.1839 +    DragTask task = mScheduledTask;
  1.1840 +    mScheduledTask = eDragTaskNone;
  1.1841 +    mTargetWindow = mPendingWindow.forget();
  1.1842 +    mTargetWindowPoint = mPendingWindowPoint;
  1.1843 +
  1.1844 +    if (task == eDragTaskLeave || task == eDragTaskSourceEnd) {
  1.1845 +        if (task == eDragTaskSourceEnd) {
  1.1846 +            // Dispatch drag end events.
  1.1847 +            EndDragSession(true);
  1.1848 +        }
  1.1849 +
  1.1850 +        // Nothing more to do
  1.1851 +        // Returning false removes the task source from the event loop.
  1.1852 +        mTaskSource = 0;
  1.1853 +        return FALSE;
  1.1854 +    }
  1.1855 +
  1.1856 +    // This may be the start of a destination drag session.
  1.1857 +    StartDragSession();
  1.1858 +
  1.1859 +    // mTargetWidget may be nullptr if the window has been destroyed.
  1.1860 +    // (The leave event is not scheduled if a drop task is still scheduled.)
  1.1861 +    // We still reply appropriately to indicate that the drop will or didn't
  1.1862 +    // succeeed. 
  1.1863 +    mTargetWidget = mTargetWindow->GetMozContainerWidget();
  1.1864 +    mTargetDragContext.steal(mPendingDragContext);
  1.1865 +    mTargetTime = mPendingTime;
  1.1866 +
  1.1867 +    // http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
  1.1868 +    // (as at 27 December 2010) indicates that a "drop" event should only be
  1.1869 +    // fired (at the current target element) if the current drag operation is
  1.1870 +    // not none.  The current drag operation will only be set to a non-none
  1.1871 +    // value during a "dragover" event.
  1.1872 +    //
  1.1873 +    // If the user has ended the drag before any dragover events have been
  1.1874 +    // sent, then the spec recommends skipping the drop (because the current
  1.1875 +    // drag operation is none).  However, here we assume that, by releasing
  1.1876 +    // the mouse button, the user has indicated that they want to drop, so we
  1.1877 +    // proceed with the drop where possible.
  1.1878 +    //
  1.1879 +    // In order to make the events appear to content in the same way as if the
  1.1880 +    // spec is being followed we make sure to dispatch a "dragover" event with
  1.1881 +    // appropriate coordinates and check canDrop before the "drop" event.
  1.1882 +    //
  1.1883 +    // When the Xdnd protocol is used for source/destination communication (as
  1.1884 +    // should be the case with GTK source applications) a dragover event
  1.1885 +    // should have already been sent during the drag-motion signal, which
  1.1886 +    // would have already been received because XdndDrop messages do not
  1.1887 +    // contain a position.  However, we can't assume the same when the Motif
  1.1888 +    // protocol is used.
  1.1889 +    if (task == eDragTaskMotion || positionHasChanged) {
  1.1890 +        UpdateDragAction();
  1.1891 +        DispatchMotionEvents();
  1.1892 +
  1.1893 +        if (task == eDragTaskMotion) {
  1.1894 +            // Reply to tell the source whether we can drop and what
  1.1895 +            // action would be taken.
  1.1896 +            ReplyToDragMotion();
  1.1897 +        }
  1.1898 +    }
  1.1899 +
  1.1900 +    if (task == eDragTaskDrop) {
  1.1901 +        gboolean success = DispatchDropEvent();
  1.1902 +
  1.1903 +        // Perhaps we should set the del parameter to TRUE when the drag
  1.1904 +        // action is move, but we don't know whether the data was successfully
  1.1905 +        // transferred.
  1.1906 +        gtk_drag_finish(mTargetDragContext, success,
  1.1907 +                        /* del = */ FALSE, mTargetTime);
  1.1908 +
  1.1909 +        // This drag is over, so clear out our reference to the previous
  1.1910 +        // window.
  1.1911 +        mTargetWindow = nullptr;
  1.1912 +        // Make sure to end the drag session. If this drag started in a
  1.1913 +        // different app, we won't get a drag_end signal to end it from.
  1.1914 +        EndDragSession(true);
  1.1915 +    }
  1.1916 +
  1.1917 +    // We're done with the drag context.
  1.1918 +    mTargetWidget = nullptr;
  1.1919 +    mTargetDragContext = nullptr;
  1.1920 +
  1.1921 +    // If we got another drag signal while running the sheduled task, that
  1.1922 +    // must have happened while running a nested event loop.  Leave the task
  1.1923 +    // source on the event loop.
  1.1924 +    if (mScheduledTask != eDragTaskNone)
  1.1925 +        return TRUE;
  1.1926 +
  1.1927 +    // We have no task scheduled.
  1.1928 +    // Returning false removes the task source from the event loop.
  1.1929 +    mTaskSource = 0;
  1.1930 +    return FALSE;
  1.1931 +}
  1.1932 +
  1.1933 +// This will update the drag action based on the information in the
  1.1934 +// drag context.  Gtk gets this from a combination of the key settings
  1.1935 +// and what the source is offering.
  1.1936 +
  1.1937 +void
  1.1938 +nsDragService::UpdateDragAction()
  1.1939 +{
  1.1940 +    // This doesn't look right.  dragSession.dragAction is used by
  1.1941 +    // nsContentUtils::SetDataTransferInEvent() to set the initial
  1.1942 +    // dataTransfer.dropEffect, so GdkDragContext::suggested_action would be
  1.1943 +    // more appropriate.  GdkDragContext::actions should be used to set
  1.1944 +    // dataTransfer.effectAllowed, which doesn't currently happen with
  1.1945 +    // external sources.
  1.1946 +
  1.1947 +    // default is to do nothing
  1.1948 +    int action = nsIDragService::DRAGDROP_ACTION_NONE;
  1.1949 +    GdkDragAction gdkAction = gdk_drag_context_get_actions(mTargetDragContext);
  1.1950 +
  1.1951 +    // set the default just in case nothing matches below
  1.1952 +    if (gdkAction & GDK_ACTION_DEFAULT)
  1.1953 +        action = nsIDragService::DRAGDROP_ACTION_MOVE;
  1.1954 +
  1.1955 +    // first check to see if move is set
  1.1956 +    if (gdkAction & GDK_ACTION_MOVE)
  1.1957 +        action = nsIDragService::DRAGDROP_ACTION_MOVE;
  1.1958 +
  1.1959 +    // then fall to the others
  1.1960 +    else if (gdkAction & GDK_ACTION_LINK)
  1.1961 +        action = nsIDragService::DRAGDROP_ACTION_LINK;
  1.1962 +
  1.1963 +    // copy is ctrl
  1.1964 +    else if (gdkAction & GDK_ACTION_COPY)
  1.1965 +        action = nsIDragService::DRAGDROP_ACTION_COPY;
  1.1966 +
  1.1967 +    // update the drag information
  1.1968 +    SetDragAction(action);
  1.1969 +}
  1.1970 +
  1.1971 +void
  1.1972 +nsDragService::DispatchMotionEvents()
  1.1973 +{
  1.1974 +    mCanDrop = false;
  1.1975 +
  1.1976 +    FireDragEventAtSource(NS_DRAGDROP_DRAG);
  1.1977 +
  1.1978 +    mTargetWindow->
  1.1979 +        DispatchDragEvent(NS_DRAGDROP_OVER, mTargetWindowPoint, mTargetTime);
  1.1980 +}
  1.1981 +
  1.1982 +// Returns true if the drop was successful
  1.1983 +gboolean
  1.1984 +nsDragService::DispatchDropEvent()
  1.1985 +{
  1.1986 +    // We need to check IsDestroyed here because the nsRefPtr
  1.1987 +    // only protects this from being deleted, it does NOT protect
  1.1988 +    // against nsView::~nsView() calling Destroy() on it, bug 378273.
  1.1989 +    if (mTargetWindow->IsDestroyed())
  1.1990 +        return FALSE;
  1.1991 +
  1.1992 +    uint32_t msg = mCanDrop ? NS_DRAGDROP_DROP : NS_DRAGDROP_EXIT;
  1.1993 +
  1.1994 +    mTargetWindow->DispatchDragEvent(msg, mTargetWindowPoint, mTargetTime);
  1.1995 +
  1.1996 +    return mCanDrop;
  1.1997 +}

mercurial