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 +}