widget/gtk/nsDragService.cpp

Thu, 22 Jan 2015 13:21:57 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Thu, 22 Jan 2015 13:21:57 +0100
branch
TOR_BUG_9701
changeset 15
b8a032363ba2
permissions
-rw-r--r--

Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6

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

mercurial