Bug 375681, use drag event names from what-wg spec, also implement new drag and dragend events, r=smaug,sr=roc
authorenndeakin@sympatico.ca
Wed, 11 Apr 2007 21:37:39 -0700
changeset 482 1b0e34df9fa8707a0eb305120307fa4306f6184d
parent 481 8c12d40522343d60ac92a3956fdccd7edfb3c4c9
child 483 0eeb0ec73c4fb5d01782b7c8adf58c284ec0fd46
push idunknown
push userunknown
push dateunknown
reviewerssmaug, roc
bugs375681
milestone1.9a4pre
Bug 375681, use drag event names from what-wg spec, also implement new drag and dragend events, r=smaug,sr=roc
content/base/src/nsContentAreaDragDrop.cpp
content/base/src/nsContentAreaDragDrop.h
content/base/src/nsGkAtomList.h
content/base/src/nsHTMLContentSerializer.cpp
content/events/src/nsDOMEvent.cpp
content/events/src/nsDOMEvent.h
content/events/src/nsEventListenerManager.cpp
content/events/src/nsEventStateManager.cpp
content/events/src/nsEventStateManager.h
dom/public/coreEvents/nsIDOMDragListener.h
editor/libeditor/text/nsEditorEventListeners.cpp
editor/libeditor/text/nsEditorEventListeners.h
layout/generic/nsObjectFrame.cpp
widget/public/nsGUIEvent.h
widget/public/nsIDragService.idl
widget/src/beos/nsDragService.cpp
widget/src/beos/nsDragService.h
widget/src/beos/nsWindow.cpp
widget/src/cocoa/nsChildView.mm
widget/src/cocoa/nsDragService.mm
widget/src/gtk/nsDragService.cpp
widget/src/gtk/nsDragService.h
widget/src/gtk/nsWindow.cpp
widget/src/gtk2/nsDragService.cpp
widget/src/gtk2/nsDragService.h
widget/src/gtk2/nsWindow.cpp
widget/src/mac/nsDragHelperService.cpp
widget/src/mac/nsDragService.cpp
widget/src/os2/nsDragService.cpp
widget/src/os2/nsDragService.h
widget/src/os2/nsWindow.cpp
widget/src/photon/nsWidget.cpp
widget/src/qt/nsDragService.cpp
widget/src/qt/nsDragService.h
widget/src/windows/nsDragService.cpp
widget/src/windows/nsDragService.h
widget/src/windows/nsNativeDragTarget.cpp
widget/src/xlib/nsAppShell.cpp
widget/src/xlib/nsDragService.cpp
widget/src/xlib/nsDragService.h
widget/src/xpwidgets/nsBaseDragService.cpp
--- a/content/base/src/nsContentAreaDragDrop.cpp
+++ b/content/base/src/nsContentAreaDragDrop.cpp
@@ -390,16 +390,32 @@ nsContentAreaDragDrop::DragOver(nsIDOMEv
 NS_IMETHODIMP
 nsContentAreaDragDrop::DragExit(nsIDOMEvent* aMouseEvent)
 {
   // nothing really to do here.
   return NS_OK;
 }
 
 
+NS_IMETHODIMP
+nsContentAreaDragDrop::Drag(nsIDOMEvent* aMouseEvent)
+{
+  // nothing really to do here.
+  return NS_OK;
+}
+
+
+NS_IMETHODIMP
+nsContentAreaDragDrop::DragEnd(nsIDOMEvent* aMouseEvent)
+{
+  // nothing really to do here.
+  return NS_OK;
+}
+
+
 //
 // ExtractURLFromData
 //
 // build up a url from whatever data we get from the OS. How we
 // interpret the data depends on the flavor as it tells us the
 // nsISupports* primitive type we have.
 //
 void
--- a/content/base/src/nsContentAreaDragDrop.h
+++ b/content/base/src/nsContentAreaDragDrop.h
@@ -88,16 +88,18 @@ public:
   virtual ~nsContentAreaDragDrop();
 
   // nsIDOMDragListener
   NS_IMETHOD DragEnter(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD DragOver(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD DragExit(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD DragDrop(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD DragGesture(nsIDOMEvent* aMouseEvent);
+  NS_IMETHOD Drag(nsIDOMEvent* aMouseEvent);
+  NS_IMETHOD DragEnd(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD HandleEvent(nsIDOMEvent *event);
 
 private:
 
   // Add/remove the relevant listeners
   nsresult AddDragListener();
   nsresult RemoveDragListener();
 
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -273,24 +273,29 @@ GK_ATOM(doctypeSystem, "doctype-system")
 GK_ATOM(document, "document")
 GK_ATOM(DOMAttrModified, "DOMAttrModified")
 GK_ATOM(DOMCharacterDataModified, "DOMCharacterDataModified")
 GK_ATOM(DOMNodeInserted, "DOMNodeInserted")
 GK_ATOM(DOMNodeInsertedIntoDocument, "DOMNodeInsertedInfoDocument")
 GK_ATOM(DOMNodeRemoved, "DOMNodeRemoved")
 GK_ATOM(DOMNodeRemovedFromDocument, "DOMNodeRemovedFromDocument")
 GK_ATOM(DOMSubtreeModified, "DOMSubtreeModified")
+GK_ATOM(drag, "drag")
 GK_ATOM(dragdrop, "dragdrop")
+GK_ATOM(dragend, "dragend")
 GK_ATOM(dragenter, "dragenter")
 GK_ATOM(dragevent, "dragevent")
 GK_ATOM(dragexit, "dragexit")
 GK_ATOM(draggesture, "draggesture")
 GK_ATOM(dragging, "dragging")
+GK_ATOM(dragleave, "dragleave")
 GK_ATOM(dragover, "dragover")
 GK_ATOM(dragSession, "dragSession")
+GK_ATOM(dragstart, "dragstart")
+GK_ATOM(drop, "drop")
 GK_ATOM(dropAfter, "dropAfter")
 GK_ATOM(dropBefore, "dropBefore")
 GK_ATOM(dropOn, "dropOn")
 GK_ATOM(dt, "dt")
 GK_ATOM(editable, "editable")
 GK_ATOM(editing, "editing")
 GK_ATOM(editor, "editor")
 GK_ATOM(editorDisplayList, "EditorDisplay-List")
@@ -554,21 +559,26 @@ GK_ATOM(onDOMCharacterDataModified, "onD
 GK_ATOM(onDOMFocusIn, "onDOMFocusIn")
 GK_ATOM(onDOMFocusOut, "onDOMFocusOut")
 GK_ATOM(onDOMMouseScroll, "onDOMMouseScroll")
 GK_ATOM(onDOMNodeInserted, "onDOMNodeInserted")
 GK_ATOM(onDOMNodeInsertedIntoDocument, "onDOMNodeInsertedIntoDocument")
 GK_ATOM(onDOMNodeRemoved, "onDOMNodeRemoved")
 GK_ATOM(onDOMNodeRemovedFromDocument, "onDOMNodeRemovedFromDocument")
 GK_ATOM(onDOMSubtreeModified, "onDOMSubtreeModified")
+GK_ATOM(ondrag, "ondrag")
 GK_ATOM(ondragdrop, "ondragdrop")
+GK_ATOM(ondragend, "ondragend")
 GK_ATOM(ondragenter, "ondragenter")
 GK_ATOM(ondragexit, "ondragexit")
 GK_ATOM(ondraggesture, "ondraggesture")
+GK_ATOM(ondragleave, "ondragleave")
 GK_ATOM(ondragover, "ondragover")
+GK_ATOM(ondragstart, "ondragstart")
+GK_ATOM(ondrop, "ondrop")
 GK_ATOM(onerror, "onerror")
 GK_ATOM(onfocus, "onfocus")
 GK_ATOM(onget, "onget")
 GK_ATOM(oninput, "oninput")
 GK_ATOM(onkeydown, "onkeydown")
 GK_ATOM(onkeypress, "onkeypress")
 GK_ATOM(onkeyup, "onkeyup")
 GK_ATOM(onLoad, "onLoad")
--- a/content/base/src/nsHTMLContentSerializer.cpp
+++ b/content/base/src/nsHTMLContentSerializer.cpp
@@ -467,17 +467,20 @@ nsHTMLContentSerializer::IsJavaScript(ns
               || (aAttrNameAtom == nsGkAtoms::onsubmit)    || (aAttrNameAtom == nsGkAtoms::onunload)
               || (aAttrNameAtom == nsGkAtoms::onabort)     || (aAttrNameAtom == nsGkAtoms::onerror)
               || (aAttrNameAtom == nsGkAtoms::onpaint)     || (aAttrNameAtom == nsGkAtoms::onresize)
               || (aAttrNameAtom == nsGkAtoms::onscroll)    || (aAttrNameAtom == nsGkAtoms::onbroadcast)
               || (aAttrNameAtom == nsGkAtoms::onclose)     || (aAttrNameAtom == nsGkAtoms::oncontextmenu)
               || (aAttrNameAtom == nsGkAtoms::oncommand)   || (aAttrNameAtom == nsGkAtoms::oncommandupdate)
               || (aAttrNameAtom == nsGkAtoms::ondragdrop)  || (aAttrNameAtom == nsGkAtoms::ondragenter)
               || (aAttrNameAtom == nsGkAtoms::ondragexit)  || (aAttrNameAtom == nsGkAtoms::ondraggesture)
-              || (aAttrNameAtom == nsGkAtoms::ondragover)  || (aAttrNameAtom == nsGkAtoms::oninput);
+              || (aAttrNameAtom == nsGkAtoms::ondragover)  || (aAttrNameAtom == nsGkAtoms::ondragstart)
+              || (aAttrNameAtom == nsGkAtoms::ondragleave) || (aAttrNameAtom == nsGkAtoms::ondrop)
+              || (aAttrNameAtom == nsGkAtoms::ondragend)   || (aAttrNameAtom == nsGkAtoms::ondrag)
+              || (aAttrNameAtom == nsGkAtoms::oninput);
   return result;
 }
 
 nsresult 
 nsHTMLContentSerializer::EscapeURI(const nsAString& aURI, nsAString& aEscapedURI)
 {
   // URL escape %xx cannot be used in JS.
   // No escaping if the scheme is 'javascript'.
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -58,18 +58,19 @@
 
 static const char* const sEventNames[] = {
   "mousedown", "mouseup", "click", "dblclick", "mouseover",
   "mouseout", "mousemove", "contextmenu", "keydown", "keyup", "keypress",
   "focus", "blur", "load", "beforeunload", "unload", "abort", "error",
   "submit", "reset", "change", "select", "input", "paint" ,"text",
   "compositionstart", "compositionend", "popupshowing", "popupshown",
   "popuphiding", "popuphidden", "close", "command", "broadcast", "commandupdate",
-  "dragenter", "dragover", "dragexit", "dragdrop", "draggesture", "resize",
-  "scroll","overflow", "underflow", "overflowchanged",
+  "dragenter", "dragover", "dragexit", "dragdrop", "draggesture",
+  "drag", "dragend", "dragstart", "dragleave", "drop", "resize",
+  "scroll", "overflow", "underflow", "overflowchanged",
   "DOMSubtreeModified", "DOMNodeInserted", "DOMNodeRemoved", 
   "DOMNodeRemovedFromDocument", "DOMNodeInsertedIntoDocument",
   "DOMAttrModified", "DOMCharacterDataModified",
   "DOMActivate", "DOMFocusIn", "DOMFocusOut",
   "pageshow", "pagehide", "DOMMouseScroll"
 #ifdef MOZ_SVG
  ,
   "SVGLoad", "SVGUnload", "SVGAbort", "SVGError", "SVGResize", "SVGScroll",
@@ -1172,20 +1173,30 @@ const char* nsDOMEvent::GetEventName(PRU
   case NS_XUL_COMMAND_UPDATE:
     return sEventNames[eDOMEvents_commandupdate];
   case NS_DRAGDROP_ENTER:
     return sEventNames[eDOMEvents_dragenter];
   case NS_DRAGDROP_OVER_SYNTH:
     return sEventNames[eDOMEvents_dragover];
   case NS_DRAGDROP_EXIT_SYNTH:
     return sEventNames[eDOMEvents_dragexit];
-  case NS_DRAGDROP_DROP:
+  case NS_DRAGDROP_DRAGDROP:
     return sEventNames[eDOMEvents_dragdrop];
   case NS_DRAGDROP_GESTURE:
     return sEventNames[eDOMEvents_draggesture];
+  case NS_DRAGDROP_DRAG:
+    return sEventNames[eDOMEvents_drag];
+  case NS_DRAGDROP_END:
+    return sEventNames[eDOMEvents_dragend];
+  case NS_DRAGDROP_START:
+    return sEventNames[eDOMEvents_dragstart];
+  case NS_DRAGDROP_LEAVE_SYNTH:
+    return sEventNames[eDOMEvents_dragleave];
+  case NS_DRAGDROP_DROP:
+    return sEventNames[eDOMEvents_drop];
   case NS_SCROLLPORT_OVERFLOW:
     return sEventNames[eDOMEvents_overflow];
   case NS_SCROLLPORT_UNDERFLOW:
     return sEventNames[eDOMEvents_underflow];
   case NS_SCROLLPORT_OVERFLOWCHANGED:
     return sEventNames[eDOMEvents_overflowchanged];
   case NS_MUTATION_SUBTREEMODIFIED:
     return sEventNames[eDOMEvents_subtreemodified];
--- a/content/events/src/nsDOMEvent.h
+++ b/content/events/src/nsDOMEvent.h
@@ -102,16 +102,21 @@ public:
     eDOMEvents_command,
     eDOMEvents_broadcast,
     eDOMEvents_commandupdate,
     eDOMEvents_dragenter,
     eDOMEvents_dragover,
     eDOMEvents_dragexit,
     eDOMEvents_dragdrop,
     eDOMEvents_draggesture,
+    eDOMEvents_drag,
+    eDOMEvents_dragend,
+    eDOMEvents_dragstart,
+    eDOMEvents_dragleave,
+    eDOMEvents_drop,
     eDOMEvents_resize,
     eDOMEvents_scroll,
     eDOMEvents_overflow,
     eDOMEvents_underflow,
     eDOMEvents_overflowchanged,
     eDOMEvents_subtreemodified,
     eDOMEvents_nodeinserted,
     eDOMEvents_noderemoved,
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -256,21 +256,26 @@ static const EventDispatchData sFormEven
 static const EventDispatchData sLoadEvents[] = {
   { NS_LOAD,               HANDLER(&nsIDOMLoadListener::Load)         },
   { NS_PAGE_UNLOAD,        HANDLER(&nsIDOMLoadListener::Unload)       },
   { NS_LOAD_ERROR,         HANDLER(&nsIDOMLoadListener::Error)        },
   { NS_BEFORE_PAGE_UNLOAD, HANDLER(&nsIDOMLoadListener::BeforeUnload) }
 };
 
 static const EventDispatchData sDragEvents[] = {
-  { NS_DRAGDROP_ENTER,      HANDLER(&nsIDOMDragListener::DragEnter)   },
-  { NS_DRAGDROP_OVER_SYNTH, HANDLER(&nsIDOMDragListener::DragOver)    },
-  { NS_DRAGDROP_EXIT_SYNTH, HANDLER(&nsIDOMDragListener::DragExit)    },
-  { NS_DRAGDROP_DROP,       HANDLER(&nsIDOMDragListener::DragDrop)    },
-  { NS_DRAGDROP_GESTURE,    HANDLER(&nsIDOMDragListener::DragGesture) }
+  { NS_DRAGDROP_ENTER,       HANDLER(&nsIDOMDragListener::DragEnter)   },
+  { NS_DRAGDROP_OVER_SYNTH,  HANDLER(&nsIDOMDragListener::DragOver)    },
+  { NS_DRAGDROP_EXIT_SYNTH,  HANDLER(&nsIDOMDragListener::DragExit)    },
+  { NS_DRAGDROP_DRAGDROP,    HANDLER(&nsIDOMDragListener::DragDrop)    },
+  { NS_DRAGDROP_GESTURE,     HANDLER(&nsIDOMDragListener::DragGesture) },
+  { NS_DRAGDROP_DRAG,        HANDLER(&nsIDOMDragListener::Drag)        },
+  { NS_DRAGDROP_END,         HANDLER(&nsIDOMDragListener::DragEnd)     },
+  { NS_DRAGDROP_START,       HANDLER(&nsIDOMDragListener::DragStart)   },
+  { NS_DRAGDROP_LEAVE_SYNTH, HANDLER(&nsIDOMDragListener::DragLeave)   },
+  { NS_DRAGDROP_DROP,        HANDLER(&nsIDOMDragListener::Drop)        },
 };
 
 static const EventDispatchData sXULEvents[] = {
   { NS_XUL_POPUP_SHOWING,  HANDLER(&nsIDOMXULListener::PopupShowing)  },
   { NS_XUL_POPUP_SHOWN,    HANDLER(&nsIDOMXULListener::PopupShown)    },
   { NS_XUL_POPUP_HIDING,   HANDLER(&nsIDOMXULListener::PopupHiding)   },
   { NS_XUL_POPUP_HIDDEN,   HANDLER(&nsIDOMXULListener::PopupHidden)   },
   { NS_XUL_CLOSE,          HANDLER(&nsIDOMXULListener::Close)         },
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -1711,41 +1711,53 @@ nsEventStateManager::GenerateDragGesture
       KillClickHoldTimer();
 #endif
 
       nsCOMPtr<nsIContent> targetContent = mGestureDownContent;
       // Stop tracking the drag gesture now. This should stop us from
       // reentering GenerateDragGesture inside DOM event processing.
       StopTrackingDragGesture();
 
+      nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetWindow();
+
       // get the widget from the target frame
-      nsEventStatus status = nsEventStatus_eIgnore;
-      nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_GESTURE,
-                         mCurrentTarget->GetWindow(), nsMouseEvent::eReal);
-      FillInEventFromGestureDown(&event);
+      nsMouseEvent startEvent(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_START,
+                              widget, nsMouseEvent::eReal);
+      FillInEventFromGestureDown(&startEvent);
+
+      nsMouseEvent gestureEvent(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_GESTURE,
+                                widget, nsMouseEvent::eReal);
+      FillInEventFromGestureDown(&gestureEvent);
 
       // Dispatch to the DOM. By setting mCurrentTarget we are faking
       // out the ESM and telling it that the current target frame is
       // actually where the mouseDown occurred, otherwise it will use
       // the frame the mouse is currently over which may or may not be
       // the same. (Note: saari and I have decided that we don't have
       // to reset |mCurrentTarget| when we're through because no one
       // else is doing anything more with this event and it will get
       // reset on the very next event to the correct frame).
 
       // Hold onto old target content through the event and reset after.
       nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
 
       // Set the current target to the content for the mouse down
       mCurrentTargetContent = targetContent;
 
-      // Dispatch to DOM
-      nsEventDispatcher::Dispatch(targetContent, aPresContext, &event, nsnull,
+      // Dispatch both the dragstart and draggesture events to the DOM
+      nsEventStatus status = nsEventStatus_eIgnore;
+      nsEventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nsnull,
                                   &status);
 
+      if (status != nsEventStatus_eConsumeNoDefault) {
+        status = nsEventStatus_eIgnore;
+        nsEventDispatcher::Dispatch(targetContent, aPresContext, &gestureEvent, nsnull,
+                                    &status);
+      }
+
       // Note that frame event handling doesn't care about NS_DRAGDROP_GESTURE,
       // which is just as well since we don't really know which frame to
       // send it to
 
       // Reset mCurretTargetContent to what it was
       mCurrentTargetContent = targetBeforeEvent;
     }
 
@@ -2218,16 +2230,44 @@ nsEventStateManager::PostHandleEvent(nsP
       }
       *aStatus = nsEventStatus_eConsumeNoDefault;
 
     }
 
     break;
 
   case NS_DRAGDROP_DROP:
+    {
+      // now fire the dragdrop event, for compatibility with XUL
+      if (mCurrentTarget) {
+        nsCOMPtr<nsIContent> targetContent;
+        mCurrentTarget->GetContentForEvent(presContext, aEvent,
+                                           getter_AddRefs(targetContent));
+
+        nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetWindow();
+        nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_DRAGDROP,
+                           widget, nsMouseEvent::eReal);
+
+        nsMouseEvent* mouseEvent = NS_STATIC_CAST(nsMouseEvent*, aEvent);
+        event.refPoint = mouseEvent->refPoint;
+        event.isShift = mouseEvent->isShift;
+        event.isControl = mouseEvent->isControl;
+        event.isAlt = mouseEvent->isAlt;
+        event.isMeta = mouseEvent->isMeta;
+
+        nsEventStatus status = nsEventStatus_eIgnore;
+        nsCOMPtr<nsIPresShell> presShell = mPresContext->GetPresShell();
+        if (presShell) {
+          presShell->HandleEventWithTarget(&event, mCurrentTarget,
+                                           targetContent, &status);
+        }
+      }
+      // fall through and call GenerateDragDropEnterExit
+    }
+
   case NS_DRAGDROP_EXIT:
     // clean up after ourselves. make sure we do this _after_ the event, else we'll
     // clean up too early!
     GenerateDragDropEnterExit(presContext, (nsGUIEvent*)aEvent);
     break;
 
   case NS_KEY_UP:
     break;
@@ -2839,134 +2879,96 @@ nsEventStateManager::GenerateDragDropEnt
     {
       if (mLastDragOverFrame != mCurrentTarget) {
         //We'll need the content, too, to check if it changed separately from the frames.
         nsCOMPtr<nsIContent> lastContent;
         nsCOMPtr<nsIContent> targetContent;
         mCurrentTarget->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(targetContent));
 
         if ( mLastDragOverFrame ) {
-          //fire drag exit
-          nsEventStatus status = nsEventStatus_eIgnore;
-          nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent),
-                             NS_DRAGDROP_EXIT_SYNTH, aEvent->widget,
-                             nsMouseEvent::eReal);
-          event.refPoint = aEvent->refPoint;
-          event.isShift = ((nsMouseEvent*)aEvent)->isShift;
-          event.isControl = ((nsMouseEvent*)aEvent)->isControl;
-          event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
-          event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
-          event.relatedTarget = targetContent;
-
-          //The frame has change but the content may not have.  Check before dispatching to content
+          //The frame has changed but the content may not have. Check before dispatching to content
           mLastDragOverFrame->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(lastContent));
 
-          mCurrentTargetContent = lastContent;
-
-          if ( lastContent != targetContent ) {
-            //XXX This event should still go somewhere!!
-            if (lastContent)
-              nsEventDispatcher::Dispatch(lastContent, aPresContext, &event,
-                                          nsnull, &status);
-
-            // clear the drag hover
-            if (status != nsEventStatus_eConsumeNoDefault )
-              SetContentState(nsnull, NS_EVENT_STATE_DRAGOVER);
-          }
-
-          // Finally dispatch exit to the frame
-          if ( mLastDragOverFrame ) {
-            mLastDragOverFrame->HandleEvent(aPresContext, &event, &status);
-
-          }
+          FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_LEAVE_SYNTH,
+                              targetContent, lastContent, mLastDragOverFrame);
+          FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_EXIT_SYNTH,
+                              targetContent, lastContent, mLastDragOverFrame);
         }
 
-        //fire drag enter
-        nsEventStatus status = nsEventStatus_eIgnore;
-        nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_ENTER,
-                           aEvent->widget, nsMouseEvent::eReal);
-        event.refPoint = aEvent->refPoint;
-        event.isShift = ((nsMouseEvent*)aEvent)->isShift;
-        event.isControl = ((nsMouseEvent*)aEvent)->isControl;
-        event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
-        event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
-        event.relatedTarget = lastContent;
-
-        mCurrentTargetContent = targetContent;
-
-        //The frame has change but the content may not have.  Check before dispatching to content
-        if (lastContent != targetContent) {
-          //XXX This event should still go somewhere!!
-          if (targetContent)
-            nsEventDispatcher::Dispatch(targetContent, aPresContext, &event,
-                                        nsnull, &status);
-
-          // set drag hover on this frame
-          if (status != nsEventStatus_eConsumeNoDefault)
-            SetContentState(targetContent, NS_EVENT_STATE_DRAGOVER);
-        }
-
-        // Finally dispatch to the frame
-        if (mCurrentTarget) {
-          //XXX Get the new frame
-          mCurrentTarget->HandleEvent(aPresContext, &event, &status);
-        }
+        FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_ENTER,
+                            lastContent, targetContent, mCurrentTarget);
 
         mLastDragOverFrame = mCurrentTarget;
       }
     }
     break;
 
   case NS_DRAGDROP_DROP:
   case NS_DRAGDROP_EXIT:
     {
       //This is actually the window mouse exit event.
       if ( mLastDragOverFrame ) {
-
-        // fire mouseout
-        nsEventStatus status = nsEventStatus_eIgnore;
-        nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_EXIT_SYNTH,
-                           aEvent->widget, nsMouseEvent::eReal);
-        event.refPoint = aEvent->refPoint;
-        event.isShift = ((nsMouseEvent*)aEvent)->isShift;
-        event.isControl = ((nsMouseEvent*)aEvent)->isControl;
-        event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
-        event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
-
-        // dispatch to content via DOM
         nsCOMPtr<nsIContent> lastContent;
         mLastDragOverFrame->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(lastContent));
 
-        mCurrentTargetContent = lastContent;
-
-        if (lastContent) {
-          nsEventDispatcher::Dispatch(lastContent, aPresContext, &event, nsnull,
-                                      &status);
-          if (status != nsEventStatus_eConsumeNoDefault)
-            SetContentState(nsnull, NS_EVENT_STATE_DRAGOVER);
-        }
-
-        // Finally dispatch to the frame
-        if ( mLastDragOverFrame ) {
-          //XXX Get the new frame
-          mLastDragOverFrame->HandleEvent(aPresContext, &event, &status);
-          mLastDragOverFrame = nsnull;
-        }
-     }
+        FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_LEAVE_SYNTH,
+                            nsnull, lastContent, mLastDragOverFrame);
+        FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_EXIT_SYNTH,
+                            nsnull, lastContent, mLastDragOverFrame);
+
+        mLastDragOverFrame = nsnull;
+      }
     }
     break;
   }
 
   //reset mCurretTargetContent to what it was
   mCurrentTargetContent = targetBeforeEvent;
 
   // Now flush all pending notifications, for better responsiveness.
   FlushPendingEvents(aPresContext);
 }
 
+void
+nsEventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext,
+                                         nsGUIEvent* aEvent,
+                                         PRUint32 aMsg,
+                                         nsIContent* aRelatedTarget,
+                                         nsIContent* aTargetContent,
+                                         nsWeakFrame& aTargetFrame)
+{
+  nsEventStatus status = nsEventStatus_eIgnore;
+  nsMouseEvent event(NS_IS_TRUSTED_EVENT(aEvent), aMsg,
+                     aEvent->widget, nsMouseEvent::eReal);
+  event.refPoint = aEvent->refPoint;
+  event.isShift = ((nsMouseEvent*)aEvent)->isShift;
+  event.isControl = ((nsMouseEvent*)aEvent)->isControl;
+  event.isAlt = ((nsMouseEvent*)aEvent)->isAlt;
+  event.isMeta = ((nsMouseEvent*)aEvent)->isMeta;
+  event.relatedTarget = aRelatedTarget;
+
+  mCurrentTargetContent = aTargetContent;
+
+  if (aTargetContent != aRelatedTarget) {
+    //XXX This event should still go somewhere!!
+    if (aTargetContent)
+      nsEventDispatcher::Dispatch(aTargetContent, aPresContext, &event,
+                                  nsnull, &status);
+
+    // adjust the drag hover
+    if (status != nsEventStatus_eConsumeNoDefault)
+      SetContentState((aMsg == NS_DRAGDROP_ENTER) ? aTargetContent : nsnull,
+                      NS_EVENT_STATE_DRAGOVER);
+  }
+
+  // Finally dispatch the event to the frame
+  if (aTargetFrame)
+    aTargetFrame->HandleEvent(aPresContext, &event, &status);
+}
+
 nsresult
 nsEventStateManager::SetClickCount(nsPresContext* aPresContext,
                                    nsMouseEvent *aEvent,
                                    nsEventStatus* aStatus)
 {
   nsCOMPtr<nsIContent> mouseContent;
   mCurrentTarget->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(mouseContent));
 
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -189,16 +189,30 @@ protected:
    * @param aEvent the event that triggered the mouseout
    * @param aMovingInto the content node we've moved into.  This is used to set
    *        the relatedTarget for mouseout events.  Also, if it's non-null
    *        NotifyMouseOut will NOT change the current hover content to null;
    *        in that case the caller is responsible for updating hover state.
    */
   void NotifyMouseOut(nsGUIEvent* aEvent, nsIContent* aMovingInto);
   void GenerateDragDropEnterExit(nsPresContext* aPresContext, nsGUIEvent* aEvent);
+  /**
+   * Fire the dragenter and dragexit/dragleave events when the mouse moves to a
+   * new target.
+   *
+   * @param aRelatedTarget relatedTarget to set for the event
+   * @param aTargetContent target to set for the event
+   * @param aTargetFrame target frame for the event
+   */
+  void FireDragEnterOrExit(nsPresContext* aPresContext,
+                           nsGUIEvent* aEvent,
+                           PRUint32 aMsg,
+                           nsIContent* aRelatedTarget,
+                           nsIContent* aTargetContent,
+                           nsWeakFrame& aTargetFrame);
   nsresult SetClickCount(nsPresContext* aPresContext, nsMouseEvent *aEvent, nsEventStatus* aStatus);
   nsresult CheckForAndDispatchClick(nsPresContext* aPresContext, nsMouseEvent *aEvent, nsEventStatus* aStatus);
   nsresult GetNextTabbableContent(nsIContent* aRootContent,
                                   nsIContent* aStartContent,
                                   nsIFrame* aStartFrame,
                                   PRBool forward, PRBool ignoreTabIndex,
                                   nsIContent** aResultNode,
                                   nsIFrame** aResultFrame);
--- a/dom/public/coreEvents/nsIDOMDragListener.h
+++ b/dom/public/coreEvents/nsIDOMDragListener.h
@@ -38,62 +38,107 @@
 
 #ifndef nsIDOMDragListener_h__
 #define nsIDOMDragListener_h__
 
 #include "nsIDOMEvent.h"
 #include "nsIDOMEventListener.h"
 
 /*
- * Mouse up/down/move event listener
+ * The listener for drag events.
  *
+ * The reason for two events for the same operation are for compatibility
+ * between the WHAT-WG drag and drop spec and existing XUL code.
  */
 #define NS_IDOMDRAGLISTENER_IID \
-{ /* 6b8b25d0-ded5-11d1-bd85-00805f8ae3f4 */ \
-0x6b8b25d0, 0xded5, 0x11d1, \
-{0xbd, 0x85, 0x00, 0x80, 0x5f, 0x8a, 0xe3, 0xf4} }
+{ /* 1A107271-1E26-419A-BCF1-0A4CF7A66B45 */ \
+0x1a107271, 0x1e26, 0x419a, \
+{0xbc, 0xf1, 0x0a, 0x4c, 0xf7, 0xa6, 0x6b, 0x45} }
+
+
 
 class nsIDOMDragListener : public nsIDOMEventListener {
 
 public:
 
    NS_DECLARE_STATIC_IID_ACCESSOR(NS_IDOMDRAGLISTENER_IID)
 
   /**
-  * Processes a drag enter event
+  * The dragenter event is fired when the mouse is moved from one node onto
+  * another. The target is the node that the mouse is moved onto and the
+  * related target is the node that the mouse left.
+  *
   * @param aMouseEvent @see nsIDOMEvent.h 
   * @returns whether the event was consumed or ignored. @see nsresult
   */
   NS_IMETHOD DragEnter(nsIDOMEvent* aMouseEvent) = 0;
 
   /**
-  * Processes a drag over event
+  * The dragover event is fired at regular intervals (several times per second)
+  * while a drag is occuring. The target of this event is the node that the
+  * mouse is over.
+  *
   * @param aMouseEvent @see nsIDOMEvent.h 
   * @returns whether the event was consumed or ignored. @see nsresult
   */
   NS_IMETHOD DragOver(nsIDOMEvent* aMouseEvent) = 0;
 
   /**
-  * Processes a drag Exit event
+  * The dragleave event is fired when the mouse leaves a node for another
+  * node. The dragexit event is fired immediately afterwards which will
+  * call this method. The target is the node that the mouse left and the
+  * related target is the node that the mouse is entering. A dragenter
+  * event will be fired on the node that the mouse is entering after both
+  * the dragleave and dragexit event are fired.
+  *
   * @param aMouseEvent @see nsIDOMEvent.h 
   * @returns whether the event was consumed or ignored. @see nsresult
   */
   NS_IMETHOD DragExit(nsIDOMEvent* aMouseEvent) = 0;
 
   /**
-   * Processes a drag drop event
+   * The drop event will be fired on the node that the mouse is over once
+   * the drag is complete. The dragdrop event will be fired immediately
+   * afterwards which will call this method.
+   *
    * @param aMouseEvent @see nsIDOMEvent.h 
    * @returns whether the event was consumed or ignored. @see nsresult
    */
   NS_IMETHOD DragDrop(nsIDOMEvent* aMouseEvent) = 0;
   
   /**
-   * Processes a drag gesture event
+   * When the user begins a drag by pressing the mouse button and moving the
+   * mouse slightly, a dragstart event will be fired. Afterwards a draggesture
+   * event will be fired which will call this method.
+   *
    * @param aMouseEvent @see nsIDOMEvent.h 
    * @returns whether the event was consumed or ignored. @see nsresult
    */
   NS_IMETHOD DragGesture(nsIDOMEvent* aMouseEvent) = 0;
 
+  /**
+   * The dragend event is fired when a drag is finished, whether the data was
+   * dropped successfully or whether the drag was cancelled. The target of
+   * this event is the source node of the drag.
+   *
+   * @param aMouseEvent @see nsIDOMEvent.h 
+   * @returns whether the event was consumed or ignored. @see nsresult
+   */
+  NS_IMETHOD DragEnd(nsIDOMEvent* aMouseEvent) = 0;
+
+  /**
+   * The drag event is fired just before a dragover event is fired. The target
+   * of this event is the source node of the drag.
+   *
+   * @param aMouseEvent @see nsIDOMEvent.h 
+   * @returns whether the event was consumed or ignored. @see nsresult
+   */
+  NS_IMETHOD Drag(nsIDOMEvent* aMouseEvent) = 0;
+
+  // these methods are for compatibility 
+  NS_IMETHOD DragStart(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+  NS_IMETHOD DragLeave(nsIDOMEvent* aMouseEvent) { return NS_OK; }
+  NS_IMETHOD Drop(nsIDOMEvent* aMouseEvent) { return NS_OK; }
 };
 
 NS_DEFINE_STATIC_IID_ACCESSOR(nsIDOMDragListener, NS_IDOMDRAGLISTENER_IID)
 
 #endif // nsIDOMDragListener_h__
--- a/editor/libeditor/text/nsEditorEventListeners.cpp
+++ b/editor/libeditor/text/nsEditorEventListeners.cpp
@@ -667,16 +667,28 @@ nsTextEditorDragListener::DragDrop(nsIDO
     return NS_OK;
   }
 
   aMouseEvent->StopPropagation();
   aMouseEvent->PreventDefault();
   return mEditor->InsertFromDrop(aMouseEvent);
 }
 
+nsresult
+nsTextEditorDragListener::Drag(nsIDOMEvent* aDragEvent)
+{
+  return NS_OK;
+}
+
+nsresult
+nsTextEditorDragListener::DragEnd(nsIDOMEvent* aDragEvent)
+{
+  return NS_OK;
+}
+
 PRBool
 nsTextEditorDragListener::CanDrop(nsIDOMEvent* aEvent)
 {
   // if the target doc is read-only, we can't drop
   PRUint32 flags;
   if (NS_FAILED(mEditor->GetFlags(&flags)))
     return PR_FALSE;
 
--- a/editor/libeditor/text/nsEditorEventListeners.h
+++ b/editor/libeditor/text/nsEditorEventListeners.h
@@ -218,16 +218,18 @@ public:
 
 /*BEGIN implementations of dragevent handler interface*/
   NS_IMETHOD HandleEvent(nsIDOMEvent* aEvent);
   NS_IMETHOD DragEnter(nsIDOMEvent* aDragEvent);
   NS_IMETHOD DragOver(nsIDOMEvent* aDragEvent);
   NS_IMETHOD DragExit(nsIDOMEvent* aDragEvent);
   NS_IMETHOD DragDrop(nsIDOMEvent* aDragEvent);
   NS_IMETHOD DragGesture(nsIDOMEvent* aDragEvent);
+  NS_IMETHOD Drag(nsIDOMEvent* aDragEvent);
+  NS_IMETHOD DragEnd(nsIDOMEvent* aDragEvent);
 /*END implementations of dragevent handler interface*/
 
 protected:
 
   PRBool     CanDrop(nsIDOMEvent* aEvent);
   
 protected:
 
--- a/layout/generic/nsObjectFrame.cpp
+++ b/layout/generic/nsObjectFrame.cpp
@@ -321,16 +321,18 @@ public:
   NS_IMETHOD Blur(nsIDOMEvent * aFocusEvent);
   
   // nsIDOMDragListener interfaces
   NS_IMETHOD DragEnter(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD DragOver(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD DragExit(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD DragDrop(nsIDOMEvent* aMouseEvent);
   NS_IMETHOD DragGesture(nsIDOMEvent* aMouseEvent);
+  NS_IMETHOD Drag(nsIDOMEvent* aMouseEvent);
+  NS_IMETHOD DragEnd(nsIDOMEvent* aMouseEvent);
   
 
   nsresult Destroy();  
 
   //nsIEventListener interface
   nsEventStatus ProcessEvent(const nsGUIEvent & anEvent);
   
   void Paint(const nsRect& aDirtyRect, PRUint32 ndc = 0);
@@ -2761,16 +2763,38 @@ nsresult nsPluginInstanceOwner::DragGest
     // Let the plugin handle drag events.
     aMouseEvent->PreventDefault();
     aMouseEvent->StopPropagation();
   }
 
   return NS_OK;
 }
 
+nsresult nsPluginInstanceOwner::Drag(nsIDOMEvent* aMouseEvent)
+{
+  if (mInstance) {
+    // Let the plugin handle drag events.
+    aMouseEvent->PreventDefault();
+    aMouseEvent->StopPropagation();
+  }
+
+  return NS_OK;
+}
+
+nsresult nsPluginInstanceOwner::DragEnd(nsIDOMEvent* aMouseEvent)
+{
+  if (mInstance) {
+    // Let the plugin handle drag events.
+    aMouseEvent->PreventDefault();
+    aMouseEvent->StopPropagation();
+  }
+
+  return NS_OK;
+}
+
 
 
 /*=============== nsIKeyListener ======================*/
 nsresult nsPluginInstanceOwner::KeyDown(nsIDOMEvent* aKeyEvent)
 {
   return DispatchKeyToPlugin(aKeyEvent);
 }
 
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -240,20 +240,25 @@ class nsHashKey;
 #define NS_FOCUS_CONTENT                (NS_FOCUS_EVENT_START)
 #define NS_BLUR_CONTENT                 (NS_FOCUS_EVENT_START + 1)
 
 
 #define NS_DRAGDROP_EVENT_START         1400
 #define NS_DRAGDROP_ENTER               (NS_DRAGDROP_EVENT_START)
 #define NS_DRAGDROP_OVER                (NS_DRAGDROP_EVENT_START + 1)
 #define NS_DRAGDROP_EXIT                (NS_DRAGDROP_EVENT_START + 2)
-#define NS_DRAGDROP_DROP                (NS_DRAGDROP_EVENT_START + 3)
+#define NS_DRAGDROP_DRAGDROP            (NS_DRAGDROP_EVENT_START + 3)
 #define NS_DRAGDROP_GESTURE             (NS_DRAGDROP_EVENT_START + 4)
+#define NS_DRAGDROP_DRAG                (NS_DRAGDROP_EVENT_START + 5)
+#define NS_DRAGDROP_END                 (NS_DRAGDROP_EVENT_START + 6)
+#define NS_DRAGDROP_START               (NS_DRAGDROP_EVENT_START + 7)
+#define NS_DRAGDROP_DROP                (NS_DRAGDROP_EVENT_START + 8)
 #define NS_DRAGDROP_OVER_SYNTH          (NS_DRAGDROP_EVENT_START + 1)
 #define NS_DRAGDROP_EXIT_SYNTH          (NS_DRAGDROP_EVENT_START + 2)
+#define NS_DRAGDROP_LEAVE_SYNTH         (NS_DRAGDROP_EVENT_START + 9)
 
 // Events for popups
 #define NS_XUL_EVENT_START            1500
 #define NS_XUL_POPUP_SHOWING          (NS_XUL_EVENT_START)
 #define NS_XUL_POPUP_SHOWN            (NS_XUL_EVENT_START+1)
 #define NS_XUL_POPUP_HIDING           (NS_XUL_EVENT_START+2)
 #define NS_XUL_POPUP_HIDDEN           (NS_XUL_EVENT_START+3)
 // NS_XUL_COMMAND used to be here     (NS_XUL_EVENT_START+4)
--- a/widget/public/nsIDragService.idl
+++ b/widget/public/nsIDragService.idl
@@ -42,17 +42,17 @@
 #include "nsIDragSession.idl"
 #include "nsIScriptableRegion.idl"
 
 
 interface nsIDOMNode;
 interface nsIDOMMouseEvent;
 interface nsISelection;
 
-[scriptable, uuid(D2875740-95D3-4F12-8E97-9FAB76C87093)]
+[scriptable, uuid(E8CD74A6-8BB6-4D27-9C65-4ED1B4398F8C)]
 interface nsIDragService : nsISupports
 {
   const long DRAGDROP_ACTION_NONE = 0;
   const long DRAGDROP_ACTION_COPY = 1;
   const long DRAGDROP_ACTION_MOVE = 2;
   const long DRAGDROP_ACTION_LINK = 4;
 
   /**
@@ -119,17 +119,24 @@ interface nsIDragService : nsISupports
     * Tells the Drag Service to start a drag session. This is called when
     * an external drag occurs
     */
   void startDragSession ( ) ;
 
   /**
     * Tells the Drag Service to end a drag session. This is called when
     * an external drag occurs
+    *
+    * If aDoneDrag is true, the drag has finished, otherwise the drag has
+    * just left the window.
     */
-  void endDragSession ( ) ;
-  
+  void endDragSession ( in PRBool aDoneDrag ) ;
+
+  /**
+   * Fire a drag event at the source of the drag
+   */
+  void fireDragEventAtSource ( in unsigned long aMsg );
 };
 
 
 %{ C++
 
 %}
--- a/widget/src/beos/nsDragService.cpp
+++ b/widget/src/beos/nsDragService.cpp
@@ -333,26 +333,26 @@ nsDragService::StartDragSession()
 //-------------------------------------------------------------------------
 //
 // nsIDragService : EndDragSession
 //
 // We overwrite this so we can log it
 //
 //-------------------------------------------------------------------------
 NS_IMETHODIMP
-nsDragService::EndDragSession()
+nsDragService::EndDragSession(PRBool aDoneDrag)
 {
     PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::EndDragSession()"));
     //Don't reset drag info, keep it until there is a new drag, in case a negotiated drag'n'drop wants the info.
     //We need the draginfo as we are ending starting the dragsession
     //on entering/exiting different views (nsWindows) now.
     //That way the dragsession is always ended when we go outside mozilla windows, but we do throw away the 
     // mSourceDocument and mSourceNode. We do hold on to the nsTransferable if it was a internal drag. 
     //ResetDragInfo();
-    return nsBaseDragService::EndDragSession();
+    return nsBaseDragService::EndDragSession(aDoneDrag);
 }
 
 //-------------------------------------------------------------------------
 //
 // nsIDragSession : SetCanDrop
 //
 // We overwrite this so we can log it
 //
--- a/widget/src/beos/nsDragService.h
+++ b/widget/src/beos/nsDragService.h
@@ -59,17 +59,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   
   //nsIDragService
   NS_IMETHOD InvokeDragSession(nsIDOMNode *aDOMNode,
                                nsISupportsArray * anArrayTransferables,
                                nsIScriptableRegion * aRegion,
                                PRUint32 aActionType);
   NS_IMETHOD StartDragSession();
-  NS_IMETHOD EndDragSession();
+  NS_IMETHOD EndDragSession(PRBool aDoneDrag);
   
   // nsIDragSession
   NS_IMETHOD GetNumDropItems      (PRUint32 * aNumItems);
   NS_IMETHOD GetData              (nsITransferable * aTransferable,
                                    PRUint32 aItemIndex);
   NS_IMETHOD IsDataFlavorSupported(const char *aDataFlavor, 
                                    PRBool *_retval);
 
--- a/widget/src/beos/nsWindow.cpp
+++ b/widget/src/beos/nsWindow.cpp
@@ -2001,17 +2001,17 @@ bool nsWindow::CallMethod(MethodInfo *in
 					}
 					dragSession->SetDragAction(action);
 				}
 			}
 			DispatchWindowEvent(&event);
 			NS_RELEASE(event.widget);
 
 			if (dragService)
-				dragService->EndDragSession();
+				dragService->EndDragSession(PR_TRUE);
 		}
 		break;
 
 	case nsSwitchToUIThread::ONACTIVATE:
 		NS_ASSERTION(info->nArgs == 2, "Wrong number of arguments to CallMethod");
 		if (!mEnabled || eWindowType_popup == mWindowType || 0 == mView->Window())
 			return false;
 		if ((BWindow *)info->args[1] != mView->Window())
@@ -3101,21 +3101,26 @@ void nsViewBeOS::MouseMoved(BPoint point
 		}
 		break;
 	case B_EXITED_VIEW:
 		{
 			args[0] = NULL != msg ? NS_DRAGDROP_EXIT : NS_MOUSE_EXIT;
 			if (msg == NULL)
 				break;
 			nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
-			dragService->EndDragSession();
+			dragService->EndDragSession(PR_FALSE);
 		}
 		break;
 	default:
 		args[0]= msg == NULL ? NS_MOUSE_MOVE : NS_DRAGDROP_OVER;
+        // fire the drag event at the source
+        if (msg != NULL) {
+			nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
+			dragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
+        }
  	}
  	
 	MethodInfo *moveInfo = nsnull;
 	if (nsnull != (moveInfo = new MethodInfo(w, w, nsSwitchToUIThread::ONMOUSE, 4, args)))
 		t->CallMethodAsync(moveInfo);
 	NS_RELEASE(t);
 }
 
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -3684,18 +3684,22 @@ static PRBool IsSpecialGeckoKey(UInt32 m
     return NO;
 
   if (aMessage == NS_DRAGDROP_ENTER)
     mDragService->StartDragSession();
 
   nsCOMPtr<nsIDragSession> dragSession;
   mDragService->GetCurrentSession(getter_AddRefs(dragSession));
   if (dragSession) {
-    if (aMessage == NS_DRAGDROP_OVER)
+    if (aMessage == NS_DRAGDROP_OVER) {
+      // fire the drag event at the source. Just ignore whether it was
+      // cancelled or not as there isn't actually a means to stop the drag
+      mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
       dragSession->SetCanDrop(PR_FALSE);
+    }
     else if (aMessage == NS_DRAGDROP_DROP) {
       // We make the assuption that the dragOver handlers have correctly set
       // the |canDrop| property of the Drag Session.
       PRBool canDrop = PR_FALSE;
       if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop)
         return NO;
     }
     
@@ -3723,17 +3727,17 @@ static PRBool IsSpecialGeckoKey(UInt32 m
   if (aMessage == NS_DRAGDROP_EXIT && dragSession) {
     nsCOMPtr<nsIDOMNode> sourceNode;
     dragSession->GetSourceNode(getter_AddRefs(sourceNode));
     if (!sourceNode) {
       // We're leaving a window while doing a drag that was
       // initiated in a different app. End the drag session,
       // since we're done with it for now (until the user
       // drags back into mozilla).
-      mDragService->EndDragSession();
+      mDragService->EndDragSession(PR_FALSE);
     }
   }
 
   return handled ? YES : NO;
 }
 
 
 - (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
--- a/widget/src/cocoa/nsDragService.mm
+++ b/widget/src/cocoa/nsDragService.mm
@@ -359,17 +359,17 @@ nsDragService::InvokeDragSession(nsIDOMN
   nsBaseDragService::StartDragSession();
   [globalDragView dragImage:image
                          at:localPoint
                      offset:NSMakeSize(0,0)
                       event:globalDragEvent
                  pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
                      source:globalDragView
                   slideBack:YES];
-  nsBaseDragService::EndDragSession();
+  nsBaseDragService::EndDragSession(PR_TRUE);
 
   return NS_OK;
 }
 
 
 NS_IMETHODIMP
 nsDragService::GetData(nsITransferable* aTransferable, PRUint32 aItemIndex)
 {
--- a/widget/src/gtk/nsDragService.cpp
+++ b/widget/src/gtk/nsDragService.cpp
@@ -225,22 +225,22 @@ nsDragService::InvokeDragSession (nsIDOM
 NS_IMETHODIMP
 nsDragService::StartDragSession()
 {
   PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::StartDragSession"));
   return nsBaseDragService::StartDragSession();
 }
  
 NS_IMETHODIMP
-nsDragService::EndDragSession()
+nsDragService::EndDragSession(PRBool aDoneDrag)
 {
   PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::EndDragSession"));
   // unset our drag action
   SetDragAction(DRAGDROP_ACTION_NONE);
-  return nsBaseDragService::EndDragSession();
+  return nsBaseDragService::EndDragSession(aDoneDrag);
 }
 
 // nsIDragSession
 NS_IMETHODIMP
 nsDragService::SetCanDrop            (PRBool           aCanDrop)
 {
   PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::SetCanDrop %d",
                                  aCanDrop));
@@ -969,17 +969,17 @@ nsDragService::GetSourceList(void)
 
 void
 nsDragService::SourceEndDrag(void)
 {
   // this just releases the list of data items that we provide
   mSourceDataItems = 0;
 
   // Inform the drag session that we're ending the drag.
-  EndDragSession();
+  EndDragSession(PR_TRUE);
 }
 
 static void
 CreateUriList(nsISupportsArray *items, gchar **text, gint *length)
 {
   PRUint32 i, count;
   GString *uriList = g_string_new(NULL);
 
--- a/widget/src/gtk/nsDragService.h
+++ b/widget/src/gtk/nsDragService.h
@@ -64,17 +64,17 @@ public:
   NS_DECL_NSIOBSERVER
 
   // nsIDragService
   NS_IMETHOD InvokeDragSession (nsIDOMNode *aDOMNode,
                                 nsISupportsArray * anArrayTransferables,
                                 nsIScriptableRegion * aRegion,
                                 PRUint32 aActionType);
   NS_IMETHOD StartDragSession();
-  NS_IMETHOD EndDragSession();
+  NS_IMETHOD EndDragSession(PRBool aDragDone);
 
   // nsIDragSession
   NS_IMETHOD SetCanDrop            (PRBool           aCanDrop);
   NS_IMETHOD GetCanDrop            (PRBool          *aCanDrop);
   NS_IMETHOD GetNumDropItems       (PRUint32 * aNumItems);
   NS_IMETHOD GetData               (nsITransferable * aTransferable,
                                     PRUint32 aItemIndex);
   NS_IMETHOD IsDataFlavorSupported (const char *aDataFlavor, PRBool *_retval);
--- a/widget/src/gtk/nsWindow.cpp
+++ b/widget/src/gtk/nsWindow.cpp
@@ -3425,17 +3425,17 @@ nsWindow::OnDragDropSignal        (GtkWi
 
   // send our drag exit event
   innerMostWidget->OnDragLeave();
   // and clear the mLastDragMotion window
   mLastDragMotionWindow = 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();
+  dragService->EndDragSession(PR_TRUE);
 
   return TRUE;
 }
 
 // when the data has been received
 /* static */
 void
 nsWindow::DragDataReceived (GtkWidget         *aWidget,
@@ -3496,17 +3496,17 @@ nsWindow::OnDragLeave(void)
       nsCOMPtr<nsIDOMNode> sourceNode;
       currentDragSession->GetSourceNode(getter_AddRefs(sourceNode));
 
       if (!sourceNode) {
         // We're leaving a window while doing a drag that was
         // initiated in a different app. End the drag session, since
         // we're done with it for now (until the user drags back into
         // mozilla).
-        dragService->EndDragSession();
+        dragService->EndDragSession(PR_FALSE);
       }
     }
   }
 
   Release();
 }
 
 void
--- a/widget/src/gtk2/nsDragService.cpp
+++ b/widget/src/gtk2/nsDragService.cpp
@@ -235,22 +235,22 @@ nsDragService::InvokeDragSession(nsIDOMN
 NS_IMETHODIMP
 nsDragService::StartDragSession()
 {
     PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::StartDragSession"));
     return nsBaseDragService::StartDragSession();
 }
  
 NS_IMETHODIMP
-nsDragService::EndDragSession()
+nsDragService::EndDragSession(PRBool aDoneDrag)
 {
     PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::EndDragSession"));
     // unset our drag action
     SetDragAction(DRAGDROP_ACTION_NONE);
-    return nsBaseDragService::EndDragSession();
+    return nsBaseDragService::EndDragSession(aDoneDrag);
 }
 
 // nsIDragSession
 NS_IMETHODIMP
 nsDragService::SetCanDrop(PRBool aCanDrop)
 {
     PR_LOG(sDragLm, PR_LOG_DEBUG, ("nsDragService::SetCanDrop %d",
                                    aCanDrop));
@@ -1033,17 +1033,17 @@ nsDragService::GetSourceList(void)
 
 void
 nsDragService::SourceEndDrag(void)
 {
     // this just releases the list of data items that we provide
     mSourceDataItems = 0;
 
     // Inform the drag session that we're ending the drag.
-    EndDragSession();
+    EndDragSession(PR_TRUE);
 }
 
 static void
 CreateUriList(nsISupportsArray *items, gchar **text, gint *length)
 {
     PRUint32 i, count;
     GString *uriList = g_string_new(NULL);
 
--- a/widget/src/gtk2/nsDragService.h
+++ b/widget/src/gtk2/nsDragService.h
@@ -64,17 +64,17 @@ public:
     NS_DECL_NSIOBSERVER
 
     // nsIDragService
     NS_IMETHOD InvokeDragSession (nsIDOMNode *aDOMNode,
                                   nsISupportsArray * anArrayTransferables,
                                   nsIScriptableRegion * aRegion,
                                   PRUint32 aActionType);
     NS_IMETHOD StartDragSession();
-    NS_IMETHOD EndDragSession();
+    NS_IMETHOD EndDragSession(PRBool aDoneDrag);
 
     // nsIDragSession
     NS_IMETHOD SetCanDrop            (PRBool           aCanDrop);
     NS_IMETHOD GetCanDrop            (PRBool          *aCanDrop);
     NS_IMETHOD GetNumDropItems       (PRUint32 * aNumItems);
     NS_IMETHOD GetData               (nsITransferable * aTransferable,
                                       PRUint32 aItemIndex);
     NS_IMETHOD IsDataFlavorSupported (const char *aDataFlavor, PRBool *_retval);
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -2469,16 +2469,18 @@ nsWindow::OnDragMotionEvent(GtkWidget *a
     mLastDragMotionWindow = innerMostWidget;
 
     // update the drag context
     dragSessionGTK->TargetSetLastContext(aWidget, aDragContext, aTime);
 
     // notify the drag service that we are starting a drag motion.
     dragSessionGTK->TargetStartDragMotion();
 
+    dragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
+
     nsMouseEvent event(PR_TRUE, NS_DRAGDROP_OVER, innerMostWidget,
                        nsMouseEvent::eReal);
 
     InitDragEvent(event);
 
     // now that we have initialized the event update our drag status
     UpdateDragStatus(event, aDragContext, dragService);
 
@@ -2627,17 +2629,17 @@ nsWindow::OnDragDropEvent(GtkWidget *aWi
 
     // send our drag exit event
     innerMostWidget->OnDragLeave();
     // and clear the mLastDragMotion window
     mLastDragMotionWindow = 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();
+    dragService->EndDragSession(PR_TRUE);
 
     return TRUE;
 }
 
 void
 nsWindow::OnDragDataReceivedEvent(GtkWidget *aWidget,
                                   GdkDragContext *aDragContext,
                                   gint aX,
@@ -2679,17 +2681,17 @@ nsWindow::OnDragLeave(void)
             nsCOMPtr<nsIDOMNode> sourceNode;
             currentDragSession->GetSourceNode(getter_AddRefs(sourceNode));
 
             if (!sourceNode) {
                 // We're leaving a window while doing a drag that was
                 // initiated in a different app. End the drag session,
                 // since we're done with it for now (until the user
                 // drags back into mozilla).
-                dragService->EndDragSession();
+                dragService->EndDragSession(PR_FALSE);
             }
         }
     }
 
     Release();
 }
 
 void
--- a/widget/src/mac/nsDragHelperService.cpp
+++ b/widget/src/mac/nsDragHelperService.cpp
@@ -185,17 +185,17 @@ nsDragHelperService::Leave(DragReference
     nsCOMPtr<nsIDOMNode> sourceNode;
     currentDragSession->GetSourceNode(getter_AddRefs(sourceNode));
 
     if (!sourceNode) {
       // We're leaving a window while doing a drag that was
       // initiated in a differnt app. End the drag session,
       // since we're done with it for now (until the user
       // drags back into mozilla).
-      mDragService->EndDragSession();
+      mDragService->EndDragSession(PR_FALSE);
     }
   }
 
   // we're _really_ done with it, so let go of the service.
   mDragService = nsnull;
 
   return NS_OK;
 }
--- a/widget/src/mac/nsDragService.cpp
+++ b/widget/src/mac/nsDragService.cpp
@@ -313,17 +313,17 @@ nsDragService::InvokeDragSession (nsIDOM
   // register drag send proc which will call us back when asked for the actual
   // flavor data (instead of placing it all into the drag manager)
   ::SetDragSendProc ( theDragRef, mDragSendDataUPP, this );
 
   // start the drag. Be careful, mDragRef will be invalid AFTER this call (it is
   // reset by the dragTrackingHandler).
   StartDragSession();
   ::TrackDrag ( theDragRef, &theEvent, theDragRgn );
-  EndDragSession();
+  EndDragSession(PR_TRUE);
   
   // clean up after ourselves 
   ::DisposeRgn ( theDragRgn );
   result = ::DisposeDrag ( theDragRef );
 #if DEBUG_DD
   printf("**** disposing drag ref %ld\n", theDragRef);
 #endif
   NS_ASSERTION ( result == noErr, "Error disposing drag" );
--- a/widget/src/os2/nsDragService.cpp
+++ b/widget/src/os2/nsDragService.cpp
@@ -232,16 +232,17 @@ NS_IMETHODIMP nsDragService::InvokeDragS
     dragimage.cxOffset = 2;
   }
   else
     dragimage.hImage  = WinQuerySysPointer(HWND_DESKTOP, SPTR_FILE, FALSE);
     
   mDoingDrag = PR_TRUE;
   HWND hwndDest = DrgDrag(mDragWnd, pDragInfo, &dragimage, 1, VK_BUTTON2,
                   (void*)0x80000000L); // Don't lock the desktop PS
+  FireDragEventAtSource(NS_DRAGDROP_END);
   mDoingDrag = PR_FALSE;
 
     // do clean up;  if the drop completed,
     // the target will delete the string handles
   if (hwndDest == 0)
       DrgDeleteDraginfoStrHandles(pDragInfo);
   DrgFreeDraginfo(pDragInfo);
 
@@ -347,17 +348,17 @@ MRESULT EXPENTRY nsDragWindowProc(HWND h
 // they're overridden here and turned into no-ops to prevent this
 
 NS_IMETHODIMP nsDragService::StartDragSession()
 {
   NS_ASSERTION(0, "OS/2 version of StartDragSession() should never be called!");
   return NS_OK;
 }
 
-NS_IMETHODIMP nsDragService::EndDragSession()
+NS_IMETHODIMP nsDragService::EndDragSession(PRBool aDragDone)
 {
   NS_ASSERTION(0, "OS/2 version of EndDragSession() should never be called!");
   return NS_OK;
 }
 
 // --------------------------------------------------------------------------
 
 NS_IMETHODIMP nsDragService::GetNumDropItems(PRUint32 *aNumDropItems)
--- a/widget/src/os2/nsDragService.h
+++ b/widget/src/os2/nsDragService.h
@@ -58,17 +58,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
 
     // nsIDragService
   NS_IMETHOD InvokeDragSession (nsIDOMNode* aDOMNode,
                                 nsISupportsArray* aTransferables,
                                 nsIScriptableRegion* aRegion,
                                 PRUint32 aActionType);
   NS_IMETHOD StartDragSession();
-  NS_IMETHOD EndDragSession();
+  NS_IMETHOD EndDragSession(PRBool aDoneDrag);
 
     // nsIDragSession
   NS_IMETHOD GetNumDropItems(PRUint32* aNumDropItems);
   NS_IMETHOD GetData(nsITransferable* aTransferable, PRUint32 aItemIndex);
   NS_IMETHOD IsDataFlavorSupported(const char* aDataFlavor, PRBool* _retval);
 
     // nsIDragSessionOS2
   NS_IMETHOD DragOverMsg(PDRAGINFO pdinfo, MRESULT& mr, PRUint32* dragFlags);
--- a/widget/src/os2/nsWindow.cpp
+++ b/widget/src/os2/nsWindow.cpp
@@ -3917,16 +3917,18 @@ PRBool nsWindow::OnDragDropMsg(ULONG msg
     nsCOMPtr<nsIDragSessionOS2> dragSession(
                         do_QueryInterface(dragService, &rv));
     if (dragSession) {
 
         // handle all possible input without regard to outcome
       switch (msg) {
 
         case DM_DRAGOVER:
+          dragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
+
           rv = dragSession->DragOverMsg((PDRAGINFO)mp1, mr, &dragFlags);
           eventType = NS_DRAGDROP_OVER;
           break;
 
         case DM_DRAGLEAVE:
           rv = dragSession->DragLeaveMsg((PDRAGINFO)mp1, &dragFlags);
           eventType = NS_DRAGDROP_EXIT;
           break;
--- a/widget/src/photon/nsWidget.cpp
+++ b/widget/src/photon/nsWidget.cpp
@@ -1308,35 +1308,36 @@ int nsWidget::DndCallback( PtWidget_t *w
 			sDragService->StartDragSession();
 			pWidget->ProcessDrag( cbinfo->event, NS_DRAGDROP_ENTER, &ptrev->pos );
 
 			PtDndSelect( widget, &dnd_data_template, 1, NULL, NULL, cbinfo );
 			}
 			break;
 
 		case Ph_EV_DND_MOTION: {
+			sDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
 			pWidget->ProcessDrag( cbinfo->event, NS_DRAGDROP_OVER, &ptrev->pos );
 			}
 			break;
 		case Ph_EV_DND_DROP:
 			nsDragService *d;
 			d = ( nsDragService * )sDragService;
 			if( d->SetDropData( (char*)cbdnd->data ) != NS_OK ) break;
 			pWidget->ProcessDrag( cbinfo->event, NS_DRAGDROP_DROP, &ptrev->pos );
-			sDragService->EndDragSession();
+			sDragService->EndDragSession(PR_TRUE);
 			((nsDragService*) sDragService)->SourceEndDrag();
 			break;
 
 		case Ph_EV_DND_LEAVE:
 			pWidget->ProcessDrag( cbinfo->event, NS_DRAGDROP_EXIT, &ptrev->pos );
-			sDragService->EndDragSession();
+			sDragService->EndDragSession(PR_FALSE);
 			break;
 
 		case Ph_EV_DND_CANCEL:
 			pWidget->ProcessDrag( cbinfo->event, NS_DRAGDROP_EXIT, &ptrev->pos );
-			sDragService->EndDragSession();
+			sDragService->EndDragSession(PR_TRUE);
 			((nsDragService*) sDragService)->SourceEndDrag();
 			break;
 		}
 
 	return Pt_CONTINUE;
 	}
 #endif /* PHOTON_DND */
--- a/widget/src/qt/nsDragService.cpp
+++ b/widget/src/qt/nsDragService.cpp
@@ -185,23 +185,23 @@ QDragObject *nsDragService::RegisterDrag
 NS_IMETHODIMP nsDragService::StartDragSession()
 {
 #ifdef NS_DEBUG
   printf(" DnD: StartDragSession\n");
 #endif
   return nsBaseDragService::StartDragSession();
 }
 
-NS_IMETHODIMP nsDragService::EndDragSession()
+NS_IMETHODIMP nsDragService::EndDragSession(PRBool aDragDone)
 {
 #ifdef NS_DEBUG
   printf(" DnD: EndDragSession\n");
 #endif
   mDragObject = 0;
-  return nsBaseDragService::EndDragSession();
+  return nsBaseDragService::EndDragSession(aDragDone);
 }
 
 // nsIDragSession
 NS_IMETHODIMP nsDragService::SetCanDrop(PRBool aCanDrop)
 {
   mCanDrop = aCanDrop;
   return NS_OK;
 }
--- a/widget/src/qt/nsDragService.h
+++ b/widget/src/qt/nsDragService.h
@@ -57,17 +57,17 @@ public:
   NS_DECL_ISUPPORTS_INHERITED
   
     //nsIDragService
   NS_IMETHOD InvokeDragSession(nsIDOMNode *aDOMNode,
                                nsISupportsArray *anArrayTransferables,
                                nsIScriptableRegion *aRegion,
                                PRUint32 aActionType);
   NS_IMETHOD StartDragSession();
-  NS_IMETHOD EndDragSession();
+  NS_IMETHOD EndDragSession(PRBool aDragDone);
 
   // nsIDragSession
   NS_IMETHOD SetCanDrop(PRBool aCanDrop);
   NS_IMETHOD GetCanDrop(PRBool *aCanDrop);
   NS_IMETHOD GetNumDropItems(PRUint32 *aNumItems);
   NS_IMETHOD GetData(nsITransferable *aTransferable,PRUint32 aItemIndex);
   NS_IMETHOD IsDataFlavorSupported(const char *aDataFlavor,PRBool *_retval);
 
--- a/widget/src/windows/nsDragService.cpp
+++ b/widget/src/windows/nsDragService.cpp
@@ -221,17 +221,17 @@ nsDragService::StartInvokingDragSession(
     {
       pAsyncOp->InOperation(&isAsync);
       if (!isAsync)
         aDataObj->Release();
     }
   }
 
   // We're done dragging
-  EndDragSession();
+  EndDragSession(PR_TRUE);
 
   // For some drag/drop interactions, IDataObject::SetData doesn't get
   // called with a CFSTR_PERFORMEDDROPEFFECT format and the
   // intermediate file (if it was created) isn't deleted.  See
   // http://bugzilla.mozilla.org/show_bug.cgi?id=203847#c4 for a
   // detailed description of the different cases.  Now that we know
   // that the drag/drop operation has ended, call SetData() so that
   // the intermediate file is deleted.
@@ -477,19 +477,19 @@ nsDragService::IsCollectionObject(IDataO
 //
 // EndDragSession
 //
 // Override the default to make sure that we release the data object
 // when the drag ends. It seems that OLE doesn't like to let apps quit
 // w/out crashing when we're still holding onto their data
 //
 NS_IMETHODIMP
-nsDragService::EndDragSession()
+nsDragService::EndDragSession(PRBool aDoneDrag)
 {
-  nsBaseDragService::EndDragSession();
+  nsBaseDragService::EndDragSession(aDoneDrag);
   NS_IF_RELEASE(mDataObject);
 
   return NS_OK;
 }
 
 // Gets shell version as packed 64 bit int
 PRUint64 nsDragService::GetShellVersion()
 {
--- a/widget/src/windows/nsDragService.h
+++ b/widget/src/windows/nsDragService.h
@@ -63,17 +63,17 @@ public:
                                nsISupportsArray *anArrayTransferables,
                                nsIScriptableRegion *aRegion,
                                PRUint32 aActionType);
 
   // nsIDragSession
   NS_IMETHOD GetData(nsITransferable * aTransferable, PRUint32 anItem);
   NS_IMETHOD GetNumDropItems(PRUint32 * aNumItems);
   NS_IMETHOD IsDataFlavorSupported(const char *aDataFlavor, PRBool *_retval);
-  NS_IMETHOD EndDragSession();
+  NS_IMETHOD EndDragSession(PRBool aDoneDrag);
 
   // native impl.
   NS_IMETHOD SetIDataObject(IDataObject * aDataObj);
   NS_IMETHOD StartInvokingDragSession(IDataObject * aDataObj,
                                       PRUint32 aActionType);
 
 protected:
   nsDataObjCollection* GetDataObjCollection(IDataObject * aDataObj);
--- a/widget/src/windows/nsNativeDragTarget.cpp
+++ b/widget/src/windows/nsNativeDragTarget.cpp
@@ -289,16 +289,18 @@ nsNativeDragTarget::DragOver(DWORD   grf
                              POINTL  pt,
                              LPDWORD pdwEffect)
 {
   if (DRAG_DEBUG) printf("DragOver\n");
 	if (!mDragService) {
 		return ResultFromScode(E_FAIL);
   }
 
+  mDragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
+
   // Now process the native drag state and then dispatch the event
   ProcessDrag(nsnull, NS_DRAGDROP_OVER, grfKeyState, pt, pdwEffect);
   return S_OK;
 }
 
 
 //-----------------------------------------------------
 STDMETHODIMP
@@ -320,17 +322,17 @@ nsNativeDragTarget::DragLeave()
     nsCOMPtr<nsIDOMNode> sourceNode;
     currentDragSession->GetSourceNode(getter_AddRefs(sourceNode));
 
     if (!sourceNode) {
       // We're leaving a window while doing a drag that was
       // initiated in a different app. End the drag session, since
       // we're done with it for now (until the user drags back into
       // mozilla).
-      mDragService->EndDragSession();
+      mDragService->EndDragSession(PR_FALSE);
     }
   }
 
   return S_OK;
 }
 
 
 //-----------------------------------------------------
@@ -355,11 +357,11 @@ nsNativeDragTarget::Drop(LPDATAOBJECT pD
 
   // Note: Calling ProcessDrag can destroy us; don't touch members after that.
   nsCOMPtr<nsIDragService> serv = mDragService;
 
   // Now process the native drag state and then dispatch the event
   ProcessDrag(pData, NS_DRAGDROP_DROP, grfKeyState, aPT, pdwEffect);
 
   // tell the drag service we're done with the session
-  serv->EndDragSession();
+  serv->EndDragSession(PR_TRUE);
   return S_OK;
 }
--- a/widget/src/xlib/nsAppShell.cpp
+++ b/widget/src/xlib/nsAppShell.cpp
@@ -1143,16 +1143,18 @@ void nsAppShell::HandleDragMotionEvent(X
     if (dragServiceXlib) {
       dragServiceXlib->IsDragging(&currentlyDragging);
     }
   }
 
   if (currentlyDragging) {
     dragServiceXlib->UpdatePosition(event->xmotion.x, event->xmotion.y);
 
+    dragService->FireDragEventAtSource(NS_DRAGDROP_DRAG);
+
     nsMouseEvent mevent(PR_TRUE, NS_DRAGDROP_OVER, aWidget,
                         nsMouseEvent::eReal);
     mevent.refPoint.x = event->xmotion.x;
     mevent.refPoint.y = event->xmotion.y;
 
     NS_ADDREF(aWidget);
     aWidget->DispatchMouseEvent(mevent);
     NS_RELEASE(aWidget);
--- a/widget/src/xlib/nsDragService.cpp
+++ b/widget/src/xlib/nsDragService.cpp
@@ -134,25 +134,25 @@ NS_IMETHODIMP nsDragService::InvokeDragS
 
 NS_IMETHODIMP nsDragService::StartDragSession()
 {
   mDragging = PR_TRUE;
 
   return nsBaseDragService::StartDragSession();
 }
 
-NS_IMETHODIMP nsDragService::EndDragSession()
+NS_IMETHODIMP nsDragService::EndDragSession(PRBool aDoneDrag)
 {
   if (sWindow) {
     XDestroyWindow(sDisplay, sWindow);
     sWindow = 0;
   }
   mDragging = PR_FALSE;
 
-  return nsBaseDragService::EndDragSession();
+  return nsBaseDragService::EndDragSession(aDoneDrag);
 }
 
 // nsIDragSession
 
 // For some reason we need this, but GTK does not. Hmmm...
 NS_IMETHODIMP nsDragService::GetCurrentSession(nsIDragSession **aSession)
 {
   if (!aSession)
@@ -226,17 +226,17 @@ NS_IMETHODIMP nsDragService::GetData(nsI
       nsCOMPtr <nsISupports> data;
       PRUint32 dataLen = 0;
 
       item->GetTransferData(foundFlavor.get(), getter_AddRefs(data), &dataLen);
       aTransferable->SetTransferData(foundFlavor.get(), data, dataLen);
     }
   }
 
-  EndDragSession();
+  EndDragSession(PR_TRUE);
 
   return NS_OK;
 }
 
 NS_IMETHODIMP nsDragService::IsDataFlavorSupported(const char *aDataFlavor, PRBool *_retval)
 {
   /* XXX Please implement this - for now - support all flavors */
   *_retval = PR_TRUE;
--- a/widget/src/xlib/nsDragService.h
+++ b/widget/src/xlib/nsDragService.h
@@ -63,17 +63,17 @@ public:
   virtual ~nsDragService();
 
   // nsIDragService
   NS_IMETHOD InvokeDragSession (nsIDOMNode *aDOMNode,
                                 nsISupportsArray * anArrayTransferables,
                                 nsIScriptableRegion * aRegion,
                                 PRUint32 aActionType);
   NS_IMETHOD StartDragSession();
-  NS_IMETHOD EndDragSession();
+  NS_IMETHOD EndDragSession(PRBool aDoneDrag);
 
   // nsIDragSession
   NS_IMETHOD GetCurrentSession     (nsIDragSession **aSession);
   NS_IMETHOD SetCanDrop            (PRBool           aCanDrop);
   NS_IMETHOD GetCanDrop            (PRBool          *aCanDrop);
   NS_IMETHOD GetNumDropItems       (PRUint32 * aNumItems);
   NS_IMETHOD GetData               (nsITransferable * aTransferable, PRUint32 anItemIndex);
   NS_IMETHOD IsDataFlavorSupported (const char *aDataFlavor, PRBool *_retval);
--- a/widget/src/xpwidgets/nsBaseDragService.cpp
+++ b/widget/src/xpwidgets/nsBaseDragService.cpp
@@ -63,16 +63,17 @@
 #include "nsICanvasElement.h"
 #include "nsIImage.h"
 #include "nsIImageLoadingContent.h"
 #include "gfxIImageFrame.h"
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "nsIViewObserver.h"
 #include "nsRegion.h"
+#include "nsGUIEvent.h"
 
 #ifdef MOZ_CAIRO_GFX
 #include "gfxContext.h"
 #include "gfxImageSurface.h"
 
 #endif
 
 NS_IMPL_ADDREF(nsBaseDragService)
@@ -319,38 +320,61 @@ nsBaseDragService::StartDragSession()
     return NS_ERROR_FAILURE;
   }
   mDoingDrag = PR_TRUE;
   return NS_OK;
 }
 
 //-------------------------------------------------------------------------
 NS_IMETHODIMP
-nsBaseDragService::EndDragSession()
+nsBaseDragService::EndDragSession(PRBool aDoneDrag)
 {
   if (!mDoingDrag) {
     return NS_ERROR_FAILURE;
   }
 
+  if (aDoneDrag)
+    FireDragEventAtSource(NS_DRAGDROP_END);
+
   mDoingDrag = PR_FALSE;
 
   // release the source we've been holding on to.
   mSourceDocument = nsnull;
   mSourceNode = nsnull;
   mSelection = nsnull;
   mHasImage = PR_FALSE;
   mImage = nsnull;
   mImageX = 0;
   mImageY = 0;
   mScreenX = -1;
   mScreenY = -1;
 
   return NS_OK;
 }
 
+NS_IMETHODIMP
+nsBaseDragService::FireDragEventAtSource(PRUint32 aMsg)
+{
+  if (mSourceNode) {
+    nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument);
+    if (doc) {
+      nsCOMPtr<nsIPresShell> presShell = doc->GetShellAt(0);
+      if (presShell) {
+        nsEventStatus status = nsEventStatus_eIgnore;
+        nsMouseEvent event(PR_TRUE, aMsg, nsnull, nsMouseEvent::eReal);
+
+        nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode);
+        return presShell->HandleDOMEventWithTarget(content, &event, &status);
+      }
+    }
+  }
+
+  return NS_OK;
+}
+
 #ifdef MOZ_CAIRO_GFX
 
 static nsIPresShell*
 GetPresShellForContent(nsIDOMNode* aDOMNode)
 {
   nsCOMPtr<nsIContent> content = do_QueryInterface(aDOMNode);
   nsCOMPtr<nsIDocument> document = content->GetCurrentDoc();
   if (document) {