DragAndDropHandler.cpp [plain text]
#include "config.h"
#include "DragAndDropHandler.h"
#if ENABLE(DRAG_SUPPORT)
#include "WebPageProxy.h"
#include <WebCore/DataObjectGtk.h>
#include <WebCore/DragData.h>
#include <WebCore/GRefPtrGtk.h>
#include <WebCore/GtkUtilities.h>
#include <WebCore/PasteboardHelper.h>
#include <gtk/gtk.h>
#include <wtf/RunLoop.h>
#include <wtf/glib/GUniquePtr.h>
using namespace WebCore;
namespace WebKit {
DragAndDropHandler::DragAndDropHandler(WebPageProxy& page)
: m_page(page)
#if GTK_CHECK_VERSION(3, 16, 0)
, m_dragContext(nullptr)
#endif
{
}
DragAndDropHandler::DroppingContext::DroppingContext(GdkDragContext* gdkContext, const IntPoint& position)
: gdkContext(gdkContext)
, dataObject(DataObjectGtk::create())
, lastMotionPosition(position)
, dropHappened(false)
{
}
static inline GdkDragAction dragOperationToGdkDragActions(DragOperation coreAction)
{
GdkDragAction gdkAction = static_cast<GdkDragAction>(0);
if (coreAction == DragOperationNone)
return gdkAction;
if (coreAction & DragOperationCopy)
gdkAction = static_cast<GdkDragAction>(GDK_ACTION_COPY | gdkAction);
if (coreAction & DragOperationMove)
gdkAction = static_cast<GdkDragAction>(GDK_ACTION_MOVE | gdkAction);
if (coreAction & DragOperationLink)
gdkAction = static_cast<GdkDragAction>(GDK_ACTION_LINK | gdkAction);
if (coreAction & DragOperationPrivate)
gdkAction = static_cast<GdkDragAction>(GDK_ACTION_PRIVATE | gdkAction);
return gdkAction;
}
static inline GdkDragAction dragOperationToSingleGdkDragAction(DragOperation coreAction)
{
if (coreAction == DragOperationEvery || coreAction & DragOperationCopy)
return GDK_ACTION_COPY;
if (coreAction & DragOperationMove)
return GDK_ACTION_MOVE;
if (coreAction & DragOperationLink)
return GDK_ACTION_LINK;
if (coreAction & DragOperationPrivate)
return GDK_ACTION_PRIVATE;
return static_cast<GdkDragAction>(0);
}
static inline DragOperation gdkDragActionToDragOperation(GdkDragAction gdkAction)
{
if (gdkAction & GDK_ACTION_COPY
&& gdkAction & GDK_ACTION_MOVE
&& gdkAction & GDK_ACTION_LINK
&& gdkAction & GDK_ACTION_PRIVATE)
return DragOperationEvery;
unsigned action = DragOperationNone;
if (gdkAction & GDK_ACTION_COPY)
action |= DragOperationCopy;
if (gdkAction & GDK_ACTION_MOVE)
action |= DragOperationMove;
if (gdkAction & GDK_ACTION_LINK)
action |= DragOperationLink;
if (gdkAction & GDK_ACTION_PRIVATE)
action |= DragOperationPrivate;
return static_cast<DragOperation>(action);
}
void DragAndDropHandler::startDrag(const DragData& dragData, PassRefPtr<ShareableBitmap> dragImage)
{
#if GTK_CHECK_VERSION(3, 16, 0)
m_draggingDataObject = adoptRef(dragData.platformData());
GRefPtr<GtkTargetList> targetList = adoptGRef(PasteboardHelper::singleton().targetListForDataObject(m_draggingDataObject.get()));
#else
RefPtr<DataObjectGtk> dataObject = adoptRef(dragData.platformData());
GRefPtr<GtkTargetList> targetList = adoptGRef(PasteboardHelper::singleton().targetListForDataObject(dataObject.get()));
#endif
GUniquePtr<GdkEvent> currentEvent(gtk_get_current_event());
GdkDragContext* context = gtk_drag_begin(m_page.viewWidget(), targetList.get(), dragOperationToGdkDragActions(dragData.draggingSourceOperationMask()),
GDK_BUTTON_PRIMARY, currentEvent.get());
#if GTK_CHECK_VERSION(3, 16, 0)
if (m_dragContext)
gtk_drag_cancel(m_dragContext.get());
m_dragContext = context;
#else
m_draggingDataObjects.set(context, dataObject.get());
#endif
if (dragImage) {
RefPtr<cairo_surface_t> image(dragImage->createCairoSurface());
cairo_surface_set_device_offset(image.get(), -cairo_image_surface_get_width(image.get()) / 2, -cairo_image_surface_get_height(image.get()) / 2);
gtk_drag_set_icon_surface(context, image.get());
} else
gtk_drag_set_icon_default(context);
}
void DragAndDropHandler::fillDragData(GdkDragContext* context, GtkSelectionData* selectionData, unsigned info)
{
#if GTK_CHECK_VERSION(3, 16, 0)
if (m_dragContext.get() != context)
return;
ASSERT(m_draggingDataObject);
PasteboardHelper::singleton().fillSelectionData(selectionData, info, m_draggingDataObject.get());
#else
if (DataObjectGtk* dataObject = m_draggingDataObjects.get(context))
PasteboardHelper::singleton().fillSelectionData(selectionData, info, dataObject);
#endif
}
void DragAndDropHandler::finishDrag(GdkDragContext* context)
{
#if GTK_CHECK_VERSION(3, 16, 0)
if (m_dragContext.get() != context)
return;
if (!m_draggingDataObject)
return;
m_dragContext = nullptr;
m_draggingDataObject = nullptr;
#else
if (!m_draggingDataObjects.remove(context))
return;
#endif
GdkDevice* device = gdk_drag_context_get_device(context);
int x = 0, y = 0;
gdk_device_get_window_at_position(device, &x, &y);
int xRoot = 0, yRoot = 0;
gdk_device_get_position(device, nullptr, &xRoot, &yRoot);
m_page.dragEnded(IntPoint(x, y), IntPoint(xRoot, yRoot), gdkDragActionToDragOperation(gdk_drag_context_get_selected_action(context)));
}
DataObjectGtk* DragAndDropHandler::dataObjectForDropData(GdkDragContext* context, GtkSelectionData* selectionData, unsigned info, IntPoint& position)
{
DroppingContext* droppingContext = m_droppingContexts.get(context);
if (!droppingContext)
return nullptr;
droppingContext->pendingDataRequests--;
PasteboardHelper::singleton().fillDataObjectFromDropData(selectionData, info, droppingContext->dataObject.get());
if (droppingContext->pendingDataRequests)
return nullptr;
position = droppingContext->lastMotionPosition;
return droppingContext->dataObject.get();
}
void DragAndDropHandler::dragEntered(GdkDragContext* context, GtkSelectionData* selectionData, unsigned info, unsigned time)
{
IntPoint position;
DataObjectGtk* dataObject = dataObjectForDropData(context, selectionData, info, position);
if (!dataObject)
return;
DragData dragData(dataObject, position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)));
m_page.resetCurrentDragInformation();
m_page.dragEntered(dragData);
DragOperation operation = m_page.currentDragOperation();
gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time);
}
DataObjectGtk* DragAndDropHandler::requestDragData(GdkDragContext* context, const IntPoint& position, unsigned time)
{
std::unique_ptr<DroppingContext>& droppingContext = m_droppingContexts.add(context, nullptr).iterator->value;
if (!droppingContext) {
GtkWidget* widget = m_page.viewWidget();
droppingContext = std::make_unique<DroppingContext>(context, position);
Vector<GdkAtom> acceptableTargets(PasteboardHelper::singleton().dropAtomsForContext(widget, droppingContext->gdkContext));
droppingContext->pendingDataRequests = acceptableTargets.size();
for (auto& target : acceptableTargets)
gtk_drag_get_data(widget, droppingContext->gdkContext, target, time);
} else
droppingContext->lastMotionPosition = position;
if (droppingContext->pendingDataRequests > 0)
return nullptr;
return droppingContext->dataObject.get();
}
void DragAndDropHandler::dragMotion(GdkDragContext* context, const IntPoint& position, unsigned time)
{
DataObjectGtk* dataObject = requestDragData(context, position, time);
if (!dataObject)
return;
DragData dragData(dataObject, position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)));
m_page.dragUpdated(dragData);
DragOperation operation = m_page.currentDragOperation();
gdk_drag_status(context, dragOperationToSingleGdkDragAction(operation), time);
}
void DragAndDropHandler::dragLeave(GdkDragContext* context)
{
DroppingContext* droppingContext = m_droppingContexts.get(context);
if (!droppingContext)
return;
RunLoop::main().dispatch([this, droppingContext]() {
auto it = m_droppingContexts.find(droppingContext->gdkContext);
if (it == m_droppingContexts.end())
return;
if (droppingContext->pendingDataRequests)
return;
if (!droppingContext->dropHappened) {
const IntPoint& position = droppingContext->lastMotionPosition;
DragData dragData(droppingContext->dataObject.get(), position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), DragOperationNone);
m_page.dragExited(dragData);
m_page.resetCurrentDragInformation();
}
m_droppingContexts.remove(it);
});
}
bool DragAndDropHandler::drop(GdkDragContext* context, const IntPoint& position, unsigned time)
{
DroppingContext* droppingContext = m_droppingContexts.get(context);
if (!droppingContext)
return false;
droppingContext->dropHappened = true;
DataObjectGtk* dataObject = droppingContext->dataObject.get();
if (!dataObject)
return false;
DragData dragData(dataObject, position, convertWidgetPointToScreenPoint(m_page.viewWidget(), position), gdkDragActionToDragOperation(gdk_drag_context_get_actions(context)));
SandboxExtension::Handle handle;
SandboxExtension::HandleArray sandboxExtensionForUpload;
m_page.performDragOperation(dragData, String(), handle, sandboxExtensionForUpload);
gtk_drag_finish(context, TRUE, FALSE, time);
return true;
}
}
#endif // ENABLE(DRAG_SUPPORT)