b=497498 schedule event dispatch in response to GTK drag target signals to avoid running the event loop at unexpected times r=roc

--HG--
extra : rebase_source : 881ad7c0efa85174347059a9f53b3a5bd4c76696
This commit is contained in:
Karl Tomlinson 2012-04-19 18:18:31 +12:00
parent c2121f5398
commit a5dc82ba4e
4 changed files with 389 additions and 274 deletions

View File

@ -127,6 +127,7 @@ invisibleSourceDragDataGet(GtkWidget *aWidget,
gpointer aData); gpointer aData);
nsDragService::nsDragService() nsDragService::nsDragService()
: mTaskSource(0)
{ {
// We have to destroy the hidden widget before the event loop stops // We have to destroy the hidden widget before the event loop stops
// running. // running.
@ -162,9 +163,6 @@ nsDragService::nsDragService()
sDragLm = PR_NewLogModule("nsDragService"); sDragLm = PR_NewLogModule("nsDragService");
PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::nsDragService")); PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::nsDragService"));
mGrabWidget = 0; mGrabWidget = 0;
mTargetWidget = 0;
mTargetDragContext = 0;
mTargetTime = 0;
mCanDrop = false; mCanDrop = false;
mTargetDragDataReceived = false; mTargetDragDataReceived = false;
mTargetDragData = 0; mTargetDragData = 0;
@ -174,6 +172,9 @@ nsDragService::nsDragService()
nsDragService::~nsDragService() nsDragService::~nsDragService()
{ {
PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::~nsDragService")); PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::~nsDragService"));
if (mTaskSource)
g_source_remove(mTaskSource);
} }
NS_IMPL_ISUPPORTS_INHERITED2(nsDragService, nsBaseDragService, NS_IMPL_ISUPPORTS_INHERITED2(nsDragService, nsBaseDragService,
@ -1131,7 +1132,8 @@ nsDragService::GetTargetDragData(GdkAtom aFlavor)
{ {
PR_LOG(sDragLm, PR_LOG_DEBUG, ("getting data flavor %d\n", aFlavor)); PR_LOG(sDragLm, PR_LOG_DEBUG, ("getting data flavor %d\n", aFlavor));
PR_LOG(sDragLm, PR_LOG_DEBUG, ("mLastWidget is %p and mLastContext is %p\n", PR_LOG(sDragLm, PR_LOG_DEBUG, ("mLastWidget is %p and mLastContext is %p\n",
mTargetWidget, mTargetDragContext)); mTargetWidget.get(),
mTargetDragContext.get()));
// reset our target data areas // reset our target data areas
TargetResetData(); TargetResetData();
gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime); gtk_drag_get_data(mTargetWidget, mTargetDragContext, aFlavor, mTargetTime);
@ -1702,3 +1704,231 @@ invisibleSourceDragEnd(GtkWidget *aWidget,
dragService->SourceEndDragSession(aContext, MOZ_GTK_DRAG_RESULT_SUCCESS); dragService->SourceEndDragSession(aContext, MOZ_GTK_DRAG_RESULT_SUCCESS);
} }
// The following methods handle responding to GTK drag destination signals and
// tracking state between these signals.
//
// In general, GTK does not expect us to run the event loop while handling its
// drag destination signals, however our drag event handlers may run the
// event loop, most often to fetch information about the drag data.
//
// GTK, for example, uses the return value from drag-motion signals to
// determine whether drag-leave signals should be sent. If an event loop is
// run during drag-motion the XdndLeave message can get processed but when GTK
// receives the message it does not yet know that it needs to send the
// drag-leave signal to our widget.
//
// After a drag-drop signal, we need to reply with gtk_drag_finish().
// However, gtk_drag_finish should happen after the drag-drop signal handler
// returns so that when the Motif drag protocol is used, the
// XmTRANSFER_SUCCESS during gtk_drag_finish is sent after the XmDROP_START
// reply sent on return from the drag-drop signal handler.
//
// Therefore we reply to the signals immediately and schedule a task to
// dispatch the Gecko events, which may run the event loop.
//
// Action in response to drag-leave signals is also delayed until the event
// loop runs again so that we find out whether a drag-drop signal follows.
//
// A single task is scheduled to manage responses to all three GTK signals.
// If further signals are received while the task is scheduled, the scheduled
// response is updated, sometimes effectively compressing successive signals.
//
// No Gecko drag events are dispatched (during nested event loops) while other
// Gecko drag events are in flight. This helps event handlers that may not
// expect nested events, while accessing an event's dataTransfer for example.
gboolean
nsDragService::ScheduleMotionEvent(nsWindow *aWindow,
GdkDragContext *aDragContext,
nsIntPoint aWindowPoint, guint aTime)
{
if (mScheduledTask == eDragTaskMotion) {
// The drag source has sent another motion message before we've
// replied to the previous. That shouldn't happen with Xdnd. The
// spec for Motif drags is less clear, but we'll just update the
// scheduled task with the new position reply only to the most
// recent message.
NS_WARNING("Drag Motion message received before previous reply was sent");
}
// Returning TRUE means we'll reply with a status message, unless we first
// get a leave.
return Schedule(eDragTaskMotion, aWindow, aDragContext,
aWindowPoint, aTime);
}
void
nsDragService::ScheduleLeaveEvent()
{
// We don't know at this stage whether a drop signal will immediately
// follow. If the drop signal gets sent it will happen before we return
// to the main loop and the scheduled leave task will be replaced.
if (!Schedule(eDragTaskLeave, nsnull, NULL, nsIntPoint(), 0)) {
NS_WARNING("Drag leave after drop");
}
}
gboolean
nsDragService::ScheduleDropEvent(nsWindow *aWindow,
GdkDragContext *aDragContext,
nsIntPoint aWindowPoint, guint aTime)
{
if (!Schedule(eDragTaskDrop, aWindow,
aDragContext, aWindowPoint, aTime)) {
NS_WARNING("Additional drag drop ignored");
return FALSE;
}
SetDragEndPoint(aWindowPoint + aWindow->WidgetToScreenOffset());
// We'll reply with gtk_drag_finish().
return TRUE;
}
gboolean
nsDragService::Schedule(DragTask aTask, nsWindow *aWindow,
GdkDragContext *aDragContext,
nsIntPoint aWindowPoint, guint aTime)
{
// If we haven't yet run a scheduled drop task, just say that
// we are not ready to receive another drop.
if (mScheduledTask == eDragTaskDrop)
return FALSE;
// If there is an existing leave or motion task scheduled, then that
// will be replaced. When the new task is run, it will dispatch
// any necessary leave or motion events.
mScheduledTask = aTask;
mPendingWindow = aWindow;
mPendingDragContext = aDragContext;
mPendingWindowPoint = aWindowPoint;
mPendingTime = aTime;
if (!mTaskSource) {
// High priority is used here because the native events involved have
// already waited at default priority. Perhaps a lower than default
// priority could be used for motion tasks because there is a chance
// that a leave or drop is waiting, but managing different priorities
// may not be worth the effort. Motion tasks shouldn't queue up as
// they should be throttled based on replies.
mTaskSource =
g_idle_add_full(G_PRIORITY_HIGH, TaskDispatchCallback, this, NULL);
}
return TRUE;
}
gboolean
nsDragService::TaskDispatchCallback(gpointer data)
{
nsRefPtr<nsDragService> dragService = static_cast<nsDragService*>(data);
return dragService->RunScheduledTask();
}
gboolean
nsDragService::RunScheduledTask()
{
if (mTargetWindow && mTargetWindow != mPendingWindow) {
mTargetWindow->OnDragLeave();
}
// It is possible that the pending state has been updated during dispatch
// of the leave event. That's fine.
// Now we collect the pending state because, from this point on, we want
// to use the same state for all events dispatched. All state is updated
// so that when other tasks are scheduled during dispatch here, this
// task is considered to have already been run.
bool positionHasChanged =
mPendingWindow != mTargetWindow ||
mPendingWindowPoint != mTargetWindowPoint;
DragTask task = mScheduledTask;
mScheduledTask = eDragTaskNone;
mTargetWindow = mPendingWindow.forget();
mTargetWindowPoint = mPendingWindowPoint;
if (task == eDragTaskLeave) {
// Nothing more to do
// Returning false removes the task source from the event loop.
mTaskSource = 0;
return FALSE;
}
// This may be the start of a destination drag session.
StartDragSession();
// mTargetWidget may be NULL if the window has been destroyed.
// (The leave event is not scheduled if a drop task is still scheduled.)
// We still reply appropriately to indicate that the drop will or didn't
// succeeed.
mTargetWidget = mTargetWindow->GetMozContainerWidget();
mTargetDragContext.steal(mPendingDragContext);
mTargetTime = mPendingTime;
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
// (as at 27 December 2010) indicates that a "drop" event should only be
// fired (at the current target element) if the current drag operation is
// not none. The current drag operation will only be set to a non-none
// value during a "dragover" event.
//
// If the user has ended the drag before any dragover events have been
// sent, then the spec recommends skipping the drop (because the current
// drag operation is none). However, here we assume that, by releasing
// the mouse button, the user has indicated that they want to drop, so we
// proceed with the drop where possible.
//
// In order to make the events appear to content in the same way as if the
// spec is being followed we make sure to dispatch a "dragover" event with
// appropriate coordinates and check canDrop before the "drop" event.
//
// When the Xdnd protocol is used for source/destination communication (as
// should be the case with GTK source applications) a dragover event
// should have already been sent during the drag-motion signal, which
// would have already been received because XdndDrop messages do not
// contain a position. However, we can't assume the same when the Motif
// protocol is used.
if (task == eDragTaskMotion || positionHasChanged) {
nsWindow::UpdateDragStatus(mTargetDragContext, this);
mTargetWindow->
DispatchDragMotionEvents(this, mTargetWindowPoint, mTargetTime);
if (task == eDragTaskMotion) {
// Reply to tell the source whether we can drop and what
// action would be taken.
TargetEndDragMotion(mTargetWidget, mTargetDragContext, mTargetTime);
}
}
if (task == eDragTaskDrop) {
gboolean success = mTargetWindow->
DispatchDragDropEvent(this, mTargetWindowPoint, mTargetTime);
// Perhaps we should set the del parameter to TRUE when the drag
// action is move, but we don't know whether the data was successfully
// transferred.
gtk_drag_finish(mTargetDragContext, success,
/* del = */ FALSE, mTargetTime);
// This drag is over, so clear out our reference to the previous
// window.
mTargetWindow = nsnull;
// Make sure to end the drag session. If this drag started in a
// different app, we won't get a drag_end signal to end it from.
EndDragSession(true);
}
// We're done with the drag context.
mTargetWidget = NULL;
mTargetDragContext = NULL;
// If we got another drag signal while running the sheduled task, that
// must have happened while running a nested event loop. Leave the task
// source on the event loop.
if (mScheduledTask != eDragTaskNone)
return TRUE;
// We have no task scheduled.
// Returning false removes the task source from the event loop.
mTaskSource = 0;
return FALSE;
}

View File

@ -46,6 +46,30 @@
#include "nsIObserver.h" #include "nsIObserver.h"
#include <gtk/gtk.h> #include <gtk/gtk.h>
class nsWindow;
#ifndef HAVE_NSGOBJECTREFTRAITS
#define HAVE_NSGOBJECTREFTRAITS
template <class T>
class nsGObjectRefTraits : public nsPointerRefTraits<T> {
public:
static void Release(T *aPtr) { g_object_unref(aPtr); }
static void AddRef(T *aPtr) { g_object_ref(aPtr); }
};
#endif
#ifndef HAVE_NSAUTOREFTRAITS_GTKWIDGET
#define HAVE_NSAUTOREFTRAITS_GTKWIDGET
template <>
class nsAutoRefTraits<GtkWidget> : public nsGObjectRefTraits<GtkWidget> { };
#endif
#ifndef HAVE_NSAUTOREFTRAITS_GDKDRAGCONTEXT
#define HAVE_NSAUTOREFTRAITS_GDKDRAGCONTEXT
template <>
class nsAutoRefTraits<GdkDragContext> :
public nsGObjectRefTraits<GdkDragContext> { };
#endif
/** /**
* Native GTK DragService wrapper * Native GTK DragService wrapper
@ -100,6 +124,25 @@ public:
static nsDragService* GetInstance(); static nsDragService* GetInstance();
// Methods called from nsWindow to handle responding to GTK drag
// destination signals
gboolean ScheduleMotionEvent(nsWindow *aWindow,
GdkDragContext *aDragContext,
nsIntPoint aWindowPoint,
guint aTime);
void ScheduleLeaveEvent();
gboolean ScheduleDropEvent(nsWindow *aWindow,
GdkDragContext *aDragContext,
nsIntPoint aWindowPoint,
guint aTime);
nsWindow* GetMostRecentDestWindow()
{
return mScheduledTask == eDragTaskNone ? mTargetWindow
: mPendingWindow;
}
// END PUBLIC API // END PUBLIC API
// These methods are public only so that they can be called from functions // These methods are public only so that they can be called from functions
@ -118,14 +161,47 @@ public:
private: private:
// target side vars // mScheduledTask indicates what signal has been received from GTK and
// so what needs to be dispatched when the scheduled task is run. It is
// eDragTaskNone when there is no task scheduled (but the
// previous task may still not have finished running).
enum DragTask {
eDragTaskNone,
eDragTaskMotion,
eDragTaskLeave,
eDragTaskDrop
};
DragTask mScheduledTask;
// mTaskSource is the GSource id for the task that is either scheduled
// or currently running. It is 0 if no task is scheduled or running.
guint mTaskSource;
// the last widget that was the target of a drag // target/destination side vars
GtkWidget *mTargetWidget; // These variables keep track of the state of the current drag.
GdkDragContext *mTargetDragContext;
// mPendingWindow, mPendingWindowPoint, mPendingDragContext, and
// mPendingTime, carry information from the GTK signal that will be used
// when the scheduled task is run. mPendingWindow and mPendingDragContext
// will be NULL if the scheduled task is eDragTaskLeave.
nsRefPtr<nsWindow> mPendingWindow;
nsIntPoint mPendingWindowPoint;
nsCountedRef<GdkDragContext> mPendingDragContext;
guint mPendingTime;
// mTargetWindow and mTargetWindowPoint record the position of the last
// eDragTaskMotion or eDragTaskDrop task that was run or is still running.
// mTargetWindow is cleared once the drag has completed or left.
nsRefPtr<nsWindow> mTargetWindow;
nsIntPoint mTargetWindowPoint;
// mTargetWidget and mTargetDragContext are set only while dispatching
// motion or drop events. mTime records the corresponding timestamp.
nsCountedRef<GtkWidget> mTargetWidget;
nsCountedRef<GdkDragContext> mTargetDragContext;
guint mTargetTime; guint mTargetTime;
// is it OK to drop on us? // is it OK to drop on us?
bool mCanDrop; bool mCanDrop;
// have we received our drag data? // have we received our drag data?
bool mTargetDragDataReceived; bool mTargetDragDataReceived;
// last data received and its length // last data received and its length
@ -161,6 +237,13 @@ private:
PRInt32 aYOffset, PRInt32 aYOffset,
const nsIntRect &dragRect); const nsIntRect &dragRect);
gboolean Schedule(DragTask aTask, nsWindow *aWindow,
GdkDragContext *aDragContext,
nsIntPoint aWindowPoint, guint aTime);
// Callback for g_idle_add_full() to run mScheduledTask.
static gboolean TaskDispatchCallback(gpointer data);
gboolean RunScheduledTask();
}; };
#endif // nsDragService_h__ #endif // nsDragService_h__

View File

@ -272,9 +272,6 @@ static void drag_data_received_event_cb(GtkWidget *aWidget,
/* initialization static functions */ /* initialization static functions */
static nsresult initialize_prefs (void); static nsresult initialize_prefs (void);
// this is the last window that had a drag event happen on it.
nsWindow *nsWindow::sLastDragMotionWindow = NULL;
// Time of the last button release event. We use it to detect when the // Time of the last button release event. We use it to detect when the
// drag ended before we could properly setup drag and drop. // drag ended before we could properly setup drag and drop.
static guint32 sLastButtonReleaseTime = 0; static guint32 sLastButtonReleaseTime = 0;
@ -433,9 +430,6 @@ nsWindow::nsWindow()
nsWindow::~nsWindow() nsWindow::~nsWindow()
{ {
LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this)); LOG(("nsWindow::~nsWindow() [%p]\n", (void *)this));
if (sLastDragMotionWindow == this) {
sLastDragMotionWindow = NULL;
}
delete[] mTransparencyBitmap; delete[] mTransparencyBitmap;
mTransparencyBitmap = nsnull; mTransparencyBitmap = nsnull;
@ -687,6 +681,11 @@ nsWindow::Destroy(void)
gRollupListener = nsnull; gRollupListener = nsnull;
} }
nsDragService *dragService = nsDragService::GetInstance();
if (this == dragService->GetMostRecentDestWindow()) {
dragService->ScheduleLeaveEvent();
}
NativeShow(false); NativeShow(false);
if (mIMModule) { if (mIMModule) {
@ -715,11 +714,6 @@ nsWindow::Destroy(void)
// the surface after its X Window. // the surface after its X Window.
mThebesSurface = nsnull; mThebesSurface = nsnull;
if (mDragLeaveTimer) {
mDragLeaveTimer->Cancel();
mDragLeaveTimer = nsnull;
}
GtkWidget *owningWidget = GetMozContainerWidget(); GtkWidget *owningWidget = GetMozContainerWidget();
if (mShell) { if (mShell) {
gtk_widget_destroy(mShell); gtk_widget_destroy(mShell);
@ -3309,35 +3303,6 @@ nsWindow::ThemeChanged()
} }
} }
void
nsWindow::CheckNeedDragLeave(nsWindow* aInnerMostWidget,
nsIDragService* aDragService,
GdkDragContext *aDragContext,
nscoord aX, nscoord aY)
{
// check to see if there was a drag motion window already in place
if (sLastDragMotionWindow) {
// same as the last window so no need for dragleave event
if (sLastDragMotionWindow == aInnerMostWidget) {
UpdateDragStatus(aDragContext, aDragService);
return;
}
// send a dragleave event to the last window that got a motion event
nsRefPtr<nsWindow> kungFuDeathGrip = sLastDragMotionWindow;
sLastDragMotionWindow->OnDragLeave();
}
// Make sure that the drag service knows we're now dragging
aDragService->StartDragSession();
// update our drag status
UpdateDragStatus(aDragContext, aDragService);
// set the last window to the innerMostWidget
sLastDragMotionWindow = aInnerMostWidget;
}
void void
nsWindow::DispatchDragMotionEvents(nsDragService *aDragService, nsWindow::DispatchDragMotionEvents(nsDragService *aDragService,
const nsIntPoint& aWindowPoint, guint aTime) const nsIntPoint& aWindowPoint, guint aTime)
@ -3386,170 +3351,6 @@ nsWindow::DispatchDragEvent(PRUint32 aMsg, const nsIntPoint& aRefPoint,
DispatchEvent(&event, status); DispatchEvent(&event, status);
} }
gboolean
nsWindow::OnDragMotionEvent(GtkWidget *aWidget,
GdkDragContext *aDragContext,
gint aX,
gint aY,
guint aTime,
gpointer aData)
{
LOGDRAG(("nsWindow::OnDragMotionSignal\n"));
// get our drag context
nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
nsDragService *dragServiceGTK =
static_cast<nsDragService*>(dragService.get());
// first, figure out which internal widget this drag motion actually
// happened on
nscoord retx = 0;
nscoord rety = 0;
GdkWindow *innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
&retx, &rety);
nsRefPtr<nsWindow> innerMostWidget = get_window_for_gdk_window(innerWindow);
if (!innerMostWidget)
innerMostWidget = this;
// clear any drag leave timer that might be pending so that it
// doesn't get processed when we actually go out to get data.
if (mDragLeaveTimer) {
mDragLeaveTimer->Cancel();
mDragLeaveTimer = nsnull;
}
CheckNeedDragLeave(innerMostWidget, dragService, aDragContext, retx, rety);
// update the drag context
dragServiceGTK->TargetSetLastContext(aWidget, aDragContext, aTime);
innerMostWidget->
DispatchDragMotionEvents(dragServiceGTK, nsIntPoint(retx, rety), aTime);
// Reply to tell the source whether we can drop and what action would be
// taken.
dragServiceGTK->TargetEndDragMotion(aWidget, aDragContext, aTime);
// and unset our context
dragServiceGTK->TargetSetLastContext(0, 0, 0);
return TRUE;
}
void
nsWindow::OnDragLeaveEvent(GtkWidget *aWidget,
GdkDragContext *aDragContext,
guint aTime,
gpointer aData)
{
// XXX Do we want to pass this on only if the event's subwindow is null?
LOGDRAG(("nsWindow::OnDragLeaveSignal(%p)\n", (void*)this));
if (mDragLeaveTimer) {
return;
}
// create a fast timer - we're delaying the drag leave until the
// next mainloop in hopes that we might be able to get a drag drop
// signal
mDragLeaveTimer = do_CreateInstance("@mozilla.org/timer;1");
NS_ASSERTION(mDragLeaveTimer, "Failed to create drag leave timer!");
// fire this baby asafp, but not too quickly... see bug 216800 ;-)
mDragLeaveTimer->InitWithFuncCallback(DragLeaveTimerCallback,
(void *)this,
20, nsITimer::TYPE_ONE_SHOT);
}
gboolean
nsWindow::OnDragDropEvent(GtkWidget *aWidget,
GdkDragContext *aDragContext,
gint aX,
gint aY,
guint aTime,
gpointer aData)
{
LOGDRAG(("nsWindow::OnDragDropSignal\n"));
// get our drag context
nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
nsDragService *dragServiceGTK = static_cast<nsDragService*>(dragService.get());
dragServiceGTK->SetDragEndPoint(nsIntPoint(aX, aY) + WidgetToScreenOffset());
nscoord retx = 0;
nscoord rety = 0;
GdkWindow *innerWindow = get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
&retx, &rety);
nsRefPtr<nsWindow> innerMostWidget = get_window_for_gdk_window(innerWindow);
if (!innerMostWidget)
innerMostWidget = this;
// clear any drag leave timer that might be pending so that it
// doesn't get processed when we actually go out to get data.
if (mDragLeaveTimer) {
mDragLeaveTimer->Cancel();
mDragLeaveTimer = nsnull;
}
CheckNeedDragLeave(innerMostWidget, dragService, aDragContext, retx, rety);
// http://www.whatwg.org/specs/web-apps/current-work/multipage/dnd.html#drag-and-drop-processing-model
// (as at 27 December 2010) indicates that a "drop" event should only be
// fired (at the current target element) if the current drag operation is
// not none. The current drag operation will only be set to a non-none
// value during a "dragover" event.
//
// If the user has ended the drag before any dragover events have been
// sent, then the spec recommends skipping the drop (because the current
// drag operation is none). However, here we assume that, by releasing
// the mouse button, the user has indicated that they want to drop, so we
// proceed with the drop where possible.
//
// In order to make the events appear to content in the same way as if the
// spec is being followed we make sure to dispatch a "dragover" event with
// appropriate coordinates and check canDrop before the "drop" event.
//
// When the Xdnd protocol is used for source/destination communication (as
// should be the case with GTK source applications) a dragover event
// should have already been sent during the drag-motion signal, which
// would have already been received because XdndDrop messages do not
// contain a position. However, we can't assume the same when the Motif
// protocol is used.
dragServiceGTK->TargetSetLastContext(aWidget, aDragContext, aTime);
innerMostWidget->
DispatchDragMotionEvents(dragServiceGTK, nsIntPoint(retx, rety), aTime);
gboolean success = innerMostWidget->
DispatchDragDropEvent(dragServiceGTK, nsIntPoint(retx, rety), aTime);
// before we unset the context we need to do a drop_finish
gdk_drop_finish(aDragContext, success, aTime);
// after a drop takes place we need to make sure that the drag
// service doesn't think that it still has a context. if the other
// way ( besides the drop ) to end a drag event is during the leave
// event and and that case is handled in that handler.
dragServiceGTK->TargetSetLastContext(0, 0, 0);
// clear the sLastDragMotion window
sLastDragMotionWindow = 0;
// Make sure to end the drag session. If this drag started in a
// different app, we won't get a drag_end signal to end it from.
dragService->EndDragSession(true);
return TRUE;
}
void void
nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget, nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
GdkDragContext *aDragContext, GdkDragContext *aDragContext,
@ -5910,7 +5711,7 @@ nsWindow::InitDragEvent(nsDragEvent &aEvent)
// drag context. Gtk gets this from a combination of the key settings // drag context. Gtk gets this from a combination of the key settings
// and what the source is offering. // and what the source is offering.
void /* static */ void
nsWindow::UpdateDragStatus(GdkDragContext *aDragContext, nsWindow::UpdateDragStatus(GdkDragContext *aDragContext,
nsIDragService *aDragService) nsIDragService *aDragService)
{ {
@ -5973,9 +5774,24 @@ drag_motion_event_cb(GtkWidget *aWidget,
} }
} }
return window->OnDragMotionEvent(aWidget, // figure out which internal widget this drag motion actually happened on
aDragContext, nscoord retx = 0;
aX, aY, aTime, aData); nscoord rety = 0;
GdkWindow *innerWindow =
get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
&retx, &rety);
nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
if (!innerMostWindow) {
innerMostWindow = window;
}
LOGDRAG(("nsWindow drag-motion signal for %p\n", (void*)innerMostWindow));
return nsDragService::GetInstance()->
ScheduleMotionEvent(innerMostWindow, aDragContext,
nsIntPoint(aX, aY), aTime);
} }
static void static void
@ -5988,7 +5804,27 @@ drag_leave_event_cb(GtkWidget *aWidget,
if (!window) if (!window)
return; return;
window->OnDragLeaveEvent(aWidget, aDragContext, aTime, aData); nsDragService *dragService = nsDragService::GetInstance();
nsWindow *mostRecentDragWindow = dragService->GetMostRecentDestWindow();
if (!mostRecentDragWindow) {
NS_WARNING("Spurious drag leave signal");
return;
}
GtkWidget *mozContainer = mostRecentDragWindow->GetMozContainerWidget();
if (aWidget != mozContainer)
{
// When the drag moves between widgets, GTK can send leave signal for
// the old widget after the motion or drop signal for the new widget.
// We'll send the leave event when the motion or drop event is run.
return;
}
LOGDRAG(("nsWindow drag-leave signal for %p\n",
(void*)mostRecentDragWindow));
dragService->ScheduleLeaveEvent();
} }
@ -6004,9 +5840,24 @@ drag_drop_event_cb(GtkWidget *aWidget,
if (!window) if (!window)
return FALSE; return FALSE;
return window->OnDragDropEvent(aWidget, // figure out which internal widget this drag motion actually happened on
aDragContext, nscoord retx = 0;
aX, aY, aTime, aData); nscoord rety = 0;
GdkWindow *innerWindow =
get_inner_gdk_window(gtk_widget_get_window(aWidget), aX, aY,
&retx, &rety);
nsRefPtr<nsWindow> innerMostWindow = get_window_for_gdk_window(innerWindow);
if (!innerMostWindow) {
innerMostWindow = window;
}
LOGDRAG(("nsWindow drag-drop signal for %p\n", (void*)innerMostWindow));
return nsDragService::GetInstance()->
ScheduleDropEvent(innerMostWindow, aDragContext,
nsIntPoint(aX, aY), aTime);
} }
static void static void
@ -6041,30 +5892,6 @@ initialize_prefs(void)
return NS_OK; return NS_OK;
} }
void
nsWindow::FireDragLeaveTimer(void)
{
LOGDRAG(("nsWindow::FireDragLeaveTimer(%p)\n", (void*)this));
mDragLeaveTimer = nsnull;
// clean up any pending drag motion window info
if (sLastDragMotionWindow) {
nsRefPtr<nsWindow> kungFuDeathGrip = sLastDragMotionWindow;
// send our leave signal
sLastDragMotionWindow->OnDragLeave();
sLastDragMotionWindow = 0;
}
}
/* static */
void
nsWindow::DragLeaveTimerCallback(nsITimer *aTimer, void *aClosure)
{
nsRefPtr<nsWindow> window = static_cast<nsWindow *>(aClosure);
window->FireDragLeaveTimer();
}
static GdkWindow * static GdkWindow *
get_inner_gdk_window (GdkWindow *aWindow, get_inner_gdk_window (GdkWindow *aWindow,
gint x, gint y, gint x, gint y,

View File

@ -237,22 +237,6 @@ public:
GdkEventVisibility *aEvent); GdkEventVisibility *aEvent);
void OnWindowStateEvent(GtkWidget *aWidget, void OnWindowStateEvent(GtkWidget *aWidget,
GdkEventWindowState *aEvent); GdkEventWindowState *aEvent);
gboolean OnDragMotionEvent(GtkWidget *aWidget,
GdkDragContext *aDragContext,
gint aX,
gint aY,
guint aTime,
gpointer aData);
void OnDragLeaveEvent(GtkWidget * aWidget,
GdkDragContext *aDragContext,
guint aTime,
gpointer aData);
gboolean OnDragDropEvent(GtkWidget *aWidget,
GdkDragContext *aDragContext,
gint aX,
gint aY,
guint aTime,
gpointer aData);
void OnDragDataReceivedEvent(GtkWidget *aWidget, void OnDragDataReceivedEvent(GtkWidget *aWidget,
GdkDragContext *aDragContext, GdkDragContext *aDragContext,
gint aX, gint aX,
@ -297,11 +281,6 @@ public:
void ThemeChanged(void); void ThemeChanged(void);
void CheckNeedDragLeave(nsWindow* aInnerMostWidget,
nsIDragService* aDragService,
GdkDragContext *aDragContext,
nscoord aX, nscoord aY);
#ifdef MOZ_X11 #ifdef MOZ_X11
Window mOldFocusWindow; Window mOldFocusWindow;
#endif /* MOZ_X11 */ #endif /* MOZ_X11 */
@ -312,6 +291,9 @@ public:
NS_IMETHOD BeginMoveDrag(nsMouseEvent* aEvent); NS_IMETHOD BeginMoveDrag(nsMouseEvent* aEvent);
MozContainer* GetMozContainer() { return mContainer; } MozContainer* GetMozContainer() { return mContainer; }
// GetMozContainerWidget returns the MozContainer even for undestroyed
// descendant windows
GtkWidget* GetMozContainerWidget();
GdkWindow* GetGdkWindow() { return mGdkWindow; } GdkWindow* GetGdkWindow() { return mGdkWindow; }
bool IsDestroyed() { return mIsDestroyed; } bool IsDestroyed() { return mIsDestroyed; }
@ -324,6 +306,8 @@ public:
gboolean DispatchDragDropEvent(nsDragService *aDragService, gboolean DispatchDragDropEvent(nsDragService *aDragService,
const nsIntPoint& aWindowPoint, const nsIntPoint& aWindowPoint,
guint aTime); guint aTime);
static void UpdateDragStatus (GdkDragContext *aDragContext,
nsIDragService *aDragService);
// If this dispatched the keydown event actually, this returns TRUE, // If this dispatched the keydown event actually, this returns TRUE,
// otherwise, FALSE. // otherwise, FALSE.
bool DispatchKeyDownEvent(GdkEventKey *aEvent, bool DispatchKeyDownEvent(GdkEventKey *aEvent,
@ -393,7 +377,6 @@ protected:
private: private:
void DestroyChildWindows(); void DestroyChildWindows();
void GetToplevelWidget(GtkWidget **aWidget); void GetToplevelWidget(GtkWidget **aWidget);
GtkWidget *GetMozContainerWidget();
nsWindow *GetContainerWindow(); nsWindow *GetContainerWindow();
void SetUrgencyHint(GtkWidget *top_window, bool state); void SetUrgencyHint(GtkWidget *top_window, bool state);
void *SetupPluginPort(void); void *SetupPluginPort(void);
@ -493,13 +476,8 @@ private:
gchar* mTransparencyBitmap; gchar* mTransparencyBitmap;
// all of our DND stuff // all of our DND stuff
// this is the last window that had a drag event happen on it.
static nsWindow *sLastDragMotionWindow;
void InitDragEvent (nsDragEvent &aEvent); void InitDragEvent (nsDragEvent &aEvent);
void UpdateDragStatus (GdkDragContext *aDragContext,
nsIDragService *aDragService);
nsCOMPtr<nsITimer> mDragLeaveTimer;
float mLastMotionPressure; float mLastMotionPressure;
// Remember the last sizemode so that we can restore it when // Remember the last sizemode so that we can restore it when
@ -508,9 +486,6 @@ private:
static bool DragInProgress(void); static bool DragInProgress(void);
void FireDragLeaveTimer (void);
static void DragLeaveTimerCallback (nsITimer *aTimer, void *aClosure);
void DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent); void DispatchMissedButtonReleases(GdkEventCrossing *aGdkEvent);
/** /**