Bug 356295. Implement HTML5 drag-drop. r=smaug,sr=roc
☠☠ backed out by aa14280d31fb ☠ ☠
authorNeil Deakin <enndeakin@gmail.com>
Wed, 20 Aug 2008 10:31:45 +1200
changeset 17078 30d900751ca93a98ccc4fca46179e44d4d900935
parent 17077 1571fd60e4c5246aa42f3e7ffe4b2824215eac0c
child 17079 f524f51c2810fba5c6e9abd4bbcd157bcb63b115
child 17081 c131d93c1b46c9427a0ad91c813a3a6239410464
child 17085 aa14280d31fb3486a3371581ae6a04461738d1eb
push idunknown
push userunknown
push dateunknown
reviewerssmaug, roc
bugs356295
milestone1.9.1a2pre
Bug 356295. Implement HTML5 drag-drop. r=smaug,sr=roc
browser/base/content/pageinfo/pageInfo.js
content/base/public/nsContentUtils.h
content/base/src/nsContentAreaDragDrop.cpp
content/base/src/nsContentAreaDragDrop.h
content/base/src/nsContentUtils.cpp
content/base/src/nsGkAtomList.h
content/events/public/nsIPrivateDOMEvent.h
content/events/src/Makefile.in
content/events/src/nsDOMDataTransfer.cpp
content/events/src/nsDOMDataTransfer.h
content/events/src/nsDOMDragEvent.cpp
content/events/src/nsDOMDragEvent.h
content/events/src/nsDOMEvent.cpp
content/events/src/nsDOMEvent.h
content/events/src/nsDOMMouseEvent.h
content/events/src/nsDOMUIEvent.cpp
content/events/src/nsEventDispatcher.cpp
content/events/src/nsEventListenerManager.cpp
content/events/src/nsEventStateManager.cpp
content/events/src/nsEventStateManager.h
content/events/test/Makefile.in
content/events/test/test_draggableprop.html
content/events/test/test_dragstart.html
content/html/content/src/nsGenericHTMLElement.cpp
content/html/content/src/nsGenericHTMLElement.h
content/html/content/src/nsHTMLAnchorElement.cpp
content/html/content/src/nsHTMLImageElement.cpp
dom/public/coreEvents/nsIDOMDragListener.h
dom/public/idl/events/Makefile.in
dom/public/idl/events/nsIDOMDataTransfer.idl
dom/public/idl/events/nsIDOMDragEvent.idl
dom/public/idl/html/nsIDOMNSHTMLElement.idl
dom/public/nsDOMClassInfoID.h
dom/src/base/nsDOMClassInfo.cpp
editor/libeditor/base/nsEditorUtils.cpp
editor/libeditor/base/nsEditorUtils.h
editor/libeditor/html/nsHTMLDataTransfer.cpp
editor/libeditor/text/nsEditorEventListeners.cpp
editor/libeditor/text/nsPlaintextDataTransfer.cpp
layout/base/nsLayoutUtils.cpp
layout/xul/base/src/tree/public/nsITreeBoxObject.idl
layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
layout/xul/base/src/tree/src/nsTreeBoxObject.cpp
testing/mochitest/tests/SimpleTest/EventUtils.js
toolkit/content/nsDragAndDrop.js
widget/public/nsGUIEvent.h
widget/public/nsIDragService.idl
widget/public/nsIDragSession.idl
widget/src/beos/nsWindow.cpp
widget/src/cocoa/nsChildView.mm
widget/src/cocoa/nsDragService.mm
widget/src/gtk2/nsWindow.cpp
widget/src/gtk2/nsWindow.h
widget/src/os2/nsWindow.cpp
widget/src/photon/nsWidget.cpp
widget/src/windows/nsNativeDragTarget.cpp
widget/src/xpwidgets/nsBaseDragService.cpp
widget/src/xpwidgets/nsBaseDragService.h
widget/src/xpwidgets/nsPrimitiveHelpers.cpp
xpfe/global/jar.mn
--- a/browser/base/content/pageinfo/pageInfo.js
+++ b/browser/base/content/pageinfo/pageInfo.js
@@ -183,20 +183,16 @@ gImageView.getCellProperties = function(
 
 var gImageHash = { };
 
 // localized strings (will be filled in when the document is loaded)
 // this isn't all of them, these are just the ones that would otherwise have been loaded inside a loop
 var gStrings = { };
 var gBundle;
 
-const DRAGSERVICE_CONTRACTID    = "@mozilla.org/widget/dragservice;1";
-const TRANSFERABLE_CONTRACTID   = "@mozilla.org/widget/transferable;1";
-const ARRAY_CONTRACTID          = "@mozilla.org/supports-array;1";
-const STRING_CONTRACTID         = "@mozilla.org/supports-string;1";
 const PERMISSION_CONTRACTID     = "@mozilla.org/permissionmanager;1";
 const PREFERENCES_CONTRACTID    = "@mozilla.org/preferences-service;1";
 const ATOM_CONTRACTID           = "@mozilla.org/atom-service;1";
 
 // a number of services I'll need later
 // the cache services
 const nsICacheService = Components.interfaces.nsICacheService;
 const ACCESS_READ     = Components.interfaces.nsICache.ACCESS_READ;
@@ -658,42 +654,26 @@ function onBeginLinkDrag(event,urlField,
   var tree = event.target;
   if (!("treeBoxObject" in tree))
     tree = tree.parentNode;
 
   var row = tree.treeBoxObject.getRowAt(event.clientX, event.clientY);
   if (row == -1)
     return;
 
-  // Getting drag-system needed services
-  var dragService = Components.classes[DRAGSERVICE_CONTRACTID].getService()
-                              .QueryInterface(Components.interfaces.nsIDragService);
-  var transArray = Components.classes[ARRAY_CONTRACTID]
-                             .createInstance(Components.interfaces.nsISupportsArray);
-  if (!transArray)
-    return;
-
-  var trans = Components.classes[TRANSFERABLE_CONTRACTID]
-                        .createInstance(Components.interfaces.nsITransferable);
-  if (!trans)
-    return;
-
   // Adding URL flavor
-  trans.addDataFlavor("text/x-moz-url");
   var col = tree.columns[urlField];
   var url = tree.view.getCellText(row, col);
   col = tree.columns[descField];
   var desc = tree.view.getCellText(row, col);
-  var stringURL = Components.classes[STRING_CONTRACTID]
-                            .createInstance(Components.interfaces.nsISupportsString);
-  stringURL.data = url + "\n" + desc;
-  trans.setTransferData("text/x-moz-url", stringURL, stringURL.data.length * 2 );
-  transArray.AppendElement(trans.QueryInterface(Components.interfaces.nsISupports));
 
-  dragService.invokeDragSession(event.target, transArray, null, dragService.DRAGDROP_ACTION_NONE);
+  var dt = event.dataTransfer;
+  dt.setData("text/x-moz-url", url + "\n" + desc);
+  dt.setData("text/url-list", url);
+  dt.setData("text/plain", url);
 }
 
 //******** Image Stuff
 function getSelectedImage(tree)
 {
   if (!gImageView.rowCount)
     return null;
 
--- a/content/base/public/nsContentUtils.h
+++ b/content/base/public/nsContentUtils.h
@@ -96,16 +96,17 @@ class nsIScriptContext;
 class nsIRunnable;
 template<class E> class nsCOMArray;
 class nsIPref;
 class nsVoidArray;
 struct JSRuntime;
 class nsICaseConversion;
 class nsIUGenCategory;
 class nsIWidget;
+class nsIDragSession;
 class nsPIDOMWindow;
 #ifdef MOZ_XTF
 class nsIXTFService;
 #endif
 #ifdef IBMBIDI
 class nsIBidiKeyboard;
 #endif
 
@@ -1214,16 +1215,21 @@ public:
 
   /**
    * Hide any XUL popups associated with aDocument, including any documents
    * displayed in child frames.
    */
   static void HidePopupsInDocument(nsIDocument* aDocument);
 
   /**
+   * Retrieve the current drag session, or null if no drag is currently occuring
+   */
+  static already_AddRefed<nsIDragSession> GetDragSession();
+
+  /**
    * Return true if aURI is a local file URI (i.e. file://).
    */
   static PRBool URIIsLocalFile(nsIURI *aURI);
 
   /**
    * If aContent is an HTML element with a DOM level 0 'name', then
    * return the name. Otherwise return null.
    */
--- a/content/base/src/nsContentAreaDragDrop.cpp
+++ b/content/base/src/nsContentAreaDragDrop.cpp
@@ -40,24 +40,25 @@
 
 // Local Includes
 #include "nsContentAreaDragDrop.h"
 
 // Helper Classes
 #include "nsString.h"
 
 // Interfaces needed to be included
+#include "nsIVariant.h"
 #include "nsIDOMNSUIEvent.h"
 #include "nsIDOMUIEvent.h"
 #include "nsISelection.h"
 #include "nsIDOMNode.h"
 #include "nsIDOMNodeList.h"
 #include "nsIDOMEvent.h"
 #include "nsIDOMNSEvent.h"
-#include "nsIDOMMouseEvent.h"
+#include "nsIDOMDragEvent.h"
 #include "nsIDOMAbstractView.h"
 #include "nsPIDOMWindow.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentRange.h"
 #include "nsIDOMRange.h"
 #include "nsIDocumentEncoder.h"
 #include "nsIFormControl.h"
 #include "nsISelectionPrivate.h"
@@ -68,38 +69,38 @@
 #include "nsIDragSession.h"
 #include "nsComponentManagerUtils.h"
 #include "nsXPCOM.h"
 #include "nsISupportsPrimitives.h"
 #include "nsServiceManagerUtils.h"
 #include "nsNetUtil.h"
 #include "nsIFile.h"
 #include "nsIWebNavigation.h"
-#include "nsIClipboardDragDropHooks.h"
-#include "nsIClipboardDragDropHookList.h"
 #include "nsIDocShell.h"
 #include "nsIContent.h"
 #include "nsIImageLoadingContent.h"
 #include "nsINameSpaceManager.h"
 #include "nsUnicharUtils.h"
 #include "nsIURL.h"
 #include "nsIImage.h"
 #include "nsIDocument.h"
 #include "nsIScriptSecurityManager.h"
+#include "nsIPrincipal.h"
 #include "nsIPresShell.h"
 #include "nsPresContext.h"
 #include "nsIDocShellTreeItem.h"
 #include "nsIFrame.h"
 #include "nsRange.h"
 #include "nsIWebBrowserPersist.h"
 #include "nsEscape.h"
 #include "nsContentUtils.h"
 #include "nsIMIMEService.h"
 #include "imgIRequest.h"
 #include "nsContentCID.h"
+#include "nsDOMDataTransfer.h"
 #include "nsISelectionController.h"
 #include "nsFrameSelection.h"
 #include "nsIDOMEventTarget.h"
 #include "nsWidgetsCID.h"
 
 static NS_DEFINE_CID(kHTMLConverterCID,        NS_HTMLFORMATCONVERTER_CID);
 
 // private clipboard data flavors for html copy, used by editor when pasting
@@ -109,53 +110,62 @@ static NS_DEFINE_CID(kHTMLConverterCID, 
 
 NS_IMPL_ADDREF(nsContentAreaDragDrop)
 NS_IMPL_RELEASE(nsContentAreaDragDrop)
 
 NS_INTERFACE_MAP_BEGIN(nsContentAreaDragDrop)
     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDragListener)
     NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsIDOMEventListener, nsIDOMDragListener)
     NS_INTERFACE_MAP_ENTRY(nsIDOMDragListener)
-    NS_INTERFACE_MAP_ENTRY(nsIFlavorDataProvider)
     NS_INTERFACE_MAP_ENTRY(nsIDragDropHandler)
 NS_INTERFACE_MAP_END
 
 
 class NS_STACK_CLASS nsTransferableFactory
 {
 public:
-  nsTransferableFactory(nsIDOMEvent* inMouseEvent,
-                        nsIFlavorDataProvider *inFlavorDataProvider);
-  nsresult Produce(PRBool *aDragSelection, nsITransferable** outTrans);
+  nsTransferableFactory(nsIDOMWindow* aWindow,
+                        nsIContent* aTarget,
+                        nsIContent* aSelectionTargetNode,
+                        PRBool aIsAltKeyPressed);
+  nsresult Produce(nsDOMDataTransfer* aDataTransfer,
+                   PRBool* aCanDrag,
+                   PRBool* aDragSelection,
+                   nsIContent** aDragNode);
 
 private:
-  nsresult ConvertStringsToTransferable(nsITransferable** outTrans);
+  void AddString(nsDOMDataTransfer* aDataTransfer,
+                 const nsAString& aFlavor,
+                 const nsAString& aData,
+                 nsIPrincipal* aPrincipal);
+  nsresult AddStringsToDataTransfer(nsIContent* aDragNode,
+                                    nsDOMDataTransfer* aDataTransfer);
   static nsresult GetDraggableSelectionData(nsISelection* inSelection,
-                                            nsIDOMNode* inRealTargetNode,
-                                            nsIDOMNode **outImageOrLinkNode,
+                                            nsIContent* inRealTargetNode,
+                                            nsIContent **outImageOrLinkNode,
                                             PRBool* outDragSelectedText);
-  static already_AddRefed<nsIDOMNode> FindParentLinkNode(nsIDOMNode* inNode);
-  static void GetAnchorURL(nsIDOMNode* inNode, nsAString& outURL);
-  static void GetNodeString(nsIDOMNode* inNode, nsAString & outNodeString);
+  static already_AddRefed<nsIContent> FindParentLinkNode(nsIContent* inNode);
+  static void GetAnchorURL(nsIContent* inNode, nsAString& outURL);
+  static void GetNodeString(nsIContent* inNode, nsAString & outNodeString);
   static void CreateLinkText(const nsAString& inURL, const nsAString & inText,
                               nsAString& outLinkText);
   static void GetSelectedLink(nsISelection* inSelection,
-                              nsIDOMNode **outLinkNode);
+                              nsIContent **outLinkNode);
 
   // if inNode is null, use the selection from the window
   static nsresult SerializeNodeOrSelection(nsIDOMWindow* inWindow,
-                                           nsIDOMNode* inNode,
+                                           nsIContent* inNode,
                                            nsAString& outResultString,
                                            nsAString& outHTMLContext,
                                            nsAString& outHTMLInfo);
 
-  PRBool mInstanceAlreadyUsed;
-
-  nsCOMPtr<nsIDOMEvent> mMouseEvent;
-  nsCOMPtr<nsIFlavorDataProvider> mFlavorDataProvider;
+  nsCOMPtr<nsIDOMWindow> mWindow;
+  nsCOMPtr<nsIContent> mTarget;
+  nsCOMPtr<nsIContent> mSelectionTargetNode;
+  PRPackedBool mIsAltKeyPressed;
 
   nsString mUrlString;
   nsString mImageSourceString;
   nsString mImageDestFileName;
   nsString mTitleString;
   // will be filled automatically if you fill urlstring
   nsString mHtmlString;
   nsString mContextString;
@@ -287,101 +297,63 @@ nsContentAreaDragDrop::DragOver(nsIDOMEv
     nsuiEvent->GetPreventDefault(&preventDefault);
   if ( preventDefault )
     return NS_OK;
 
   // if the drag originated w/in this content area, bail
   // early. This avoids loading a URL dragged from the content
   // area into the very same content area (which is almost never
   // the desired action).
-  nsCOMPtr<nsIDragService> dragService =
-    do_GetService("@mozilla.org/widget/dragservice;1");
-  if (!dragService)
-    return NS_ERROR_FAILURE;
 
-  nsCOMPtr<nsIDragSession> session;
-  dragService->GetCurrentSession(getter_AddRefs(session));
+  nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
+  NS_ENSURE_TRUE(session, NS_OK);
+
+  PRBool dropAllowed = PR_TRUE;
 
-  if (session) {
-    // if the client has provided an override callback, check if we
-    // the drop is allowed. If it allows it, we should still protect
-    // against dropping w/in the same document.
-    PRBool dropAllowed = PR_TRUE;
-    nsCOMPtr<nsISimpleEnumerator> enumerator;
-    GetHookEnumeratorFromEvent(inEvent, getter_AddRefs(enumerator));
+  nsCOMPtr<nsIDOMDocument> sourceDoc;
+  session->GetSourceDocument(getter_AddRefs(sourceDoc));
+  nsCOMPtr<nsIDOMDocument> eventDoc;
+  GetEventDocument(inEvent, getter_AddRefs(eventDoc));
+
+  if (sourceDoc == eventDoc) {  // common case
+    dropAllowed = PR_FALSE;
+  } else if (sourceDoc && eventDoc) {
+    // dig deeper
+    // XXXbz we need better ways to get from a document to the docshell!
+    nsCOMPtr<nsIDocument> sourceDocument(do_QueryInterface(sourceDoc));
+    nsCOMPtr<nsIDocument> eventDocument(do_QueryInterface(eventDoc));
+    NS_ASSERTION(sourceDocument, "Confused document object");
+    NS_ASSERTION(eventDocument, "Confused document object");
 
-    if (enumerator) {
-      PRBool hasMoreHooks = PR_FALSE;
-      while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks))
-             && hasMoreHooks) {
-        nsCOMPtr<nsISupports> isupp;
-        if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp)))) {
-          break;
-        }
+    nsPIDOMWindow* sourceWindow = sourceDocument->GetWindow();
+    nsPIDOMWindow* eventWindow = eventDocument->GetWindow();
+
+    if (sourceWindow && eventWindow) {
+      nsCOMPtr<nsIDocShellTreeItem> sourceShell =
+        do_QueryInterface(sourceWindow->GetDocShell());
+      nsCOMPtr<nsIDocShellTreeItem> eventShell =
+        do_QueryInterface(eventWindow->GetDocShell());
 
-        nsCOMPtr<nsIClipboardDragDropHooks> override =
-          do_QueryInterface(isupp);
+      if (sourceShell && eventShell) {
+        // Whew.  Almost there.  Get the roots that are of the same type
+        // (otherwise we'll always end up with the root docshell for the
+        // window, and drag/drop from chrom to content won't work).
+        nsCOMPtr<nsIDocShellTreeItem> sourceRoot;
+        nsCOMPtr<nsIDocShellTreeItem> eventRoot;
+        sourceShell->GetSameTypeRootTreeItem(getter_AddRefs(sourceRoot));
+        eventShell->GetSameTypeRootTreeItem(getter_AddRefs(eventRoot));
 
-        if (override) {
-#ifdef DEBUG
-          nsresult hookResult =
-#endif
-          override->AllowDrop(inEvent, session, &dropAllowed);
-          NS_ASSERTION(NS_SUCCEEDED(hookResult), "hook failure in AllowDrop");
-
-          if (!dropAllowed) {
-            break;
-          }
+        if (sourceRoot && sourceRoot == eventRoot) {
+          dropAllowed = PR_FALSE;
         }
       }
     }
-
-    nsCOMPtr<nsIDOMDocument> sourceDoc;
-    session->GetSourceDocument(getter_AddRefs(sourceDoc));
-    nsCOMPtr<nsIDOMDocument> eventDoc;
-    GetEventDocument(inEvent, getter_AddRefs(eventDoc));
-
-    if (sourceDoc == eventDoc) {  // common case
-      dropAllowed = PR_FALSE;
-    } else if (sourceDoc && eventDoc) {
-      // dig deeper
-      // XXXbz we need better ways to get from a document to the docshell!
-      nsCOMPtr<nsIDocument> sourceDocument(do_QueryInterface(sourceDoc));
-      nsCOMPtr<nsIDocument> eventDocument(do_QueryInterface(eventDoc));
-      NS_ASSERTION(sourceDocument, "Confused document object");
-      NS_ASSERTION(eventDocument, "Confused document object");
-
-      nsPIDOMWindow* sourceWindow = sourceDocument->GetWindow();
-      nsPIDOMWindow* eventWindow = eventDocument->GetWindow();
-
-      if (sourceWindow && eventWindow) {
-        nsCOMPtr<nsIDocShellTreeItem> sourceShell =
-          do_QueryInterface(sourceWindow->GetDocShell());
-        nsCOMPtr<nsIDocShellTreeItem> eventShell =
-          do_QueryInterface(eventWindow->GetDocShell());
-
-        if (sourceShell && eventShell) {
-          // Whew.  Almost there.  Get the roots that are of the same type
-          // (otherwise we'll always end up with the root docshell for the
-          // window, and drag/drop from chrom to content won't work).
-          nsCOMPtr<nsIDocShellTreeItem> sourceRoot;
-          nsCOMPtr<nsIDocShellTreeItem> eventRoot;
-          sourceShell->GetSameTypeRootTreeItem(getter_AddRefs(sourceRoot));
-          eventShell->GetSameTypeRootTreeItem(getter_AddRefs(eventRoot));
-
-          if (sourceRoot && sourceRoot == eventRoot) {
-            dropAllowed = PR_FALSE;
-          }
-        }
-      }
-    }
-
-    session->SetCanDrop(dropAllowed);
   }
 
+  session->SetCanDrop(dropAllowed);
   return NS_OK;
 }
 
 
 //
 // DragExit
 //
 // Called when an OS drag is in process and the mouse is over a gecko
@@ -492,27 +464,18 @@ nsContentAreaDragDrop::DragDrop(nsIDOMEv
 
   if (preventDefault) {
     return NS_OK;
   }
 
   // pull the transferable out of the drag service. at the moment, we
   // only care about the first item of the drag. We don't allow
   // dropping multiple items into a content area.
-  nsCOMPtr<nsIDragService> dragService =
-    do_GetService("@mozilla.org/widget/dragservice;1");
-  if (!dragService) {
-    return NS_ERROR_FAILURE;
-  }
-
-  nsCOMPtr<nsIDragSession> session;
-  dragService->GetCurrentSession(getter_AddRefs(session));
-  if (!session) {
-    return NS_ERROR_FAILURE;
-  }
+  nsCOMPtr<nsIDragSession> session = nsContentUtils::GetDragSession();
+  NS_ENSURE_TRUE(session, NS_OK);
 
   nsCOMPtr<nsITransferable> trans =
     do_CreateInstance("@mozilla.org/widget/transferable;1");
   if (!trans) {
     return NS_ERROR_FAILURE;
   }
 
   // add the relevant flavors. order is important (highest fidelity to lowest)
@@ -520,45 +483,16 @@ nsContentAreaDragDrop::DragDrop(nsIDOMEv
   trans->AddDataFlavor(kURLMime);
   trans->AddDataFlavor(kFileMime);
   trans->AddDataFlavor(kUnicodeMime);
 
   // again, we only care about the first object
   nsresult rv = session->GetData(trans, 0);
 
   if (NS_SUCCEEDED(rv)) {
-    // if the client has provided an override callback, call it. It may
-    // still return that we should continue processing.
-    nsCOMPtr<nsISimpleEnumerator> enumerator;
-    GetHookEnumeratorFromEvent(inMouseEvent, getter_AddRefs(enumerator));
-
-    if (enumerator) {
-      PRBool actionCanceled = PR_TRUE;
-      PRBool hasMoreHooks = PR_FALSE;
-      while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks))
-             && hasMoreHooks) {
-        nsCOMPtr<nsISupports> isupp;
-        if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
-          break;
-        nsCOMPtr<nsIClipboardDragDropHooks> override =
-          do_QueryInterface(isupp);
-
-        if (override) {
-#ifdef DEBUG
-          nsresult hookResult =
-#endif
-          override->OnPasteOrDrop(inMouseEvent, trans, &actionCanceled);
-          NS_ASSERTION(NS_SUCCEEDED(hookResult),
-                       "hook failure in OnPasteOrDrop");
-          if (!actionCanceled)
-            return NS_OK;
-        }
-      }
-    }
-
     nsXPIDLCString flavor;
     nsCOMPtr<nsISupports> dataWrapper;
     PRUint32 dataLen = 0;
     rv = trans->GetAnyTransferData(getter_Copies(flavor),
                                    getter_AddRefs(dataWrapper), &dataLen);
     if (NS_SUCCEEDED(rv) && dataLen > 0) {
       // get the url from one of several possible formats
       nsAutoString url;
@@ -660,194 +594,64 @@ nsContentAreaDragDrop::GetEventDocument(
     nsCOMPtr<nsIDOMWindow> window(do_QueryInterface(view));
 
     if (window) {
       window->GetDocument(outDocument);
     }
   }
 }
 
-nsresult
-nsContentAreaDragDrop::GetHookEnumeratorFromEvent(nsIDOMEvent* inEvent,
-                                                  nsISimpleEnumerator **outEnumerator)
-{
-  *outEnumerator = nsnull;
-
-  nsCOMPtr<nsIDOMDocument> domdoc;
-  GetEventDocument(inEvent, getter_AddRefs(domdoc));
-  nsCOMPtr<nsIDocument> doc = do_QueryInterface(domdoc);
-  NS_ENSURE_TRUE(doc, NS_ERROR_FAILURE);
-
-  nsCOMPtr<nsISupports> container = doc->GetContainer();
-  nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
-  NS_ENSURE_TRUE(docShell, NS_ERROR_FAILURE);
-
-  nsCOMPtr<nsIClipboardDragDropHookList> hookList = do_GetInterface(docShell);
-  NS_ENSURE_TRUE(hookList, NS_ERROR_FAILURE);
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
-  hookList->GetHookEnumerator(getter_AddRefs(enumerator));
-  NS_ENSURE_TRUE(enumerator, NS_ERROR_FAILURE);
-
-  *outEnumerator = enumerator;
-  NS_ADDREF(*outEnumerator);
-
-  return NS_OK;
-}
-
 //
 // DragGesture
 //
-// Determine if the user has started to drag something and kick off
-// an OS-level drag if it's applicable
-//
 NS_IMETHODIMP
 nsContentAreaDragDrop::DragGesture(nsIDOMEvent* inMouseEvent)
 {
-  // first check that someone hasn't already handled this event
-  PRBool preventDefault = PR_TRUE;
-  nsCOMPtr<nsIDOMNSUIEvent> nsuiEvent(do_QueryInterface(inMouseEvent));
-  if (nsuiEvent) {
-    nsuiEvent->GetPreventDefault(&preventDefault);
-  }
-
-  if (preventDefault) {
-    return NS_OK;
-  }
-
-  // if the client has provided an override callback, check if we
-  // should continue
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
-  GetHookEnumeratorFromEvent(inMouseEvent, getter_AddRefs(enumerator));
-
-  if (enumerator) {
-    PRBool allow = PR_TRUE;
-    PRBool hasMoreHooks = PR_FALSE;
-    while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks))
-           && hasMoreHooks) {
-      nsCOMPtr<nsISupports> isupp;
-      if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
-        break;
-
-      nsCOMPtr<nsIClipboardDragDropHooks> override = do_QueryInterface(isupp);
-      if (override) {
-#ifdef DEBUG
-        nsresult hookResult =
-#endif
-        override->AllowStartDrag(inMouseEvent, &allow);
-        NS_ASSERTION(NS_SUCCEEDED(hookResult),
-                     "hook failure in AllowStartDrag");
-
-        if (!allow)
-          return NS_OK;
-      }
-    }
-  }
-
-  PRBool isSelection = PR_FALSE;
-  nsCOMPtr<nsITransferable> trans;
-  nsTransferableFactory factory(inMouseEvent, static_cast<nsIFlavorDataProvider*>(this));
-  factory.Produce(&isSelection, getter_AddRefs(trans));
-
-  if (trans) {
-    // if the client has provided an override callback, let them manipulate
-    // the flavors or drag data
-    nsCOMPtr<nsISimpleEnumerator> enumerator;
-    GetHookEnumeratorFromEvent(inMouseEvent, getter_AddRefs(enumerator));
-    if (enumerator) {
-      PRBool hasMoreHooks = PR_FALSE;
-      PRBool doContinueDrag = PR_TRUE;
-      while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks))
-             && hasMoreHooks) {
-        nsCOMPtr<nsISupports> isupp;
-        if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
-          break;
-        nsCOMPtr<nsIClipboardDragDropHooks> override =
-          do_QueryInterface(isupp);
+  return NS_OK;
+}
 
-        if (override) {
-#ifdef DEBUG
-          nsresult hookResult =
-#endif
-          override->OnCopyOrDrag(inMouseEvent, trans, &doContinueDrag);
-          NS_ASSERTION(NS_SUCCEEDED(hookResult),
-                       "hook failure in OnCopyOrDrag");
-
-          if (!doContinueDrag) {
-            return NS_OK;
-          }
-        }
-      }
-    }
-
-    nsCOMPtr<nsISupportsArray> transArray =
-      do_CreateInstance("@mozilla.org/supports-array;1");
-    if (!transArray) {
-      return NS_ERROR_FAILURE;
-    }
-
-    transArray->InsertElementAt(trans, 0);
-
-    // kick off the drag
-    nsCOMPtr<nsIDOMEventTarget> target;
-    inMouseEvent->GetTarget(getter_AddRefs(target));
-    nsCOMPtr<nsIDragService> dragService =
-      do_GetService("@mozilla.org/widget/dragservice;1");
+nsresult
+nsContentAreaDragDrop::GetDragData(nsIDOMWindow* aWindow,
+                                   nsIContent* aTarget,
+                                   nsIContent* aSelectionTargetNode,
+                                   PRBool aIsAltKeyPressed,
+                                   nsDOMDataTransfer* aDataTransfer,
+                                   PRBool* aCanDrag,
+                                   PRBool* aDragSelection,
+                                   nsIContent** aDragNode)
+{
+  NS_ENSURE_TRUE(aSelectionTargetNode, NS_ERROR_INVALID_ARG);
 
-    if (!dragService) {
-      return NS_ERROR_FAILURE;
-    }
-
-    PRUint32 action = nsIDragService::DRAGDROP_ACTION_COPY +
-                      nsIDragService::DRAGDROP_ACTION_MOVE +
-                      nsIDragService::DRAGDROP_ACTION_LINK;
-
-    nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(inMouseEvent));
+  *aCanDrag = PR_TRUE;
 
-    if (isSelection) {
-      nsCOMPtr<nsIContent> targetContent(do_QueryInterface(target));
-      nsIDocument* doc = targetContent->GetCurrentDoc();
-      if (doc) {
-        nsIPresShell* presShell = doc->GetPrimaryShell();
-        if (presShell) {
-          nsISelection* selection =
-            presShell->GetCurrentSelection(nsISelectionController::SELECTION_NORMAL);
-          return dragService->InvokeDragSessionWithSelection(selection,
-                                                             transArray,
-                                                             action,
-                                                             mouseEvent);
-        }
-      }
-    }
-
-    nsCOMPtr<nsIDOMNode> targetNode(do_QueryInterface(target));
-    dragService->InvokeDragSessionWithImage(targetNode, transArray, nsnull,
-                                            action, nsnull, 0, 0, mouseEvent);
-  }
-
-  return NS_OK;
+  nsTransferableFactory
+    factory(aWindow, aTarget, aSelectionTargetNode, aIsAltKeyPressed);
+  return factory.Produce(aDataTransfer, aCanDrag, aDragSelection, aDragNode);
 }
 
 
 NS_IMETHODIMP
 nsContentAreaDragDrop::HandleEvent(nsIDOMEvent *event)
 {
   return NS_OK;
 
 }
 
 #if 0
 #pragma mark -
 #endif
 
+NS_IMPL_ISUPPORTS1(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
+
 // SaveURIToFile
 // used on platforms where it's possible to drag items (e.g. images)
 // into the file system
 nsresult
-nsContentAreaDragDrop::SaveURIToFile(nsAString& inSourceURIString,
-                                     nsIFile* inDestFile)
+nsContentAreaDragDropDataProvider::SaveURIToFile(nsAString& inSourceURIString,
+                                                 nsIFile* inDestFile)
 {
   nsCOMPtr<nsIURI> sourceURI;
   nsresult rv = NS_NewURI(getter_AddRefs(sourceURI), inSourceURIString);
   if (NS_FAILED(rv)) {
     return NS_ERROR_FAILURE;
   }
 
   nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
@@ -860,37 +664,37 @@ nsContentAreaDragDrop::SaveURIToFile(nsA
 
   // we rely on the fact that the WPB is refcounted by the channel etc,
   // so we don't keep a ref to it. It will die when finished.
   nsCOMPtr<nsIWebBrowserPersist> persist =
     do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
                       &rv);
   NS_ENSURE_SUCCESS(rv, rv);
 
-  return persist->SaveURI(sourceURI, nsnull, nsnull, nsnull, nsnull,
-                          inDestFile);
+  return persist->SaveURI(sourceURI, nsnull, nsnull, nsnull, nsnull, inDestFile);
 }
 
 // This is our nsIFlavorDataProvider callback. There are several
 // assumptions here that make this work:
 //
 // 1. Someone put a kFilePromiseURLMime flavor into the transferable
 //    with the source URI of the file to save (as a string). We did
-//    that above.
+//    that in AddStringsToDataTransfer.
 //
 // 2. Someone put a kFilePromiseDirectoryMime flavor into the
 //    transferable with an nsILocalFile for the directory we are to
 //    save in. That has to be done by platform-specific code (in
-//    widget), // which gets the destination directory from
-//    OS-specific drag // information.
+//    widget), which gets the destination directory from
+//    OS-specific drag information.
 //
 NS_IMETHODIMP
-nsContentAreaDragDrop::GetFlavorData(nsITransferable *aTransferable,
-                                     const char *aFlavor, nsISupports **aData,
-                                     PRUint32 *aDataLen)
+nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable,
+                                                 const char *aFlavor,
+                                                 nsISupports **aData,
+                                                 PRUint32 *aDataLen)
 {
   NS_ENSURE_ARG_POINTER(aData && aDataLen);
   *aData = nsnull;
   *aDataLen = 0;
 
   nsresult rv = NS_ERROR_NOT_IMPLEMENTED;
 
   if (strcmp(aFlavor, kFilePromiseMime) == 0) {
@@ -932,75 +736,74 @@ nsContentAreaDragDrop::GetFlavorData(nsI
       return NS_ERROR_FAILURE;
 
     nsCOMPtr<nsIFile> file;
     rv = destDirectory->Clone(getter_AddRefs(file));
     NS_ENSURE_SUCCESS(rv, rv);
 
     file->Append(targetFilename);
 
-    // now save the file
     rv = SaveURIToFile(sourceURLString, file);
-
     // send back an nsILocalFile
     if (NS_SUCCEEDED(rv)) {
       CallQueryInterface(file, aData);
       *aDataLen = sizeof(nsIFile*);
     }
   }
 
   return rv;
 }
 
-nsTransferableFactory::nsTransferableFactory(nsIDOMEvent* inMouseEvent,
-                                             nsIFlavorDataProvider *dataProvider)
-  : mInstanceAlreadyUsed(PR_FALSE),
-    mMouseEvent(inMouseEvent),
-    mFlavorDataProvider(dataProvider)
+nsTransferableFactory::nsTransferableFactory(nsIDOMWindow* aWindow,
+                                             nsIContent* aTarget,
+                                             nsIContent* aSelectionTargetNode,
+                                             PRBool aIsAltKeyPressed)
+  : mWindow(aWindow),
+    mTarget(aTarget),
+    mSelectionTargetNode(aSelectionTargetNode),
+    mIsAltKeyPressed(aIsAltKeyPressed)
 {
 }
 
 
 //
 // FindParentLinkNode
 //
 // Finds the parent with the given link tag starting at |inNode|. If
 // it gets up to the root without finding it, we stop looking and
 // return null.
 //
-already_AddRefed<nsIDOMNode>
-nsTransferableFactory::FindParentLinkNode(nsIDOMNode* inNode)
+already_AddRefed<nsIContent>
+nsTransferableFactory::FindParentLinkNode(nsIContent* inNode)
 {
-  nsCOMPtr<nsIContent> content(do_QueryInterface(inNode));
+  nsIContent* content = inNode;
   if (!content) {
     // That must have been the document node; nothing else to do here;
     return nsnull;
   }
 
   for (; content; content = content->GetParent()) {
     if (nsContentUtils::IsDraggableLink(content)) {
-      nsIDOMNode* node = nsnull;
-      CallQueryInterface(content, &node);
-      return node;
+      NS_ADDREF(content);
+      return content;
     }
   }
 
   return nsnull;
 }
 
 
 //
 // GetAnchorURL
 //
 void
-nsTransferableFactory::GetAnchorURL(nsIDOMNode* inNode, nsAString& outURL)
+nsTransferableFactory::GetAnchorURL(nsIContent* inNode, nsAString& outURL)
 {
   nsCOMPtr<nsIURI> linkURI;
-  nsCOMPtr<nsIContent> content = do_QueryInterface(inNode);
-  if (!content || !content->IsLink(getter_AddRefs(linkURI))) {
+  if (!inNode || !inNode->IsLink(getter_AddRefs(linkURI))) {
     // Not a link
     outURL.Truncate();
     return;
   }
 
   nsCAutoString spec;
   linkURI->GetSpec(spec);
   CopyUTF8toUTF16(spec, outURL);
@@ -1032,190 +835,166 @@ nsTransferableFactory::CreateLinkText(co
 
 
 //
 // GetNodeString
 //
 // Gets the text associated with a node
 //
 void
-nsTransferableFactory::GetNodeString(nsIDOMNode* inNode,
+nsTransferableFactory::GetNodeString(nsIContent* inNode,
                                      nsAString & outNodeString)
 {
+  nsCOMPtr<nsIDOMNode> node = do_QueryInterface(inNode);
+
   outNodeString.Truncate();
 
   // use a range to get the text-equivalent of the node
   nsCOMPtr<nsIDOMDocument> doc;
-  inNode->GetOwnerDocument(getter_AddRefs(doc));
+  node->GetOwnerDocument(getter_AddRefs(doc));
   nsCOMPtr<nsIDOMDocumentRange> docRange(do_QueryInterface(doc));
   if (docRange) {
     nsCOMPtr<nsIDOMRange> range;
     docRange->CreateRange(getter_AddRefs(range));
     if (range) {
-      range->SelectNode(inNode);
+      range->SelectNode(node);
       range->ToString(outNodeString);
     }
   }
 }
 
 
 nsresult
-nsTransferableFactory::Produce(PRBool* aDragSelection,
-                               nsITransferable** outTrans)
+nsTransferableFactory::Produce(nsDOMDataTransfer* aDataTransfer,
+                               PRBool* aCanDrag,
+                               PRBool* aDragSelection,
+                               nsIContent** aDragNode)
 {
-  if (mInstanceAlreadyUsed) {
-    return NS_ERROR_FAILURE;
-  }
+  NS_PRECONDITION(aCanDrag && aDragSelection && aDataTransfer && aDragNode,
+                  "null pointer passed to Produce");
+  NS_ASSERTION(mWindow, "window not set");
+  NS_ASSERTION(mSelectionTargetNode, "selection target node should have been set");
 
-  if (!outTrans || !mMouseEvent || !mFlavorDataProvider) {
-    return NS_ERROR_FAILURE;
-  }
+  *aDragNode = nsnull;
 
-  mInstanceAlreadyUsed = PR_TRUE;
-  *outTrans = nsnull;
+  nsIContent* dragNode = nsnull;
 
-  nsCOMPtr<nsIDOMWindow> window;
-  PRBool isAltKeyDown = PR_FALSE;
   mIsAnchor = PR_FALSE;
 
-  {
-    nsCOMPtr<nsIDOMUIEvent> uiEvent(do_QueryInterface(mMouseEvent));
-    if (!uiEvent) {
-      return NS_OK;
-    }
-
-    // find the selection to see what we could be dragging and if
-    // what we're dragging is in what is selected.
-    nsCOMPtr<nsIDOMAbstractView> view;
-    uiEvent->GetView(getter_AddRefs(view));
-    window = do_QueryInterface(view);
-    if (!window) {
-      return NS_OK;
-    }
-  }
-
-  {
-    nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(mMouseEvent));
-    if (mouseEvent) {
-      mouseEvent->GetAltKey(&isAltKeyDown);
-    }
-  }
-
+  // find the selection to see what we could be dragging and if
+  // what we're dragging is in what is selected.
   nsCOMPtr<nsISelection> selection;
-  window->GetSelection(getter_AddRefs(selection));
+  mWindow->GetSelection(getter_AddRefs(selection));
   if (!selection) {
     return NS_OK;
   }
 
+  // check if the node is inside a form control. If so, dragging will be
+  // handled in editor code (nsPlaintextDataTransfer::DoDrag). Don't set
+  // aCanDrag to false however, as we still want to allow the drag.
+  nsCOMPtr<nsIContent> findFormNode = mSelectionTargetNode;
+  nsIContent* findFormParent = findFormNode->GetParent();
+  while (findFormParent) {
+    nsCOMPtr<nsIFormControl> form(do_QueryInterface(findFormParent));
+    if (form && form->GetType() != NS_FORM_OBJECT)
+      return NS_OK;
+    findFormParent = findFormParent->GetParent();
+  }
+    
   // if set, serialize the content under this node
-  nsCOMPtr<nsIDOMNode> nodeToSerialize;
-  PRBool useSelectedText = PR_FALSE;
+  nsCOMPtr<nsIContent> nodeToSerialize;
   *aDragSelection = PR_FALSE;
 
   {
     PRBool haveSelectedContent = PR_FALSE;
 
     // possible parent link node
-    nsCOMPtr<nsIDOMNode> parentLink;
-    nsCOMPtr<nsIDOMNode> draggedNode;
+    nsCOMPtr<nsIContent> parentLink;
+    nsCOMPtr<nsIContent> draggedNode;
 
     {
-      nsCOMPtr<nsIDOMEventTarget> target;
-      mMouseEvent->GetTarget(getter_AddRefs(target));
-
       // only drag form elements by using the alt key,
       // otherwise buttons and select widgets are hard to use
 
       // Note that while <object> elements implement nsIFormControl, we should
       // really allow dragging them if they happen to be images.
-      nsCOMPtr<nsIFormControl> form(do_QueryInterface(target));
-      if (form && !isAltKeyDown && form->GetType() != NS_FORM_OBJECT) {
+      nsCOMPtr<nsIFormControl> form(do_QueryInterface(mTarget));
+      if (form && !mIsAltKeyPressed && form->GetType() != NS_FORM_OBJECT) {
+        *aCanDrag = PR_FALSE;
         return NS_OK;
       }
 
-      draggedNode = do_QueryInterface(target);
+      draggedNode = mTarget;
     }
 
     nsCOMPtr<nsIDOMHTMLAreaElement>   area;   // client-side image map
     nsCOMPtr<nsIImageLoadingContent>  image;
     nsCOMPtr<nsIDOMHTMLAnchorElement> link;
 
-    {
-      // Get the real target and see if it is in the selection
-      nsCOMPtr<nsIDOMNode> realTargetNode;
+    nsCOMPtr<nsIContent> selectedImageOrLinkNode;
+    GetDraggableSelectionData(selection, mSelectionTargetNode,
+                              getter_AddRefs(selectedImageOrLinkNode),
+                              &haveSelectedContent);
 
-      {
-        nsCOMPtr<nsIDOMNSEvent> internalEvent = do_QueryInterface(mMouseEvent);
-        if (internalEvent) {
-          nsCOMPtr<nsIDOMEventTarget> realTarget;
-          internalEvent->GetExplicitOriginalTarget(getter_AddRefs(realTarget));
-          realTargetNode = do_QueryInterface(realTarget);
-        }
+    // either plain text or anchor text is selected
+    if (haveSelectedContent) {
+      link = do_QueryInterface(selectedImageOrLinkNode);
+      if (link && mIsAltKeyPressed) {
+        // if alt is pressed, select the link text instead of drag the link
+        *aCanDrag = PR_FALSE;
+        return NS_OK;
       }
 
-      {
-        nsCOMPtr<nsIDOMNode> selectedImageOrLinkNode;
-        GetDraggableSelectionData(selection, realTargetNode,
-                                  getter_AddRefs(selectedImageOrLinkNode),
-                                  &haveSelectedContent);
-
-        // either plain text or anchor text is selected
-        if (haveSelectedContent) {
-          link = do_QueryInterface(selectedImageOrLinkNode);
-          if (link && isAltKeyDown) {
-            return NS_OK;
-          }
+      *aDragSelection = PR_TRUE;
+    } else if (selectedImageOrLinkNode) {
+      // an image is selected
+      image = do_QueryInterface(selectedImageOrLinkNode);
+    } else {
+      // nothing is selected -
+      //
+      // look for draggable elements under the mouse
+      //
+      // if the alt key is down, don't start a drag if we're in an
+      // anchor because we want to do selection.
+      parentLink = FindParentLinkNode(draggedNode);
+      if (parentLink && mIsAltKeyPressed) {
+        *aCanDrag = PR_FALSE;
+        return NS_OK;
+      }
 
-          useSelectedText = PR_TRUE;
-          *aDragSelection = PR_TRUE;
-        } else if (selectedImageOrLinkNode) {
-          // an image is selected
-          image = do_QueryInterface(selectedImageOrLinkNode);
-        } else {
-          // nothing is selected -
-          //
-          // look for draggable elements under the mouse
-          //
-          // if the alt key is down, don't start a drag if we're in an
-          // anchor because we want to do selection.
-          parentLink = FindParentLinkNode(draggedNode);
-          if (parentLink && isAltKeyDown) {
-            return NS_OK;
-          }
-
-          area  = do_QueryInterface(draggedNode);
-          image = do_QueryInterface(draggedNode);
-          link  = do_QueryInterface(draggedNode);
-        }
-      }
+      area  = do_QueryInterface(draggedNode);
+      image = do_QueryInterface(draggedNode);
+      link  = do_QueryInterface(draggedNode);
     }
 
     {
       // set for linked images, and links
-      nsCOMPtr<nsIDOMNode> linkNode;
+      nsCOMPtr<nsIContent> linkNode;
 
       if (area) {
         // use the alt text (or, if missing, the href) as the title
         area->GetAttribute(NS_LITERAL_STRING("alt"), mTitleString);
         if (mTitleString.IsEmpty()) {
           // this can be a relative link
           area->GetAttribute(NS_LITERAL_STRING("href"), mTitleString);
         }
 
         // we'll generate HTML like <a href="absurl">alt text</a>
         mIsAnchor = PR_TRUE;
 
         // gives an absolute link
-        GetAnchorURL(area, mUrlString);
+        GetAnchorURL(draggedNode, mUrlString);
 
         mHtmlString.AssignLiteral("<a href=\"");
         mHtmlString.Append(mUrlString);
         mHtmlString.AppendLiteral("\">");
         mHtmlString.Append(mTitleString);
         mHtmlString.AppendLiteral("</a>");
+
+        dragNode = draggedNode;
       } else if (image) {
         mIsAnchor = PR_TRUE;
         // grab the href as the url, use alt text as the title of the
         // area if it's there.  the drag data is the image tag and src
         // attribute.
         nsCOMPtr<nsIURI> imageURI;
         image->GetCurrentURI(getter_AddRefs(imageURI));
         if (imageURI) {
@@ -1305,45 +1084,47 @@ nsTransferableFactory::Produce(PRBool* a
         }
 
         if (parentLink) {
           // If we are dragging around an image in an anchor, then we
           // are dragging the entire anchor
           linkNode = parentLink;
           nodeToSerialize = linkNode;
         } else {
-          nodeToSerialize = draggedNode;
+          nodeToSerialize = do_QueryInterface(draggedNode);
         }
+        dragNode = nodeToSerialize;
       } else if (link) {
         // set linkNode. The code below will handle this
-        linkNode = link;    // XXX test this
+        linkNode = do_QueryInterface(link);    // XXX test this
         GetNodeString(draggedNode, mTitleString);
       } else if (parentLink) {
         // parentLink will always be null if there's selected content
         linkNode = parentLink;
         nodeToSerialize = linkNode;
       } else if (!haveSelectedContent) {
         // nothing draggable
         return NS_OK;
       }
 
       if (linkNode) {
         mIsAnchor = PR_TRUE;
         GetAnchorURL(linkNode, mUrlString);
+        dragNode = linkNode;
       }
     }
   }
 
-  if (nodeToSerialize || useSelectedText) {
+  if (nodeToSerialize || *aDragSelection) {
     // if we have selected text, use it in preference to the node
-    if (useSelectedText) {
+    if (*aDragSelection) {
       nodeToSerialize = nsnull;
     }
 
-    SerializeNodeOrSelection(window, nodeToSerialize,
+    SerializeNodeOrSelection(mWindow, nodeToSerialize,
                              mHtmlString, mContextString, mInfoString);
 
     nsCOMPtr<nsIFormatConverter> htmlConverter =
       do_CreateInstance(kHTMLConverterCID);
     NS_ENSURE_TRUE(htmlConverter, NS_ERROR_FAILURE);
 
     nsCOMPtr<nsISupportsString> html =
       do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
@@ -1369,173 +1150,141 @@ nsTransferableFactory::Produce(PRBool* a
   if (mTitleString.IsEmpty()) {
     mTitleString = mUrlString;
   }
 
   // if we haven't constructed a html version, make one now
   if (mHtmlString.IsEmpty() && !mUrlString.IsEmpty())
     CreateLinkText(mUrlString, mTitleString, mHtmlString);
 
-  return ConvertStringsToTransferable(outTrans);
+  // if there is no drag node, which will be the case for a selection, just
+  // use the selection target node.
+  nsresult rv = AddStringsToDataTransfer(
+           dragNode ? dragNode : mSelectionTargetNode.get(), aDataTransfer);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  NS_IF_ADDREF(*aDragNode = dragNode);
+  return NS_OK;
+}
+
+void
+nsTransferableFactory::AddString(nsDOMDataTransfer* aDataTransfer,
+                                 const nsAString& aFlavor,
+                                 const nsAString& aData,
+                                 nsIPrincipal* aPrincipal)
+{
+  nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+  if (variant) {
+    variant->SetAsAString(aData);
+    aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal);
+  }
 }
 
 nsresult
-nsTransferableFactory::ConvertStringsToTransferable(nsITransferable** outTrans)
+nsTransferableFactory::AddStringsToDataTransfer(nsIContent* aDragNode,
+                                                nsDOMDataTransfer* aDataTransfer)
 {
-  // now create the transferable and stuff data into it.
-  nsCOMPtr<nsITransferable> trans =
-    do_CreateInstance("@mozilla.org/widget/transferable;1");
-  NS_ENSURE_TRUE(trans, NS_ERROR_FAILURE);
+  NS_ASSERTION(aDragNode, "adding strings for null node");
+
+  // set all of the data to have the principal of the node where the data came from
+  nsIPrincipal* principal = aDragNode->NodePrincipal();
 
   // add a special flavor if we're an anchor to indicate that we have
   // a URL in the drag data
   if (!mUrlString.IsEmpty() && mIsAnchor) {
     nsAutoString dragData(mUrlString);
     dragData.AppendLiteral("\n");
     dragData += mTitleString;
 
-    nsCOMPtr<nsISupportsString> urlPrimitive =
-      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-    NS_ENSURE_TRUE(urlPrimitive, NS_ERROR_FAILURE);
-
-    urlPrimitive->SetData(dragData);
-    trans->SetTransferData(kURLMime, urlPrimitive,
-                           dragData.Length() * sizeof(PRUnichar));
-
-    nsCOMPtr<nsISupportsString> urlDataPrimitive =
-      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-    NS_ENSURE_TRUE(urlDataPrimitive, NS_ERROR_FAILURE);
-
-    urlDataPrimitive->SetData(mUrlString);
-    trans->SetTransferData(kURLDataMime, urlDataPrimitive,
-                           mUrlString.Length() * sizeof(PRUnichar));
-
-    nsCOMPtr<nsISupportsString> urlDescPrimitive =
-      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-    NS_ENSURE_TRUE(urlDescPrimitive, NS_ERROR_FAILURE);
-
-    urlDescPrimitive->SetData(mTitleString);
-    trans->SetTransferData(kURLDescriptionMime, urlDescPrimitive,
-                           mTitleString.Length() * sizeof(PRUnichar));
+    AddString(aDataTransfer, NS_LITERAL_STRING(kURLMime), dragData, principal);
+    AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
+    AddString(aDataTransfer, NS_LITERAL_STRING(kURLDescriptionMime), mTitleString, principal);
+    AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
   }
 
   // add a special flavor, even if we don't have html context data
-  nsCOMPtr<nsISupportsString> context =
-    do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-  NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
-
-  nsAutoString contextData(mContextString);
-  context->SetData(contextData);
-  trans->SetTransferData(kHTMLContext, context, contextData.Length() * 2);
+  AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLContext), mContextString, principal);
 
   // add a special flavor if we have html info data
-  if (!mInfoString.IsEmpty()) {
-    nsCOMPtr<nsISupportsString> info =
-      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-    NS_ENSURE_TRUE(info, NS_ERROR_FAILURE);
-
-    nsAutoString infoData(mInfoString);
-    info->SetData(infoData);
-    trans->SetTransferData(kHTMLInfo, info, infoData.Length() * 2);
-  }
+  if (!mInfoString.IsEmpty())
+    AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLInfo), mInfoString, principal);
 
   // add the full html
-  nsCOMPtr<nsISupportsString> htmlPrimitive =
-    do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-  NS_ENSURE_TRUE(htmlPrimitive, NS_ERROR_FAILURE);
-
-  htmlPrimitive->SetData(mHtmlString);
-  trans->SetTransferData(kHTMLMime, htmlPrimitive,
-                         mHtmlString.Length() * sizeof(PRUnichar));
+  AddString(aDataTransfer, NS_LITERAL_STRING(kHTMLMime), mHtmlString, principal);
 
-  // add the plain (unicode) text. we use the url for text/unicode
-  // data if an anchor is being dragged, rather than the title text of
-  // the link or the alt text for an anchor image.
-  nsCOMPtr<nsISupportsString> textPrimitive =
-    do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-  NS_ENSURE_TRUE(textPrimitive, NS_ERROR_FAILURE);
-
-  textPrimitive->SetData(mIsAnchor ? mUrlString : mTitleString);
-  trans->SetTransferData(kUnicodeMime, textPrimitive,
-                         (mIsAnchor ? mUrlString.Length() :
-                          mTitleString.Length()) * sizeof(PRUnichar));
+  // add the plain text. we use the url for text/plain data if an anchor is
+  // being dragged, rather than the title text of the link or the alt text for
+  // an anchor image.
+  AddString(aDataTransfer, NS_LITERAL_STRING(kTextMime),
+            mIsAnchor ? mUrlString : mTitleString, principal);
 
   // add image data, if present. For now, all we're going to do with
   // this is turn it into a native data flavor, so indicate that with
   // a new flavor so as not to confuse anyone who is really registered
   // for image/gif or image/jpg.
   if (mImage) {
-    nsCOMPtr<nsISupportsInterfacePointer> ptrPrimitive =
-      do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
-    NS_ENSURE_TRUE(ptrPrimitive, NS_ERROR_FAILURE);
+    nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+    if (variant) {
+      variant->SetAsISupports(mImage);
+      aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kNativeImageMime),
+                                          variant, 0, principal);
+    }
 
-    ptrPrimitive->SetData(mImage);
-    trans->SetTransferData(kNativeImageMime, ptrPrimitive,
-                           sizeof(nsISupportsInterfacePointer*));
     // assume the image comes from a file, and add a file promise. We
     // register ourselves as a nsIFlavorDataProvider, and will use the
     // GetFlavorData callback to save the image to disk.
-    trans->SetTransferData(kFilePromiseMime, mFlavorDataProvider,
-                           nsITransferable::kFlavorHasDataProvider);
-
-    nsCOMPtr<nsISupportsString> imageUrlPrimitive =
-      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-    NS_ENSURE_TRUE(imageUrlPrimitive, NS_ERROR_FAILURE);
 
-    imageUrlPrimitive->SetData(mImageSourceString);
-    trans->SetTransferData(kFilePromiseURLMime, imageUrlPrimitive,
-                           mImageSourceString.Length() * sizeof(PRUnichar));
+    nsCOMPtr<nsIFlavorDataProvider> dataProvider =
+      new nsContentAreaDragDropDataProvider();
+    if (dataProvider) {
+      nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+      if (variant) {
+        variant->SetAsISupports(dataProvider);
+        aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime),
+                                            variant, 0, principal);
+      }
+    }
 
-    nsCOMPtr<nsISupportsString> imageFileNamePrimitive =
-      do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-    NS_ENSURE_TRUE(imageFileNamePrimitive, NS_ERROR_FAILURE);
-
-    imageFileNamePrimitive->SetData(mImageDestFileName);
-    trans->SetTransferData(kFilePromiseDestFilename, imageFileNamePrimitive,
-                           mImageDestFileName.Length() * sizeof(PRUnichar));
+    AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseURLMime),
+              mImageSourceString, principal);
+    AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseDestFilename),
+              mImageDestFileName, principal);
 
     // if not an anchor, add the image url
     if (!mIsAnchor) {
-      nsCOMPtr<nsISupportsString> urlDataPrimitive =
-        do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID);
-      NS_ENSURE_TRUE(urlDataPrimitive, NS_ERROR_FAILURE);
-
-      urlDataPrimitive->SetData(mUrlString);
-      trans->SetTransferData(kURLDataMime, urlDataPrimitive,
-                             mUrlString.Length() * sizeof(PRUnichar));
+      AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
+      AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
     }
   }
 
-  *outTrans = trans;
-  NS_IF_ADDREF(*outTrans);
-
   return NS_OK;
 }
 
 // note that this can return NS_OK, but a null out param (by design)
 // static
 nsresult
 nsTransferableFactory::GetDraggableSelectionData(nsISelection* inSelection,
-                                                 nsIDOMNode* inRealTargetNode,
-                                                 nsIDOMNode **outImageOrLinkNode,
+                                                 nsIContent* inRealTargetNode,
+                                                 nsIContent **outImageOrLinkNode,
                                                  PRBool* outDragSelectedText)
 {
   NS_ENSURE_ARG(inSelection);
   NS_ENSURE_ARG(inRealTargetNode);
   NS_ENSURE_ARG_POINTER(outImageOrLinkNode);
 
   *outImageOrLinkNode = nsnull;
   *outDragSelectedText = PR_FALSE;
 
   PRBool selectionContainsTarget = PR_FALSE;
 
   PRBool isCollapsed = PR_FALSE;
   inSelection->GetIsCollapsed(&isCollapsed);
   if (!isCollapsed) {
-    inSelection->ContainsNode(inRealTargetNode, PR_FALSE,
+    nsCOMPtr<nsIDOMNode> realTargetNode = do_QueryInterface(inRealTargetNode);
+    inSelection->ContainsNode(realTargetNode, PR_FALSE,
                               &selectionContainsTarget);
 
     if (selectionContainsTarget) {
       // track down the anchor node, if any, for the url
       nsCOMPtr<nsIDOMNode> selectionStart;
       inSelection->GetAnchorNode(getter_AddRefs(selectionStart));
 
       nsCOMPtr<nsIDOMNode> selectionEnd;
@@ -1559,18 +1308,17 @@ nsTransferableFactory::GetDraggableSelec
             if (selStartContent) {
               PRInt32 childOffset =
                 (anchorOffset < focusOffset) ? anchorOffset : focusOffset;
               nsIContent *childContent =
                 selStartContent->GetChildAt(childOffset);
               // if we find an image, we'll fall into the node-dragging code,
               // rather the the selection-dragging code
               if (nsContentUtils::IsDraggableImage(childContent)) {
-                CallQueryInterface(childContent, outImageOrLinkNode);
-
+                NS_ADDREF(*outImageOrLinkNode = childContent);
                 return NS_OK;
               }
             }
           }
         }
       }
 
       // see if the selection is a link;  if so, its node will be returned
@@ -1581,30 +1329,31 @@ nsTransferableFactory::GetDraggableSelec
     }
   }
 
   return NS_OK;
 }
 
 // static
 void nsTransferableFactory::GetSelectedLink(nsISelection* inSelection,
-                                            nsIDOMNode **outLinkNode)
+                                            nsIContent **outLinkNode)
 {
   *outLinkNode = nsnull;
 
-  nsCOMPtr<nsIDOMNode> selectionStart;
-  inSelection->GetAnchorNode(getter_AddRefs(selectionStart));
-  nsCOMPtr<nsIDOMNode> selectionEnd;
-  inSelection->GetFocusNode(getter_AddRefs(selectionEnd));
+  nsCOMPtr<nsIDOMNode> selectionStartNode;
+  inSelection->GetAnchorNode(getter_AddRefs(selectionStartNode));
+  nsCOMPtr<nsIDOMNode> selectionEndNode;
+  inSelection->GetFocusNode(getter_AddRefs(selectionEndNode));
 
   // simple case:  only one node is selected
   // see if it or its parent is an anchor, then exit
 
-  if (selectionStart == selectionEnd) {
-    nsCOMPtr<nsIDOMNode> link = FindParentLinkNode(selectionStart);
+  if (selectionStartNode == selectionEndNode) {
+    nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode);
+    nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart);
     if (link) {
       link.swap(*outLinkNode);
     }
 
     return;
   }
 
   // more complicated case:  multiple nodes are selected
@@ -1622,87 +1371,89 @@ void nsTransferableFactory::GetSelectedL
     nsCOMPtr<nsIDOMRange> range;
     inSelection->GetRangeAt(0, getter_AddRefs(range));
     if (!range) {
       return;
     }
 
     nsCOMPtr<nsIDOMNode> tempNode;
     range->GetStartContainer( getter_AddRefs(tempNode));
-    if (tempNode != selectionStart) {
-      selectionEnd = selectionStart;
-      selectionStart = tempNode;
+    if (tempNode != selectionStartNode) {
+      selectionEndNode = selectionStartNode;
+      selectionStartNode = tempNode;
       inSelection->GetAnchorOffset(&endOffset);
       inSelection->GetFocusOffset(&startOffset);
     } else {
       inSelection->GetAnchorOffset(&startOffset);
       inSelection->GetFocusOffset(&endOffset);
     }
   }
 
   // trim leading node if the string is empty or
   // the selection starts at the end of the text
 
   nsAutoString nodeStr;
-  selectionStart->GetNodeValue(nodeStr);
+  selectionStartNode->GetNodeValue(nodeStr);
   if (nodeStr.IsEmpty() ||
       startOffset+1 >= static_cast<PRInt32>(nodeStr.Length())) {
-    nsCOMPtr<nsIDOMNode> curr = selectionStart;
+    nsCOMPtr<nsIDOMNode> curr = selectionStartNode;
     nsIDOMNode* next;
 
     while (curr) {
       curr->GetNextSibling(&next);
 
       if (next) {
-        selectionStart = dont_AddRef(next);
+        selectionStartNode = dont_AddRef(next);
         break;
       }
 
       curr->GetParentNode(&next);
       curr = dont_AddRef(next);
     }
   }
 
   // trim trailing node if the selection ends before its text begins
 
   if (endOffset == 0) {
-    nsCOMPtr<nsIDOMNode> curr = selectionEnd;
+    nsCOMPtr<nsIDOMNode> curr = selectionEndNode;
     nsIDOMNode* next;
 
     while (curr) {
       curr->GetPreviousSibling(&next);
 
       if (next){
-        selectionEnd = dont_AddRef(next);
+        selectionEndNode = dont_AddRef(next);
         break;
       }
 
       curr->GetParentNode(&next);
       curr = dont_AddRef(next);
     }
   }
 
   // see if the leading & trailing nodes are part of the
   // same anchor - if so, return the anchor node
-  nsCOMPtr<nsIDOMNode> link = FindParentLinkNode(selectionStart);
+  nsCOMPtr<nsIContent> selectionStart = do_QueryInterface(selectionStartNode);
+  nsCOMPtr<nsIContent> link = FindParentLinkNode(selectionStart);
   if (link) {
-    nsCOMPtr<nsIDOMNode> link2 = FindParentLinkNode(selectionEnd);
+    nsCOMPtr<nsIContent> selectionEnd = do_QueryInterface(selectionEndNode);
+    nsCOMPtr<nsIContent> link2 = FindParentLinkNode(selectionEnd);
 
     if (link == link2) {
       NS_IF_ADDREF(*outLinkNode = link);
     }
   }
 
   return;
 }
 
 // static
 nsresult
 nsTransferableFactory::SerializeNodeOrSelection(nsIDOMWindow* inWindow,
-                                                nsIDOMNode* inNode,
+                                                nsIContent* inNode,
                                                 nsAString& outResultString,
                                                 nsAString& outContext,
                                                 nsAString& outInfo)
 {
   NS_ENSURE_ARG_POINTER(inWindow);
 
   nsresult rv;
   nsCOMPtr<nsIDocumentEncoder> encoder =
@@ -1712,21 +1463,22 @@ nsTransferableFactory::SerializeNodeOrSe
   nsCOMPtr<nsIDOMDocument> domDoc;
   inWindow->GetDocument(getter_AddRefs(domDoc));
   NS_ENSURE_TRUE(domDoc, NS_ERROR_FAILURE);
 
   PRUint32 flags = nsIDocumentEncoder::OutputAbsoluteLinks |
                    nsIDocumentEncoder::OutputEncodeHTMLEntities;
   nsCOMPtr<nsIDOMRange> range;
   nsCOMPtr<nsISelection> selection;
-  if (inNode) {
+  nsCOMPtr<nsIDOMNode> node = do_QueryInterface(inNode);
+  if (node) {
     // make a range around this node
     rv = NS_NewRange(getter_AddRefs(range));
     NS_ENSURE_SUCCESS(rv, rv);
-    rv = range->SelectNode(inNode);
+    rv = range->SelectNode(node);
     NS_ENSURE_SUCCESS(rv, rv);
   } else {
     inWindow->GetSelection(getter_AddRefs(selection));
     flags |= nsIDocumentEncoder::OutputSelectionOnly;
   }
 
   rv = encoder->Init(domDoc, NS_LITERAL_STRING(kHTMLMime), flags);
   NS_ENSURE_SUCCESS(rv, rv);
--- a/content/base/src/nsContentAreaDragDrop.h
+++ b/content/base/src/nsContentAreaDragDrop.h
@@ -53,16 +53,17 @@ class nsITransferable;
 class nsIImage;
 class nsIPresShell;
 class nsPresContext;
 class nsIContent;
 class nsIDocument;
 class nsIURI;
 class nsIFile;
 class nsISimpleEnumerator;
+class nsDOMDataTransfer;
 
 // {1f34bc80-1bc7-11d6-a384-d705dd0746fc}
 #define NS_CONTENTAREADRAGDROP_CID             \
 { 0x1f34bc80, 0x1bc7, 0x11d6, \
   { 0xa3, 0x84, 0xd7, 0x05, 0xdd, 0x07, 0x46, 0xfc } }
 
 #define NS_CONTENTAREADRAGDROP_CONTRACTID "@mozilla.org:/content/content-area-dragdrop;1"
 
@@ -71,64 +72,98 @@ class nsISimpleEnumerator;
 // class nsContentAreaDragDrop
 //
 // The class that listens to the chrome events handles anything
 // related to drag and drop. Registers itself with the DOM with
 // AddChromeListeners() and removes itself with
 // RemoveChromeListeners().
 //
 class nsContentAreaDragDrop : public nsIDOMDragListener,
-                              public nsIDragDropHandler,
-                              public nsIFlavorDataProvider
+                              public nsIDragDropHandler
 {
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIDRAGDROPHANDLER
-  NS_DECL_NSIFLAVORDATAPROVIDER
   
   nsContentAreaDragDrop();
   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);
 
+  /**
+   * Determine what data in the content area, if any, is being dragged.
+   *
+   * aWindow - the window containing the target node
+   * aTarget - the mousedown event target that started the drag
+   * aSelectionTargetNode - the node where the drag event should be fired
+   * aIsAltKeyPressed - true if the Alt key is pressed. In some cases, this
+   *                    will prevent the drag from occuring. For example,
+   *                    holding down Alt over a link should select the text,
+   *                    not drag the link.
+   * aDataTransfer - the dataTransfer for the drag event.
+   * aCanDrag - [out] set to true if the drag may proceed, false to stop the
+   *            drag entirely
+   * aDragSelection - [out] set to true to indicate that a selection is being
+   *                  dragged, rather than a specific node
+   * aDragNode - [out] the link, image or area being dragged, or null if the
+   *             drag occured on another element.
+   */
+  static nsresult GetDragData(nsIDOMWindow* aWindow,
+                              nsIContent* aTarget,
+                              nsIContent* aSelectionTargetNode,
+                              PRBool aIsAltKeyPressed,
+                              nsDOMDataTransfer* aDataTransfer,
+                              PRBool* aCanDrag,
+                              PRBool* aDragSelection,
+                              nsIContent** aDragNode);
+
 private:
 
   // Add/remove the relevant listeners
   nsresult AddDragListener();
   nsresult RemoveDragListener();
 
   // utility routines
   static void NormalizeSelection(nsIDOMNode* inBaseNode,
                                  nsISelection* inSelection);
   static void GetEventDocument(nsIDOMEvent* inEvent,
                                nsIDOMDocument** outDocument);
 
-  static nsresult SaveURIToFile(nsAString& inSourceURIString,
-                                nsIFile* inDestFile);
-
-  void ExtractURLFromData(const nsACString & inFlavor,
-                          nsISupports* inDataWrapper, PRUint32 inDataLen,
-                          nsAString & outURL);
-  nsresult GetHookEnumeratorFromEvent(nsIDOMEvent* inEvent,
-                                      nsISimpleEnumerator** outEnumerator);
+  static void ExtractURLFromData(const nsACString & inFlavor,
+                                 nsISupports* inDataWrapper, PRUint32 inDataLen,
+                                 nsAString & outURL);
 
   PRPackedBool mListenerInstalled;
 
   nsCOMPtr<nsPIDOMEventTarget> mEventTarget;
 
   // weak ref, this is probably my owning webshell
   // FIXME: we set this and never null it out.  That's bad!  See bug 332187.
   nsIWebNavigation* mNavigator;
 
 };
 
+// this is used to save images to disk lazily when the image data is asked for
+// during the drop instead of when it is added to the drag data transfer. This
+// ensures that the image data is only created when an image drop is allowed.
+class nsContentAreaDragDropDataProvider : public nsIFlavorDataProvider
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIFLAVORDATAPROVIDER
+
+  virtual ~nsContentAreaDragDropDataProvider() {}
+
+  nsresult SaveURIToFile(nsAString& inSourceURIString,
+                         nsIFile* inDestFile);
+};
 
 
 #endif /* nsContentAreaDragDrop_h__ */
 
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -149,16 +149,17 @@ static NS_DEFINE_CID(kXTFServiceCID, NS_
 #include "nsIPermissionManager.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIRunnable.h"
 #include "nsDOMJSUtils.h"
 #include "nsGenericHTMLElement.h"
 #include "nsAttrValue.h"
 #include "nsReferencedElement.h"
 #include "nsIUGenCategory.h"
+#include "nsIDragService.h"
 
 #ifdef IBMBIDI
 #include "nsIBidiKeyboard.h"
 #endif
 #include "nsCycleCollectionParticipant.h"
 
 // for ReportToConsole
 #include "nsIStringBundle.h"
@@ -412,23 +413,26 @@ nsContentUtils::InitializeEventTable() {
     { &nsGkAtoms::onclose,                       { NS_XUL_CLOSE, EventNameType_XUL }},
     { &nsGkAtoms::onpopupshowing,                { NS_XUL_POPUP_SHOWING, EventNameType_XUL }},
     { &nsGkAtoms::onpopupshown,                  { NS_XUL_POPUP_SHOWN, EventNameType_XUL }},
     { &nsGkAtoms::onpopuphiding,                 { NS_XUL_POPUP_HIDING, EventNameType_XUL }},
     { &nsGkAtoms::onpopuphidden,                 { NS_XUL_POPUP_HIDDEN, EventNameType_XUL }},
     { &nsGkAtoms::oncommand,                     { NS_XUL_COMMAND, EventNameType_XUL }},
     { &nsGkAtoms::onbroadcast,                   { NS_XUL_BROADCAST, EventNameType_XUL }},
     { &nsGkAtoms::oncommandupdate,               { NS_XUL_COMMAND_UPDATE, EventNameType_XUL }},
-    { &nsGkAtoms::ondragenter,                   { NS_DRAGDROP_ENTER, EventNameType_XUL }},
-    { &nsGkAtoms::ondragover,                    { NS_DRAGDROP_OVER_SYNTH, EventNameType_XUL }},
+    { &nsGkAtoms::ondragenter,                   { NS_DRAGDROP_ENTER, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::ondragover,                    { NS_DRAGDROP_OVER_SYNTH, EventNameType_HTMLXUL }},
     { &nsGkAtoms::ondragexit,                    { NS_DRAGDROP_EXIT_SYNTH, EventNameType_XUL }},
-    { &nsGkAtoms::ondragdrop,                    { NS_DRAGDROP_DROP, EventNameType_XUL }},
+    { &nsGkAtoms::ondragdrop,                    { NS_DRAGDROP_DRAGDROP, EventNameType_XUL }},
     { &nsGkAtoms::ondraggesture,                 { NS_DRAGDROP_GESTURE, EventNameType_XUL }},
-    { &nsGkAtoms::ondrag,                        { NS_DRAGDROP_DRAG, EventNameType_XUL }},
-    { &nsGkAtoms::ondragend,                     { NS_DRAGDROP_END, EventNameType_XUL }},
+    { &nsGkAtoms::ondrag,                        { NS_DRAGDROP_DRAG, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::ondragend,                     { NS_DRAGDROP_END, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::ondragstart,                   { NS_DRAGDROP_START, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::ondragleave,                   { NS_DRAGDROP_LEAVE_SYNTH, EventNameType_HTMLXUL }},
+    { &nsGkAtoms::ondrop,                        { NS_DRAGDROP_DROP, EventNameType_HTMLXUL }},
     { &nsGkAtoms::onoverflow,                    { NS_SCROLLPORT_OVERFLOW, EventNameType_XUL }},
     { &nsGkAtoms::onunderflow,                   { NS_SCROLLPORT_UNDERFLOW, EventNameType_XUL }}
 #ifdef MOZ_SVG
    ,{ &nsGkAtoms::onSVGLoad,                     { NS_SVG_LOAD, EventNameType_None }},
     { &nsGkAtoms::onSVGUnload,                   { NS_SVG_UNLOAD, EventNameType_None }},
     { &nsGkAtoms::onSVGAbort,                    { NS_SVG_ABORT, EventNameType_None }},
     { &nsGkAtoms::onSVGError,                    { NS_SVG_ERROR, EventNameType_None }},
     { &nsGkAtoms::onSVGResize,                   { NS_SVG_RESIZE, EventNameType_None }},
@@ -4303,16 +4307,28 @@ nsContentUtils::HidePopupsInDocument(nsI
     nsCOMPtr<nsIDocShellTreeItem> docShellToHide = do_QueryInterface(container);
     if (docShellToHide)
       pm->HidePopupsInDocShell(docShellToHide);
   }
 #endif
 }
 
 /* static */
+already_AddRefed<nsIDragSession>
+nsContentUtils::GetDragSession()
+{
+  nsIDragSession* dragSession = nsnull;
+  nsCOMPtr<nsIDragService> dragService =
+    do_GetService("@mozilla.org/widget/dragservice;1");
+  if (dragService)
+    dragService->GetCurrentSession(&dragSession);
+  return dragSession;
+}
+
+/* static */
 PRBool
 nsContentUtils::URIIsLocalFile(nsIURI *aURI)
 {
   PRBool isFile;
   nsCOMPtr<nsINetUtil> util = do_QueryInterface(sIOService);
 
   return util && NS_SUCCEEDED(util->ProtocolHasFlags(aURI,
                                 nsIProtocolHandler::URI_IS_LOCAL_FILE,
--- a/content/base/src/nsGkAtomList.h
+++ b/content/base/src/nsGkAtomList.h
@@ -305,20 +305,24 @@ GK_ATOM(DOMNodeRemovedFromDocument, "DOM
 GK_ATOM(DOMSubtreeModified, "DOMSubtreeModified")
 GK_ATOM(double_, "double")
 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(draggable, "draggable")
 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")
--- a/content/events/public/nsIPrivateDOMEvent.h
+++ b/content/events/public/nsIPrivateDOMEvent.h
@@ -78,16 +78,18 @@ nsresult
 NS_NewDOMDataContainerEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, nsEvent *aEvent);
 nsresult
 NS_NewDOMUIEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsGUIEvent *aEvent);
 nsresult
 NS_NewDOMMouseEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsInputEvent *aEvent);
 nsresult
 NS_NewDOMMouseScrollEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsInputEvent *aEvent);
 nsresult
+NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsDragEvent *aEvent);
+nsresult
 NS_NewDOMKeyboardEvent(nsIDOMEvent** aInstancePtrResult, nsPresContext* aPresContext, class nsKeyEvent *aEvent);
 nsresult
 NS_NewDOMMutationEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsMutationEvent* aEvent);
 nsresult
 NS_NewDOMPopupBlockedEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsPopupBlockedEvent* aEvent);
 nsresult
 NS_NewDOMTextEvent(nsIDOMEvent** aResult, nsPresContext* aPresContext, class nsTextEvent* aEvent);
 nsresult
--- a/content/events/src/Makefile.in
+++ b/content/events/src/Makefile.in
@@ -71,32 +71,34 @@ CPPSRCS		= \
 		nsEventStateManager.cpp \
 		nsDOMEvent.cpp \
 		nsDOMDataContainerEvent.cpp \
 		nsDOMUIEvent.cpp \
 		nsDOMKeyboardEvent.cpp \
 		nsDOMTextEvent.cpp \
 		nsDOMMouseEvent.cpp \
 		nsDOMMouseScrollEvent.cpp \
+		nsDOMDragEvent.cpp \
 		nsDOMMutationEvent.cpp \
 		nsDOMPopupBlockedEvent.cpp \
 		nsDOMBeforeUnloadEvent.cpp \
 		nsDOMPageTransitionEvent.cpp \
 		nsDOMXULCommandEvent.cpp \
 		nsDOMCommandEvent.cpp \
 		nsDOMMessageEvent.cpp \
 		nsPrivateTextRange.cpp \
 		nsDOMEventGroup.cpp \
 		nsXMLEventsManager.cpp \
 		nsXMLEventsElement.cpp \
 		nsPLDOMEvent.cpp \
 		nsEventDispatcher.cpp \
 		nsIMEStateManager.cpp \
 		nsQueryContentEventHandler.cpp \
 		nsDOMProgressEvent.cpp \
+		nsDOMDataTransfer.cpp \
 		$(NULL)
 
 # we don't want the shared lib, but we want to force the creation of a static lib.
 FORCE_STATIC_LIB = 1
 
 include $(topsrcdir)/config/rules.mk
 
 LOCAL_INCLUDES	= \
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDataTransfer.cpp
@@ -0,0 +1,768 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMDataTransfer.h"
+
+#include "prlog.h"
+#include "nsAutoPtr.h"
+#include "nsString.h"
+#include "nsIServiceManager.h"
+#include "nsIVariant.h"
+#include "nsISupportsPrimitives.h"
+#include "nsDOMClassInfo.h"
+#include "nsDOMLists.h"
+#include "nsGUIEvent.h"
+#include "nsDOMError.h"
+#include "nsIDragService.h"
+#include "nsIScriptableRegion.h"
+#include "nsContentUtils.h"
+
+NS_IMPL_CYCLE_COLLECTION_2(nsDOMDataTransfer, mDragTarget, mDragImage)
+
+NS_IMPL_CYCLE_COLLECTING_ADDREF(nsDOMDataTransfer)
+NS_IMPL_CYCLE_COLLECTING_RELEASE(nsDOMDataTransfer)
+
+NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsDOMDataTransfer)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMNSDataTransfer)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer)
+  NS_INTERFACE_MAP_ENTRY_DOM_CLASSINFO(DataTransfer)
+NS_INTERFACE_MAP_END
+
+// the size of the array
+const char nsDOMDataTransfer::sEffects[8][9] = {
+  "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"
+};
+
+nsDOMDataTransfer::nsDOMDataTransfer()
+  : mEventType(NS_DRAGDROP_START),
+    mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
+    mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
+    mReadOnly(PR_FALSE),
+    mIsExternal(PR_FALSE),
+    mDragImageX(0),
+    mDragImageY(0)
+{
+}
+
+nsDOMDataTransfer::nsDOMDataTransfer(PRUint32 aEventType, PRUint32 aAction)
+  : mEventType(aEventType),
+    mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
+    mReadOnly(PR_TRUE),
+    mIsExternal(PR_TRUE),
+    mDragImageX(0),
+    mDragImageY(0)
+{
+  mEffectAllowed = aAction &
+                   (nsIDragService::DRAGDROP_ACTION_COPY |
+                    nsIDragService::DRAGDROP_ACTION_LINK |
+                    nsIDragService::DRAGDROP_ACTION_MOVE);
+
+  CacheExternalFormats();
+}
+
+nsDOMDataTransfer::nsDOMDataTransfer(PRUint32 aEventType,
+                                     const PRUint32 aEffectAllowed,
+                                     PRBool aIsExternal,
+                                     nsTArray<nsTArray<TransferItem> >& aItems,
+                                     nsIDOMElement* aDragImage,
+                                     PRUint32 aDragImageX,
+                                     PRUint32 aDragImageY)
+  : mEventType(aEventType),
+    mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
+    mEffectAllowed(aEffectAllowed),
+    mReadOnly(PR_TRUE),
+    mIsExternal(aIsExternal),
+    mItems(aItems),
+    mDragImage(aDragImage),
+    mDragImageX(aDragImageX),
+    mDragImageY(aDragImageY)
+{
+  // The items are copied from aItems into mItems. There is no need to copy
+  // the actual data in the items as the data transfer will be read only. The
+  // draggesture and dragstart events are the only times when items are
+  // modifiable, but those events should have been using the first constructor
+  // above.
+  NS_ASSERTION(aEventType != NS_DRAGDROP_GESTURE &&
+               aEventType != NS_DRAGDROP_START,
+               "invalid event type for nsDOMDataTransfer constructor");
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetDropEffect(nsAString& aDropEffect)
+{
+  aDropEffect.AssignASCII(sEffects[mDropEffect]);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetDropEffect(const nsAString& aDropEffect)
+{
+  // the drop effect can only be 'none', 'copy', 'move' or 'link'.
+  for (PRUint32 e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
+    if (aDropEffect.EqualsASCII(sEffects[e])) {
+      // don't allow copyMove
+      if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
+                nsIDragService::DRAGDROP_ACTION_MOVE))
+        mDropEffect = e;
+      break;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetEffectAllowed(nsAString& aEffectAllowed)
+{
+  if (mEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
+    aEffectAllowed.AssignLiteral("uninitialized");
+  else
+    aEffectAllowed.AssignASCII(sEffects[mEffectAllowed]);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed)
+{
+  if (aEffectAllowed.EqualsLiteral("uninitialized")) {
+    mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
+    return NS_OK;
+  }
+
+  PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_NONE == 0);
+  PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_COPY == 1);
+  PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_MOVE == 2);
+  PR_STATIC_ASSERT(nsIDragService::DRAGDROP_ACTION_LINK == 4);
+
+  for (PRUint32 e = 0; e < NS_ARRAY_LENGTH(sEffects); e++) {
+    if (aEffectAllowed.EqualsASCII(sEffects[e])) {
+      mEffectAllowed = e;
+      break;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetDropEffectInt(PRUint32* aDropEffect)
+{
+  *aDropEffect = mDropEffect;
+  return  NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetDropEffectInt(PRUint32 aDropEffect)
+{
+  mDropEffect = aDropEffect;
+  return  NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetEffectAllowedInt(PRUint32* aEffectAllowed)
+{
+  *aEffectAllowed = mEffectAllowed;
+  return  NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetEffectAllowedInt(PRUint32 aEffectAllowed)
+{
+  mEffectAllowed = aEffectAllowed;
+  return  NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetTypes(nsIDOMDOMStringList** aTypes)
+{
+  return MozTypesAt(0, aTypes);
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
+{
+  // return an empty string if data for the format was not found
+  aData.Truncate();
+
+  nsCOMPtr<nsIVariant> data;
+  nsresult rv = MozGetDataAt(aFormat, 0, getter_AddRefs(data));
+  if (rv == NS_ERROR_DOM_INDEX_SIZE_ERR)
+    return NS_OK;
+
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (data) {
+    nsAutoString stringdata;
+    data->GetAsAString(stringdata);
+
+    // for the URL type, parse out the first URI from the list. The URIs are
+    // separated by newlines
+    if (aFormat.EqualsLiteral("URL")) {
+      PRInt32 lastidx = 0, idx;
+      PRInt32 length = stringdata.Length();
+      while (lastidx < length) {
+        idx = stringdata.FindChar('\n', lastidx);
+        // lines beginning with # are comments
+        if (stringdata[lastidx] == '#') {
+          if (idx == -1)
+            break;
+        }
+        else {
+          if (idx == -1)
+            aData.Assign(Substring(stringdata, lastidx));
+          else
+            aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
+          aData = nsContentUtils::TrimWhitespace(aData, PR_TRUE);
+          return NS_OK;
+        }
+        lastidx = idx + 1;
+      }
+    }
+    else {
+      aData = stringdata;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetData(const nsAString& aFormat, const nsAString& aData)
+{
+  nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+  NS_ENSURE_TRUE(variant, NS_ERROR_OUT_OF_MEMORY);
+
+  variant->SetAsAString(aData);
+
+  return MozSetDataAt(aFormat, variant, 0);
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::ClearData(const nsAString& aFormat)
+{
+  nsresult rv = MozClearDataAt(aFormat, 0);
+  return (rv == NS_ERROR_DOM_INDEX_SIZE_ERR) ? NS_OK : rv;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::GetMozItemCount(PRUint32* aCount)
+{
+  *aCount = mItems.Length();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::MozTypesAt(PRUint32 aIndex, nsIDOMDOMStringList** aTypes)
+{
+  *aTypes = nsnull;
+
+  nsRefPtr<nsDOMStringList> types = new nsDOMStringList();
+  NS_ENSURE_TRUE(types, NS_ERROR_OUT_OF_MEMORY);
+
+  if (aIndex < mItems.Length()) {
+    // note that you can retrieve the types regardless of their principal
+    nsTArray<TransferItem>& item = mItems[aIndex];
+    for (PRUint32 i = 0; i < item.Length(); i++)
+      types->Add(item[i].mFormat);
+  }
+
+  *aTypes = types;
+  NS_ADDREF(*aTypes);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::MozGetDataAt(const nsAString& aFormat,
+                                PRUint32 aIndex,
+                                nsIVariant** aData)
+{
+  *aData = nsnull;
+
+  if (aFormat.IsEmpty())
+    return NS_OK;
+
+  if (aIndex >= mItems.Length())
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+  nsAutoString format;
+  GetRealFormat(aFormat, format);
+
+  nsTArray<TransferItem>& item = mItems[aIndex];
+
+  // allow access to any data in the drop and dragdrop events, or if the
+  // UniversalBrowserRead privilege is set, otherwise only allow access to
+  // data from the same principal.
+  nsIPrincipal* principal = nsnull;
+  if (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP &&
+      !nsContentUtils::IsCallerTrustedForCapability("UniversalBrowserRead"))
+    principal = GetCurrentPrincipal();
+
+  PRUint32 count = item.Length();
+  for (PRUint32 i = 0; i < count; i++) {
+    TransferItem& formatitem = item[i];
+    if (formatitem.mFormat.Equals(format)) {
+      PRBool subsumes;
+      if (formatitem.mPrincipal && principal &&
+          (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes))
+        return NS_ERROR_DOM_SECURITY_ERR;
+
+      if (!formatitem.mData)
+        FillInExternalDragData(formatitem, aIndex);
+      *aData = formatitem.mData;
+      NS_IF_ADDREF(*aData);
+      return NS_OK;
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::MozSetDataAt(const nsAString& aFormat,
+                                nsIVariant* aData,
+                                PRUint32 aIndex)
+{
+  NS_ENSURE_TRUE(aData, NS_ERROR_NULL_POINTER);
+
+  if (aFormat.IsEmpty())
+    return NS_OK;
+
+  if (mReadOnly)
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+  // Specifying an index less than the current length will replace an existing
+  // item. Specifying an index equal to the current length will add a new item.
+  if (aIndex > mItems.Length())
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+  // don't allow non-chrome to add file data
+  // XXX perhaps this should also limit any non-string type as well
+  if ((aFormat.EqualsLiteral("application/x-moz-file-promise") ||
+       aFormat.EqualsLiteral("application/x-moz-file")) &&
+       !nsContentUtils::IsCallerChrome()) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
+
+  return SetDataWithPrincipal(aFormat, aData, aIndex, GetCurrentPrincipal());
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::MozClearDataAt(const nsAString& aFormat, PRUint32 aIndex)
+{
+  if (mReadOnly)
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+  if (aIndex >= mItems.Length())
+    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+
+  nsAutoString format;
+  GetRealFormat(aFormat, format);
+
+  nsIPrincipal* principal = GetCurrentPrincipal();
+
+  // if the format is empty, clear all formats
+  PRBool clearall = format.IsEmpty();
+
+  nsTArray<TransferItem>& item = mItems[aIndex];
+  // count backwards so that the count and index don't have to be adjusted
+  // after removing an element
+  for (PRInt32 i = item.Length() - 1; i >= 0; i--) {
+    TransferItem& formatitem = item[i];
+    if (clearall || formatitem.mFormat.Equals(format)) {
+      // don't allow removing data that has a stronger principal
+      PRBool subsumes;
+      if (formatitem.mPrincipal && principal &&
+          (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes))
+        return NS_ERROR_DOM_SECURITY_ERR;
+
+      item.RemoveElementAt(i);
+
+      // if a format was specified, break out. Otherwise, loop around until
+      // all formats have been removed
+      if (!clearall)
+        break;
+    }
+  }
+
+  // if the last format for an item is removed, remove the entire item
+  if (!item.Length())
+     mItems.RemoveElementAt(aIndex);
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::SetDragImage(nsIDOMElement* aImage, PRInt32 aX, PRInt32 aY)
+{
+  if (mReadOnly)
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+  mDragImage = aImage;
+  mDragImageX = aX;
+  mDragImageY = aY;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDataTransfer::AddElement(nsIDOMElement* aElement)
+{
+  NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
+
+  if (mReadOnly)
+    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+
+  mDragTarget = do_QueryInterface(aElement);
+
+  return NS_OK;
+}
+
+nsresult
+nsDOMDataTransfer::Clone(PRUint32 aEventType,
+                         nsIDOMDataTransfer** aNewDataTransfer)
+{
+  nsDOMDataTransfer* newDataTransfer =
+    new nsDOMDataTransfer(aEventType, mEffectAllowed, mIsExternal,
+                          mItems, mDragImage, mDragImageX, mDragImageY);
+  NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_OUT_OF_MEMORY);
+
+  *aNewDataTransfer = newDataTransfer;
+  NS_ADDREF(*aNewDataTransfer);
+  return NS_OK;
+}
+
+void
+nsDOMDataTransfer::GetTransferables(nsISupportsArray** aArray)
+{
+  *aArray = nsnull;
+
+  nsCOMPtr<nsISupportsArray> transArray =
+    do_CreateInstance("@mozilla.org/supports-array;1");
+  if (!transArray)
+    return;
+
+  PRBool added = PR_FALSE;
+  PRUint32 count = mItems.Length();
+  for (PRUint32 i = 0; i < count; i++) {
+
+    nsTArray<TransferItem>& item = mItems[i];
+    PRUint32 count = item.Length();
+    if (!count)
+      continue;
+
+    nsCOMPtr<nsITransferable> transferable =
+      do_CreateInstance("@mozilla.org/widget/transferable;1");
+    if (!transferable)
+      return;
+
+    for (PRUint32 f = 0; f < count; f++) {
+      TransferItem& formatitem = item[f];
+      if (!formatitem.mData) // skip empty items
+        continue;
+
+      PRUint32 length;
+      nsCOMPtr<nsISupports> convertedData;
+      if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData), &length))
+        continue;
+
+      // the underlying drag code uses text/unicode, so use that instead of text/plain
+      const char* format;
+      NS_ConvertUTF16toUTF8 utf8format(formatitem.mFormat);
+      if (utf8format.EqualsLiteral("text/plain"))
+        format = kUnicodeMime;
+      else
+        format = utf8format.get();
+
+      // if a converter is set for a format, set the converter for the
+      // transferable and don't add the item
+      nsCOMPtr<nsIFormatConverter> converter = do_QueryInterface(convertedData);
+      if (converter) {
+        transferable->AddDataFlavor(format);
+        transferable->SetConverter(converter);
+        continue;
+      }
+
+      nsresult rv = transferable->SetTransferData(format, convertedData, length);
+      if (NS_FAILED(rv))
+        return;
+
+      added = PR_TRUE;
+    }
+
+    // only append the transferable if data was successfully added to it
+    if (added)
+      transArray->AppendElement(transferable);
+  }
+
+  NS_ADDREF(*aArray = transArray);
+}
+
+PRBool
+nsDOMDataTransfer::ConvertFromVariant(nsIVariant* aVariant,
+                                      nsISupports** aSupports,
+                                      PRUint32* aLength)
+{
+  *aSupports = nsnull;
+  *aLength = 0;
+
+  PRUint16 type;
+  aVariant->GetDataType(&type);
+  if (type == nsIDataType::VTYPE_INTERFACE ||
+      type == nsIDataType::VTYPE_INTERFACE_IS) {
+    if (NS_FAILED(aVariant->GetAsISupports(aSupports)))
+      return PR_FALSE;
+
+    // for flavour data providers, use kFlavorHasDataProvider (which has the
+    // value 0) as the length.
+    nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(*aSupports);
+    *aLength = fdp ? sizeof(nsISupports) : nsITransferable::kFlavorHasDataProvider;
+  }
+
+  PRUnichar* chrs;
+  nsresult rv = aVariant->GetAsWString(&chrs);
+  if (NS_FAILED(rv))
+    return PR_FALSE;
+
+  nsCOMPtr<nsISupportsString>
+    strSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
+  if (!strSupports)
+    return PR_FALSE;
+
+  nsAutoString str(chrs);
+  strSupports->SetData(str);
+
+  *aSupports = strSupports;
+  NS_ADDREF(*aSupports);
+
+  // each character is two bytes
+  *aLength = str.Length() << 1;
+
+  return PR_TRUE;
+}
+
+void
+nsDOMDataTransfer::ClearAll()
+{
+  mItems.Clear();
+}
+
+nsresult
+nsDOMDataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
+                                        nsIVariant* aData,
+                                        PRUint32 aIndex,
+                                        nsIPrincipal* aPrincipal)
+{
+  nsAutoString format;
+  GetRealFormat(aFormat, format);
+
+  // check if the item for the format already exists. In that case,
+  // just replace it.
+  TransferItem* formatitem;
+  if (aIndex < mItems.Length()) {
+    nsTArray<TransferItem>& item = mItems[aIndex];
+    PRUint32 count = item.Length();
+    for (PRUint32 i = 0; i < count; i++) {
+      TransferItem& itemformat = item[i];
+      if (itemformat.mFormat.Equals(format)) {
+        // don't allow replacing data that has a stronger principal
+        PRBool subsumes;
+        if (itemformat.mPrincipal && aPrincipal &&
+            (NS_FAILED(aPrincipal->Subsumes(itemformat.mPrincipal, &subsumes)) || !subsumes))
+          return NS_ERROR_DOM_SECURITY_ERR;
+
+        itemformat.mPrincipal = aPrincipal;
+        itemformat.mData = aData;
+        return NS_OK;
+      }
+    }
+
+    // add a new format
+    formatitem = item.AppendElement();
+  }
+  else {
+    NS_ASSERTION(aIndex == mItems.Length(), "Index out of range");
+
+    // add a new index
+    nsTArray<TransferItem>* item = mItems.AppendElement();
+    NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
+
+    formatitem = item->AppendElement();
+  }
+
+  NS_ENSURE_TRUE(formatitem, NS_ERROR_OUT_OF_MEMORY);
+
+  formatitem->mFormat = format;
+  formatitem->mPrincipal = aPrincipal;
+  formatitem->mData = aData;
+
+  return NS_OK;
+}
+
+nsIPrincipal*
+nsDOMDataTransfer::GetCurrentPrincipal()
+{
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+
+  nsCOMPtr<nsIPrincipal> currentPrincipal;
+  ssm->GetSubjectPrincipal(getter_AddRefs(currentPrincipal));
+  if (!currentPrincipal)
+    ssm->GetSystemPrincipal(getter_AddRefs(currentPrincipal));
+
+  return currentPrincipal.get();
+}
+
+void
+nsDOMDataTransfer::GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat)
+{
+  // treat text/unicode as equivalent to text/plain
+  if (aInFormat.EqualsLiteral("Text") || aInFormat.EqualsLiteral("text/unicode"))
+    aOutFormat.AssignLiteral("text/plain");
+  else if (aInFormat.EqualsLiteral("URL"))
+    aOutFormat.AssignLiteral("text/uri-list");
+  else
+    aOutFormat.Assign(aInFormat);
+}
+
+void
+nsDOMDataTransfer::CacheExternalFormats()
+{
+  // Called during the constructor to cache the formats available from an
+  // external drag. The data associated with each format will be set to null.
+  // This data will instead only be retrieved in FillInExternalDragData when
+  // asked for, as it may be time consuming for the source application to
+  // generate it.
+
+  nsCOMPtr<nsIDragService> dragService =
+    do_GetService("@mozilla.org/widget/dragservice;1");
+  if (!dragService)
+    return;
+
+  nsCOMPtr<nsIDragSession> dragSession;
+  dragService->GetCurrentSession(getter_AddRefs(dragSession));
+  if (!dragSession)
+    return;
+
+  // make sure that the system principal is used for external drags
+  nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
+  nsCOMPtr<nsIPrincipal> sysPrincipal;
+  ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
+
+  // there isn't a way to get a list of the formats that might be available on
+  // all platforms, so just check for the types that can actually be imported
+  // XXXndeakin there are some other formats but those are platform specific.
+  const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime, kUnicodeMime };
+
+  PRUint32 count;
+  dragSession->GetNumDropItems(&count);
+  for (PRUint32 c = 0; c < count; c++) {
+    for (PRUint32 f = 0; f < NS_ARRAY_LENGTH(formats); f++) {
+      // IsDataFlavorSupported doesn't take an index as an argument and just
+      // checks if any of the items support a particular flavor, even though
+      // the GetData method does take an index. Here, we just assume that
+      // every item being dragged has the same set of flavors.
+      PRBool supported;
+      dragSession->IsDataFlavorSupported(formats[f], &supported);
+      // if the format is supported, add an item to the array with null as
+      // the data. When retrieved, GetRealData will read the data.
+      if (supported) {
+        if (formats[f] == kUnicodeMime) {
+          SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nsnull, c, sysPrincipal);
+        }
+        else {
+          if (formats[f] == kURLDataMime)
+            SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nsnull, c, sysPrincipal);
+          SetDataWithPrincipal(NS_ConvertUTF8toUTF16(formats[f]), nsnull, c, sysPrincipal);
+        }
+      }
+    }
+  }
+}
+
+void
+nsDOMDataTransfer::FillInExternalDragData(TransferItem& aItem, PRUint32 aIndex)
+{
+  NS_PRECONDITION(mIsExternal, "Not an external drag");
+
+  if (!aItem.mData) {
+    nsCOMPtr<nsITransferable> trans =
+      do_CreateInstance("@mozilla.org/widget/transferable;1");
+    if (!trans)
+      return;
+
+    NS_ConvertUTF16toUTF8 utf8format(aItem.mFormat);
+    const char* format = utf8format.get();
+    if (strcmp(format, "text/plain") == 0)
+      format = kUnicodeMime;
+    else if (strcmp(format, "text/uri-list") == 0)
+      format = kURLDataMime;
+
+    nsCOMPtr<nsIDragService> dragService =
+      do_GetService("@mozilla.org/widget/dragservice;1");
+    if (!dragService)
+      return;
+
+    nsCOMPtr<nsIDragSession> dragSession;
+    dragService->GetCurrentSession(getter_AddRefs(dragSession));
+    if (!dragSession)
+      return;
+
+    trans->AddDataFlavor(format);
+    dragSession->GetData(trans, aIndex);
+
+    PRUint32 length = 0;
+    nsCOMPtr<nsISupports> data;
+    trans->GetTransferData(format, getter_AddRefs(data), &length);
+    if (!data)
+      return;
+
+    nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+    if (!variant)
+      return;
+
+    nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
+    if (supportsstr) {
+      nsAutoString str;
+      supportsstr->GetData(str);
+      variant->SetAsAString(str);
+    }
+    else {
+      variant->SetAsISupports(data);
+    }
+    aItem.mData = variant;
+  }
+}
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDataTransfer.h
@@ -0,0 +1,191 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsDOMDataTransfer_h__
+#define nsDOMDataTransfer_h__
+
+#include "nsString.h"
+#include "nsTArray.h"
+#include "nsIVariant.h"
+#include "nsIPrincipal.h"
+#include "nsIDOMDataTransfer.h"
+#include "nsIDragService.h"
+#include "nsIDOMElement.h"
+#include "nsCycleCollectionParticipant.h"
+
+class nsITransferable;
+
+/**
+ * TransferItem is used to hold data for a particular format. Each piece of
+ * data has a principal set from the caller which added it. This allows a
+ * caller that wishes to retrieve the data to only be able to access the data
+ * it is allowed to, yet still allow a chrome caller to retrieve any of the
+ * data.
+ */
+struct TransferItem {
+  nsString mFormat;
+  nsCOMPtr<nsIPrincipal> mPrincipal;
+  nsCOMPtr<nsIVariant> mData;
+};
+
+class nsDOMDataTransfer : public nsIDOMDataTransfer,
+                          public nsIDOMNSDataTransfer
+{
+public:
+  NS_DECL_CYCLE_COLLECTING_ISUPPORTS
+  NS_DECL_NSIDOMDATATRANSFER
+  NS_DECL_NSIDOMNSDATATRANSFER
+
+  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(nsDOMDataTransfer, nsIDOMDataTransfer)
+
+  friend class nsDOMDragEvent;
+  friend class nsEventStateManager;
+
+protected:
+
+  // the constructors are protected so only our friends can call them
+
+  // default constructor used for the dragstart/draggesture event and
+  // synthetic events
+  nsDOMDataTransfer();
+
+  // this constructor must only be used to create a dataTransfer for a drag
+  // that was started without using a data transfer, either an external drag,
+  // that is, a drag where the source is another application, or a drag
+  // started by calling the drag service directly.
+  nsDOMDataTransfer(PRUint32 aEventType, PRUint32 aAction);
+
+  // this constructor is used only by the Clone method to copy the fields as
+  // needed to a new data transfer.
+  nsDOMDataTransfer(PRUint32 aEventType,
+                    const PRUint32 aEffectAllowed,
+                    PRBool aIsExternal,
+                    nsTArray<nsTArray<TransferItem> >& aItems,
+                    nsIDOMElement* aDragImage,
+                    PRUint32 aDragImageX,
+                    PRUint32 aDragImageY);
+
+  static const char sEffects[8][9];
+
+public:
+
+  void GetDragTarget(nsIDOMElement** aDragTarget)
+  {
+    *aDragTarget = mDragTarget;
+    NS_IF_ADDREF(*aDragTarget);
+  }
+
+  // a readonly dataTransfer cannot have new data added or existing data removed.
+  // Only the dropEffect and effectAllowed may be modified.
+  void SetReadOnly() { mReadOnly = PR_TRUE; }
+
+  // converts the data into an array of nsITransferable objects to be used for
+  // drag and drop or clipboard operations.
+  void GetTransferables(nsISupportsArray** transferables);
+
+  // converts the data in the variant to an nsISupportString if possible or
+  // an nsISupports or null otherwise.
+  PRBool ConvertFromVariant(nsIVariant* aVariant,
+                            nsISupports** aSupports,
+                            PRUint32* aLength);
+
+  // clears all of the data
+  void ClearAll();
+
+  // Similar to SetData except also specifies the principal to store.
+  // aData may be null when called from CacheExternalFormats.
+  nsresult SetDataWithPrincipal(const nsAString& aFormat,
+                                nsIVariant* aData,
+                                PRUint32 aIndex,
+                                nsIPrincipal* aPrincipal);
+
+protected:
+
+  // returns a weak reference to the drag image
+  nsIDOMElement* GetDragImage(PRInt32* aX, PRInt32* aY)
+  {
+    *aX = mDragImageX;
+    *aY = mDragImageY;
+    return mDragImage;
+  }
+
+  // returns a weak reference to the current principal
+  nsIPrincipal* GetCurrentPrincipal();
+
+  // converts some formats used for compatibility in aInFormat into aOutFormat.
+  // Text and text/unicode become text/plain, and URL becomes text/uri-list
+  void GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat);
+
+  // caches the formats that exist in the drag service that were added by an
+  // external drag
+  void CacheExternalFormats();
+
+  // fills in the data field of aItem with the data from the drag service for
+  // a given index.
+  void FillInExternalDragData(TransferItem& aItem, PRUint32 aIndex);
+
+  // the event type this data transfer is for. This will correspond to an
+  // event->message value.
+  PRUint32 mEventType;
+
+  // the drop effect and effect allowed
+  PRUint32 mDropEffect;
+  PRUint32 mEffectAllowed;
+
+  // readonly data transfers may not be modified except the drop effect and
+  // effect allowed.
+  PRPackedBool mReadOnly;
+
+  // true for drags started without a data transfer, for example, those from
+  // another application.
+  PRPackedBool mIsExternal;
+
+  // array of items, each containing an array of format->data pairs
+  nsTArray<nsTArray<TransferItem> > mItems;
+
+  // the target of the drag. The drag and dragend events will fire at this.
+  nsCOMPtr<nsIDOMElement> mDragTarget;
+
+  // the custom drag image and coordinates within the image. If mDragImage is
+  // null, the default image is created from the drag target.
+  nsCOMPtr<nsIDOMElement> mDragImage;
+  PRUint32 mDragImageX;
+  PRUint32 mDragImageY;
+};
+
+#endif // nsDOMDataTransfer_h__
+
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDragEvent.cpp
@@ -0,0 +1,246 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "nsDOMDragEvent.h"
+#include "nsIServiceManager.h"
+#include "nsGUIEvent.h"
+#include "nsContentUtils.h"
+#include "nsIEventStateManager.h"
+#include "nsDOMDataTransfer.h"
+#include "nsIDragService.h"
+
+nsDOMDragEvent::nsDOMDragEvent(nsPresContext* aPresContext,
+                               nsInputEvent* aEvent)
+  : nsDOMMouseEvent(aPresContext, aEvent ? aEvent :
+                    new nsDragEvent(PR_FALSE, 0, nsnull))
+{
+  if (aEvent) {
+    mEventIsInternal = PR_FALSE;
+  }
+  else {
+    mEventIsInternal = PR_TRUE;
+    mEvent->time = PR_Now();
+    mEvent->refPoint.x = mEvent->refPoint.y = 0;
+  }
+}
+
+nsDOMDragEvent::~nsDOMDragEvent()
+{
+  if (mEventIsInternal) {
+    if (mEvent->eventStructType == NS_DRAG_EVENT)
+      delete static_cast<nsDragEvent*>(mEvent);
+    mEvent = nsnull;
+  }
+}
+
+NS_IMPL_ADDREF_INHERITED(nsDOMDragEvent, nsDOMMouseEvent)
+NS_IMPL_RELEASE_INHERITED(nsDOMDragEvent, nsDOMMouseEvent)
+
+NS_INTERFACE_MAP_BEGIN(nsDOMDragEvent)
+  NS_INTERFACE_MAP_ENTRY(nsIDOMDragEvent)
+  NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(DragEvent)
+NS_INTERFACE_MAP_END_INHERITING(nsDOMMouseEvent)
+
+NS_IMETHODIMP
+nsDOMDragEvent::InitDragEvent(const nsAString & aType,
+                              PRBool aCanBubble,
+                              PRBool aCancelable,
+                              nsIDOMAbstractView* aView,
+                              PRInt32 aDetail,
+                              nsIDOMDataTransfer* aDataTransfer)
+{
+  nsresult rv = nsDOMUIEvent::InitUIEvent(aType, aCanBubble, aCancelable, aView, aDetail);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  if (mEventIsInternal && mEvent) {
+    nsDragEvent* dragEvent = static_cast<nsDragEvent*>(mEvent);
+    dragEvent->dataTransfer = aDataTransfer;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsDOMDragEvent::InitDragEventNS(const nsAString & aNamespaceURIArg,
+                                const nsAString & aType,
+                                PRBool aCanBubble,
+                                PRBool aCancelable,
+                                nsIDOMAbstractView* aView,
+                                PRInt32 aDetail,
+                                nsIDOMDataTransfer* aDataTransfer)
+{
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+nsDOMDragEvent::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
+{
+  *aDataTransfer = nsnull;
+
+  if (!mEvent || mEvent->eventStructType != NS_DRAG_EVENT) {
+    NS_WARNING("Tried to get dataTransfer from non-drag event!");
+    return NS_OK;
+  }
+
+  // the dataTransfer field of the event caches the DataTransfer associated
+  // with the drag. It is initialized when an attempt is made to retrieve it
+  // rather that when the event is created to avoid duplicating the data when
+  // no listener ever uses it.
+  nsDragEvent* dragEvent = static_cast<nsDragEvent*>(mEvent);
+  if (dragEvent->dataTransfer) {
+    CallQueryInterface(dragEvent->dataTransfer, aDataTransfer);
+    return NS_OK;
+  }
+
+  // for synthetic events, just use the supplied data transfer object
+  if (mEventIsInternal) {
+    NS_IF_ADDREF(*aDataTransfer = dragEvent->dataTransfer);
+    return NS_OK;
+  }
+
+  // For draggesture and dragstart events, the data transfer object is
+  // created before the event fires, so it should already be set. For other
+  // drag events, get the object from the drag session.
+  NS_ASSERTION(mEvent->message != NS_DRAGDROP_GESTURE &&
+               mEvent->message != NS_DRAGDROP_START,
+               "draggesture event created without a dataTransfer");
+
+  nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+  NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
+
+  nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
+  dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
+  if (!initialDataTransfer) {
+    // A dataTransfer won't exist when a drag was started by some other
+    // means, for instance calling the drag service directly, or a drag
+    // from another application. In either case, a new dataTransfer should
+    // be created that reflects the data. Pass true to the constructor for
+    // the aIsExternal argument, so that only system access is allowed.
+    PRUint32 action = 0;
+    dragSession->GetDragAction(&action);
+    initialDataTransfer =
+      new nsDOMDataTransfer(mEvent->message, action);
+    NS_ENSURE_TRUE(initialDataTransfer, NS_ERROR_OUT_OF_MEMORY);
+
+    // now set it in the drag session so we don't need to create it again
+    dragSession->SetDataTransfer(initialDataTransfer);
+  }
+
+  // each event should use a clone of the original dataTransfer.
+  nsCOMPtr<nsIDOMNSDataTransfer> initialDataTransferNS =
+    do_QueryInterface(initialDataTransfer);
+  NS_ENSURE_TRUE(initialDataTransferNS, NS_ERROR_FAILURE);
+  initialDataTransferNS->Clone(mEvent->message,
+                               getter_AddRefs(dragEvent->dataTransfer));
+  NS_ENSURE_TRUE(dragEvent->dataTransfer, NS_ERROR_OUT_OF_MEMORY);
+
+  // for the dragenter and dragover events, initialize the drop effect
+  // from the drop action, which platform specific widget code sets before
+  // the event is fired based on the keyboard state.
+  if (mEvent->message == NS_DRAGDROP_ENTER ||
+      mEvent->message == NS_DRAGDROP_OVER) {
+    nsCOMPtr<nsIDOMNSDataTransfer> newDataTransfer =
+      do_QueryInterface(dragEvent->dataTransfer);
+    NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE);
+
+    PRUint32 action, effectAllowed;
+    dragSession->GetDragAction(&action);
+    newDataTransfer->GetEffectAllowedInt(&effectAllowed);
+    newDataTransfer->SetDropEffectInt(FilterDropEffect(action, effectAllowed));
+  }
+  else if (mEvent->message == NS_DRAGDROP_DROP ||
+           mEvent->message == NS_DRAGDROP_DRAGDROP ||
+           mEvent->message == NS_DRAGDROP_END) {
+    // For the drop and dragend events, set the drop effect based on the
+    // last value that the dropEffect had. This will have been set in
+    // nsEventStateManager::PostHandleEvent for the last dragenter or
+    // dragover event.
+    nsCOMPtr<nsIDOMNSDataTransfer> newDataTransfer =
+      do_QueryInterface(dragEvent->dataTransfer);
+    NS_ENSURE_TRUE(newDataTransfer, NS_ERROR_FAILURE);
+
+    PRUint32 dropEffect;
+    initialDataTransferNS->GetDropEffectInt(&dropEffect);
+    newDataTransfer->SetDropEffectInt(dropEffect);
+  }
+
+  NS_IF_ADDREF(*aDataTransfer = dragEvent->dataTransfer);
+  return NS_OK;
+}
+
+// static
+PRUint32
+nsDOMDragEvent::FilterDropEffect(PRUint32 aAction, PRUint32 aEffectAllowed)
+{
+  // It is possible for the drag action to include more than one action, but
+  // the widget code which sets the action from the keyboard state should only
+  // be including one. If multiple actions were set, we just consider them in
+  //  the following order:
+  //   copy, link, move
+  if (aAction & nsIDragService::DRAGDROP_ACTION_COPY)
+    aAction = nsIDragService::DRAGDROP_ACTION_COPY;
+  else if (aAction & nsIDragService::DRAGDROP_ACTION_LINK)
+    aAction = nsIDragService::DRAGDROP_ACTION_LINK;
+  else if (aAction & nsIDragService::DRAGDROP_ACTION_MOVE)
+    aAction = nsIDragService::DRAGDROP_ACTION_MOVE;
+
+  // Filter the action based on the effectAllowed. If the effectAllowed
+  // doesn't include the action, then that action cannot be done, so adjust
+  // the action to something that is allowed. For a copy, adjust to move or
+  // link. For a move, adjust to copy or link. For a link, adjust to move or
+  // link. Otherwise, use none.
+  if (aAction & aEffectAllowed ||
+      aEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
+    return aAction;
+  if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_MOVE)
+    return nsIDragService::DRAGDROP_ACTION_MOVE;
+  if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_COPY)
+    return nsIDragService::DRAGDROP_ACTION_COPY;
+  if (aEffectAllowed & nsIDragService::DRAGDROP_ACTION_LINK)
+    return nsIDragService::DRAGDROP_ACTION_LINK;
+  return nsIDragService::DRAGDROP_ACTION_NONE;
+}
+
+nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult,
+                            nsPresContext* aPresContext,
+                            nsDragEvent *aEvent) 
+{
+  nsDOMDragEvent* event = new nsDOMDragEvent(aPresContext, aEvent);
+  NS_ENSURE_TRUE(event, NS_ERROR_OUT_OF_MEMORY);
+
+  return CallQueryInterface(event, aInstancePtrResult);
+}
new file mode 100644
--- /dev/null
+++ b/content/events/src/nsDOMDragEvent.h
@@ -0,0 +1,69 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#ifndef nsDOMDragEvent_h__
+#define nsDOMDragEvent_h__
+
+#include "nsIDOMDragEvent.h"
+#include "nsDOMMouseEvent.h"
+#include "nsIDOMDataTransfer.h"
+
+class nsIContent;
+class nsEvent;
+
+class nsDOMDragEvent : public nsIDOMDragEvent,
+                       public nsDOMMouseEvent
+{
+public:
+  nsDOMDragEvent(nsPresContext* aPresContext, nsInputEvent* aEvent);
+  virtual ~nsDOMDragEvent();
+
+  NS_DECL_ISUPPORTS_INHERITED
+
+  NS_DECL_NSIDOMDRAGEVENT
+  
+  NS_FORWARD_TO_NSDOMMOUSEEVENT
+
+  // filters the action to fit within the effects allowed and returns it.
+  static PRUint32 FilterDropEffect(PRUint32 aAction, PRUint32 aEffectAllowed);
+};
+
+nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult,
+                            nsPresContext* aPresContext,
+                            nsDragEvent* aEvent);
+
+#endif // nsDOMDragEvent_h__
--- a/content/events/src/nsDOMEvent.cpp
+++ b/content/events/src/nsDOMEvent.cpp
@@ -59,17 +59,17 @@
 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",
-  "drag", "dragend", "resize",
+  "drag", "dragend", "dragstart", "dragleave", "drop", "resize",
   "scroll", "overflow", "underflow", "overflowchanged",
   "DOMSubtreeModified", "DOMNodeInserted", "DOMNodeRemoved", 
   "DOMNodeRemovedFromDocument", "DOMNodeInsertedIntoDocument",
   "DOMAttrModified", "DOMCharacterDataModified",
   "DOMActivate", "DOMFocusIn", "DOMFocusOut",
   "pageshow", "pagehide", "DOMMouseScroll", "offline", "online",
   "copy", "cut", "paste"
 #ifdef MOZ_SVG
@@ -167,16 +167,19 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(ns
     tmp->mEvent->target = nsnull;
     tmp->mEvent->currentTarget = nsnull;
     tmp->mEvent->originalTarget = nsnull;
     switch (tmp->mEvent->eventStructType) {
       case NS_MOUSE_EVENT:
       case NS_MOUSE_SCROLL_EVENT:
         static_cast<nsMouseEvent_base*>(tmp->mEvent)->relatedTarget = nsnull;
         break;
+      case NS_DRAG_EVENT:
+        static_cast<nsDragEvent*>(tmp->mEvent)->dataTransfer = nsnull;
+        break;
       case NS_XUL_COMMAND_EVENT:
         static_cast<nsXULCommandEvent*>(tmp->mEvent)->sourceEvent = nsnull;
         break;
       case NS_MUTATION_EVENT:
         static_cast<nsMutationEvent*>(tmp->mEvent)->mRelatedNode = nsnull;
         break;
       default:
         break;
@@ -193,16 +196,20 @@ NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
     cb.NoteXPCOMChild(tmp->mEvent->currentTarget);
     cb.NoteXPCOMChild(tmp->mEvent->originalTarget);
     switch (tmp->mEvent->eventStructType) {
       case NS_MOUSE_EVENT:
       case NS_MOUSE_SCROLL_EVENT:
         cb.NoteXPCOMChild(
           static_cast<nsMouseEvent_base*>(tmp->mEvent)->relatedTarget);
         break;
+      case NS_DRAG_EVENT:
+        cb.NoteXPCOMChild(
+          static_cast<nsDragEvent*>(tmp->mEvent)->dataTransfer);
+        break;
       case NS_XUL_COMMAND_EVENT:
         cb.NoteXPCOMChild(
           static_cast<nsXULCommandEvent*>(tmp->mEvent)->sourceEvent);
         break;
       case NS_MUTATION_EVENT:
         cb.NoteXPCOMChild(
           static_cast<nsMutationEvent*>(tmp->mEvent)->mRelatedNode);
         break;
@@ -463,16 +470,37 @@ nsDOMEvent::SetEventType(const nsAString
       mEvent->message = NS_MOUSE_EXIT_SYNTH;
     else if (atom == nsGkAtoms::onmousemove)
       mEvent->message = NS_MOUSE_MOVE;
     else if (atom == nsGkAtoms::oncontextmenu)
       mEvent->message = NS_CONTEXTMENU;
   } else if (mEvent->eventStructType == NS_MOUSE_SCROLL_EVENT) {
     if (atom == nsGkAtoms::onDOMMouseScroll)
       mEvent->message = NS_MOUSE_SCROLL;
+  } else if (mEvent->eventStructType == NS_DRAG_EVENT) {
+    if (atom == nsGkAtoms::ondragstart)
+      mEvent->message = NS_DRAGDROP_START;
+    else if (atom == nsGkAtoms::ondraggesture)
+      mEvent->message = NS_DRAGDROP_GESTURE;
+    else if (atom == nsGkAtoms::ondragenter)
+      mEvent->message = NS_DRAGDROP_ENTER;
+    else if (atom == nsGkAtoms::ondragover)
+      mEvent->message = NS_DRAGDROP_OVER_SYNTH;
+    else if (atom == nsGkAtoms::ondragleave)
+      mEvent->message = NS_DRAGDROP_LEAVE_SYNTH;
+    else if (atom == nsGkAtoms::ondragexit)
+      mEvent->message = NS_DRAGDROP_EXIT;
+    else if (atom == nsGkAtoms::ondrag)
+      mEvent->message = NS_DRAGDROP_DRAG;
+    else if (atom == nsGkAtoms::ondrop)
+      mEvent->message = NS_DRAGDROP_DROP;
+    else if (atom == nsGkAtoms::ondragdrop)
+      mEvent->message = NS_DRAGDROP_DRAGDROP;
+    else if (atom == nsGkAtoms::ondragend)
+      mEvent->message = NS_DRAGDROP_END;
   } else if (mEvent->eventStructType == NS_KEY_EVENT) {
     if (atom == nsGkAtoms::onkeydown)
       mEvent->message = NS_KEY_DOWN;
     else if (atom == nsGkAtoms::onkeyup)
       mEvent->message = NS_KEY_UP;
     else if (atom == nsGkAtoms::onkeypress)
       mEvent->message = NS_KEY_PRESS;
   } else if (mEvent->eventStructType == NS_COMPOSITION_EVENT) {
@@ -755,16 +783,31 @@ NS_METHOD nsDOMEvent::DuplicatePrivateDa
       mouseEvent->clickCount = oldMouseEvent->clickCount;
       mouseEvent->acceptActivation = oldMouseEvent->acceptActivation;
       mouseEvent->context = oldMouseEvent->context;
       mouseEvent->relatedTarget = oldMouseEvent->relatedTarget;
       mouseEvent->button = oldMouseEvent->button;
       newEvent = mouseEvent;
       break;
     }
+    case NS_DRAG_EVENT:
+    {
+      nsDragEvent* oldDragEvent = static_cast<nsDragEvent*>(mEvent);
+      nsDragEvent* dragEvent =
+        new nsDragEvent(PR_FALSE, msg, nsnull);
+      NS_ENSURE_TRUE(dragEvent, NS_ERROR_OUT_OF_MEMORY);
+      isInputEvent = PR_TRUE;
+      dragEvent->dataTransfer = oldDragEvent->dataTransfer;
+      dragEvent->clickCount = oldDragEvent->clickCount;
+      dragEvent->acceptActivation = oldDragEvent->acceptActivation;
+      dragEvent->relatedTarget = oldDragEvent->relatedTarget;
+      dragEvent->button = oldDragEvent->button;
+      newEvent = dragEvent;
+      break;
+    }
     case NS_MENU_EVENT:
     {
       newEvent = new nsMenuEvent(PR_FALSE, msg, nsnull);
       NS_ENSURE_TRUE(newEvent, NS_ERROR_OUT_OF_MEMORY);
       static_cast<nsMenuEvent*>(newEvent)->mCommand =
         static_cast<nsMenuEvent*>(mEvent)->mCommand;
       break;
     }
@@ -1293,24 +1336,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
@@ -98,16 +98,19 @@ public:
     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/nsDOMMouseEvent.h
+++ b/content/events/src/nsDOMMouseEvent.h
@@ -60,13 +60,13 @@ public:
   
   // Forward to base class
   NS_FORWARD_TO_NSDOMUIEVENT
 
   // Specific implementation for a mouse event.
   NS_IMETHOD GetWhich(PRUint32 *aWhich);
 };
 
-#define NS_FORWARD_TO_NSDOMMOUSEEVENT \
+#define NS_FORWARD_TO_NSDOMMOUSEEVENT         \
   NS_FORWARD_NSIDOMMOUSEEVENT(nsDOMMouseEvent::) \
   NS_FORWARD_TO_NSDOMUIEVENT
 
 #endif // nsDOMMouseEvent_h__
--- a/content/events/src/nsDOMUIEvent.cpp
+++ b/content/events/src/nsDOMUIEvent.cpp
@@ -117,21 +117,21 @@ NS_IMPL_RELEASE_INHERITED(nsDOMUIEvent, 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION_INHERITED(nsDOMUIEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMUIEvent)
   NS_INTERFACE_MAP_ENTRY(nsIDOMNSUIEvent)
   NS_INTERFACE_MAP_ENTRY(nsIPrivateCompositionEvent)
   NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(UIEvent)
 NS_INTERFACE_MAP_END_INHERITING(nsDOMEvent)
 
 nsPoint nsDOMUIEvent::GetScreenPoint() {
-  if (!mEvent ||
-      (mEvent->eventStructType != NS_MOUSE_EVENT &&
-       mEvent->eventStructType != NS_POPUP_EVENT &&
-       mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
-       !NS_IS_DRAG_EVENT(mEvent))) {
+  if (!mEvent || 
+       (mEvent->eventStructType != NS_MOUSE_EVENT &&
+        mEvent->eventStructType != NS_POPUP_EVENT &&
+        mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
+        mEvent->eventStructType != NS_DRAG_EVENT)) {
     return nsPoint(0, 0);
   }
 
   if (!((nsGUIEvent*)mEvent)->widget ) {
     return mEvent->refPoint;
   }
 
   nsRect bounds(mEvent->refPoint, nsSize(1, 1));
@@ -142,17 +142,17 @@ nsPoint nsDOMUIEvent::GetScreenPoint() {
                  nsPresContext::AppUnitsToIntCSSPixels(offset.y * factor));
 }
 
 nsPoint nsDOMUIEvent::GetClientPoint() {
   if (!mEvent ||
       (mEvent->eventStructType != NS_MOUSE_EVENT &&
        mEvent->eventStructType != NS_POPUP_EVENT &&
        mEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
-       !NS_IS_DRAG_EVENT(mEvent)) ||
+       mEvent->eventStructType != NS_DRAG_EVENT) ||
       !mPresContext ||
       !((nsGUIEvent*)mEvent)->widget) {
     return mClientPoint;
   }
 
   nsPoint pt(0, 0);
   nsIPresShell* shell = mPresContext->GetPresShell();
   if (!shell) {
--- a/content/events/src/nsEventDispatcher.cpp
+++ b/content/events/src/nsEventDispatcher.cpp
@@ -571,16 +571,19 @@ nsEventDispatcher::CreateEvent(nsPresCon
                                     static_cast<nsKeyEvent*>(aEvent));
     case NS_MOUSE_EVENT:
     case NS_POPUP_EVENT:
       return NS_NewDOMMouseEvent(aDOMEvent, aPresContext,
                                  static_cast<nsInputEvent*>(aEvent));
     case NS_MOUSE_SCROLL_EVENT:
       return NS_NewDOMMouseScrollEvent(aDOMEvent, aPresContext,
                                  static_cast<nsInputEvent*>(aEvent));
+    case NS_DRAG_EVENT:
+      return NS_NewDOMDragEvent(aDOMEvent, aPresContext,
+                                 static_cast<nsDragEvent*>(aEvent));
     case NS_POPUPBLOCKED_EVENT:
       return NS_NewDOMPopupBlockedEvent(aDOMEvent, aPresContext,
                                         static_cast<nsPopupBlockedEvent*>
                                                    (aEvent));
     case NS_TEXT_EVENT:
       return NS_NewDOMTextEvent(aDOMEvent, aPresContext,
                                 static_cast<nsTextEvent*>(aEvent));
     case NS_BEFORE_PAGE_UNLOAD_EVENT:
@@ -617,16 +620,19 @@ nsEventDispatcher::CreateEvent(nsPresCon
   // And if we didn't get an event, check the type argument.
 
   if (aEventType.LowerCaseEqualsLiteral("mouseevent") ||
       aEventType.LowerCaseEqualsLiteral("mouseevents") ||
       aEventType.LowerCaseEqualsLiteral("popupevents"))
     return NS_NewDOMMouseEvent(aDOMEvent, aPresContext, nsnull);
   if (aEventType.LowerCaseEqualsLiteral("mousescrollevents"))
     return NS_NewDOMMouseScrollEvent(aDOMEvent, aPresContext, nsnull);
+  if (aEventType.LowerCaseEqualsLiteral("dragevent") ||
+      aEventType.LowerCaseEqualsLiteral("dragevents"))
+    return NS_NewDOMDragEvent(aDOMEvent, aPresContext, nsnull);
   if (aEventType.LowerCaseEqualsLiteral("keyboardevent") ||
       aEventType.LowerCaseEqualsLiteral("keyevents"))
     return NS_NewDOMKeyboardEvent(aDOMEvent, aPresContext, nsnull);
   if (aEventType.LowerCaseEqualsLiteral("mutationevent") ||
         aEventType.LowerCaseEqualsLiteral("mutationevents"))
     return NS_NewDOMMutationEvent(aDOMEvent, aPresContext, nsnull);
   if (aEventType.LowerCaseEqualsLiteral("textevent") ||
       aEventType.LowerCaseEqualsLiteral("textevents"))
--- a/content/events/src/nsEventListenerManager.cpp
+++ b/content/events/src/nsEventListenerManager.cpp
@@ -257,20 +257,23 @@ static const EventDispatchData sLoadEven
   { 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_DRAGDROP,    HANDLER(&nsIDOMDragListener::DragDrop)    },
   { NS_DRAGDROP_GESTURE,     HANDLER(&nsIDOMDragListener::DragGesture) },
   { NS_DRAGDROP_DRAG,        HANDLER(&nsIDOMDragListener::Drag)        },
-  { NS_DRAGDROP_END,         HANDLER(&nsIDOMDragListener::DragEnd)     }
+  { 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
@@ -55,16 +55,17 @@
 #include "nsIWidget.h"
 #include "nsPresContext.h"
 #include "nsIPresShell.h"
 #include "nsDOMEvent.h"
 #include "nsGkAtoms.h"
 #include "nsIEditorDocShell.h"
 #include "nsIFormControl.h"
 #include "nsIComboboxControlFrame.h"
+#include "nsIDOMNSHTMLElement.h"
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsIDOMHTMLInputElement.h"
 #include "nsIDOMNSHTMLInputElement.h"
 #include "nsIDOMHTMLSelectElement.h"
 #include "nsIDOMHTMLTextAreaElement.h"
 #include "nsIDOMHTMLAreaElement.h"
 #include "nsIDOMHTMLButtonElement.h"
 #include "nsIDOMHTMLObjectElement.h"
@@ -108,20 +109,21 @@
 #include "nsIDOMKeyEvent.h"
 #include "nsIObserverService.h"
 #include "nsIDocShell.h"
 #include "nsIMarkupDocumentViewer.h"
 #include "nsIScrollableViewProvider.h"
 #include "nsIDOMDocumentRange.h"
 #include "nsIDOMDocumentEvent.h"
 #include "nsIDOMMouseEvent.h"
+#include "nsIDOMDragEvent.h"
 #include "nsIDOMEventTarget.h"
 #include "nsIDOMDocumentView.h"
-#include "nsIDOMAbstractView.h"
 #include "nsIDOMNSUIEvent.h"
+#include "nsDOMDragEvent.h"
 
 #include "nsIDOMRange.h"
 #include "nsCaret.h"
 #include "nsILookAndFeel.h"
 #include "nsWidgetsCID.h"
 
 #include "nsIFrameFrame.h"
 #include "nsIFrameTraversal.h"
@@ -135,16 +137,24 @@
 #include "nsIProperties.h"
 #include "nsISupportsPrimitives.h"
 #include "nsEventDispatcher.h"
 #include "nsPresShellIterator.h"
 
 #include "nsServiceManagerUtils.h"
 #include "nsITimer.h"
 
+#include "nsIDragService.h"
+#include "nsIDragSession.h"
+#include "nsDOMDataTransfer.h"
+#include "nsContentAreaDragDrop.h"
+#ifdef MOZ_XUL
+#include "nsITreeBoxObject.h"
+#endif
+
 #ifdef XP_MACOSX
 #include <Events.h>
 #endif
 
 #if defined(DEBUG_rods) || defined(DEBUG_bryner)
 //#define DEBUG_DOCSHELL_FOCUS
 #endif
 
@@ -848,17 +858,20 @@ nsEventStateManager::PreHandleEvent(nsPr
     break;
 #ifdef CLICK_HOLD_CONTEXT_MENUS
   case NS_DRAGDROP_GESTURE:
     // an external drag gesture event came in, not generated internally
     // by Gecko. Make sure we get rid of the click-hold timer.
     KillClickHoldTimer();
     break;
 #endif
+  case NS_DRAGDROP_DROP:
   case NS_DRAGDROP_OVER:
+    // NS_DRAGDROP_DROP is fired before NS_DRAGDROP_DRAGDROP so send
+    // the enter/exit events before NS_DRAGDROP_DROP.
     GenerateDragDropEnterExit(aPresContext, (nsGUIEvent*)aEvent);
     break;
   case NS_GOTFOCUS:
     {
 #ifdef DEBUG_smaug
       printf("nsEventStateManager::PreHandleEvent, NS_GOTFOCUS \n");
 #endif
       // This is called when a child widget has received focus.
@@ -1966,62 +1979,321 @@ nsEventStateManager::GenerateDragGesture
     if (PR_ABS(pt.x - mGestureDownPoint.x) > pixelThresholdX ||
         PR_ABS(pt.y - mGestureDownPoint.y) > pixelThresholdY) {
 #ifdef CLICK_HOLD_CONTEXT_MENUS
       // stop the click-hold before we fire off the drag gesture, in case
       // it takes a long time
       KillClickHoldTimer();
 #endif
 
-      nsCOMPtr<nsIContent> targetContent = mGestureDownContent;
+      nsRefPtr<nsDOMDataTransfer> dataTransfer = new nsDOMDataTransfer();
+      if (!dataTransfer)
+        return;
+
+      PRBool isSelection = PR_FALSE;
+      nsCOMPtr<nsIContent> eventContent, targetContent;
+      mCurrentTarget->GetContentForEvent(aPresContext, aEvent,
+                                         getter_AddRefs(eventContent));
+      if (eventContent)
+        DetermineDragTarget(aPresContext, eventContent, dataTransfer,
+                            &isSelection, getter_AddRefs(targetContent));
+
       // Stop tracking the drag gesture now. This should stop us from
       // reentering GenerateDragGesture inside DOM event processing.
       StopTrackingDragGesture();
 
+      if (!targetContent)
+        return;
+
       nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetWindow();
 
       // get the widget from the target frame
-      nsMouseEvent gestureEvent(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_GESTURE,
-                                widget, nsMouseEvent::eReal);
+      nsDragEvent startEvent(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_START, widget);
+      FillInEventFromGestureDown(&startEvent);
+
+      nsDragEvent gestureEvent(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_GESTURE, widget);
       FillInEventFromGestureDown(&gestureEvent);
 
+      startEvent.dataTransfer = gestureEvent.dataTransfer = dataTransfer;
+
       // 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 the draggesture event to the DOM
+      // Dispatch both the dragstart and draggesture events to the DOM
       nsEventStatus status = nsEventStatus_eIgnore;
-      nsEventDispatcher::Dispatch(targetContent, aPresContext, &gestureEvent, nsnull,
+      nsEventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nsnull,
                                   &status);
 
+      nsDragEvent* event = &startEvent;
+      if (status != nsEventStatus_eConsumeNoDefault) {
+        status = nsEventStatus_eIgnore;
+        nsEventDispatcher::Dispatch(targetContent, aPresContext, &gestureEvent, nsnull,
+                                    &status);
+        event = &gestureEvent;
+      }
+
+      // now that the dataTransfer has been updated in the dragstart and
+      // draggesture events, make it read only so that the data doesn't
+      // change during the drag.
+      dataTransfer->SetReadOnly();
+
+      if (status != nsEventStatus_eConsumeNoDefault)
+        DoDefaultDragStart(aPresContext, event, dataTransfer,
+                           targetContent, isSelection);
+
       // 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;
     }
 
     // Now flush all pending notifications, for better responsiveness
     // while dragging.
     FlushPendingEvents(aPresContext);
   }
 } // GenerateDragGesture
 
+void
+nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext,
+                                         nsIContent* aSelectionTarget,
+                                         nsDOMDataTransfer* aDataTransfer,
+                                         PRBool* aIsSelection,
+                                         nsIContent** aTargetNode)
+{
+  *aTargetNode = nsnull;
+
+  nsCOMPtr<nsISupports> container = aPresContext->GetContainer();
+  nsCOMPtr<nsIDOMWindow> window = do_GetInterface(container);
+
+  // GetDragData determines if a selection, link or image in the content
+  // should be dragged, and places the data associated with the drag in the
+  // data transfer. Skip this check for chrome shells.
+  PRBool canDrag;
+  nsCOMPtr<nsIContent> dragDataNode;
+  nsCOMPtr<nsIDocShellTreeItem> dsti = do_QueryInterface(container);
+  if (dsti) {
+    PRInt32 type = -1;
+    if (NS_SUCCEEDED(dsti->GetItemType(&type)) &&
+        type != nsIDocShellTreeItem::typeChrome) {
+      // mGestureDownContent is the node where the mousedown event for the drag
+      // occured, and aSelectionTarget is the node to use when a selection is used
+      nsresult rv =
+        nsContentAreaDragDrop::GetDragData(window, mGestureDownContent,
+                                           aSelectionTarget, mGestureDownAlt,
+                                           aDataTransfer, &canDrag, aIsSelection,
+                                           getter_AddRefs(dragDataNode));
+      if (NS_FAILED(rv) || !canDrag)
+        return;
+    }
+  }
+
+  // if GetDragData returned a node, use that as the node being dragged.
+  // Otherwise, if a selection is being dragged, use the node within the
+  // selection that was dragged. Otherwise, just use the mousedown target.
+  nsIContent* dragContent = mGestureDownContent;
+  if (dragDataNode)
+    dragContent = dragDataNode;
+  else if (*aIsSelection)
+    dragContent = aSelectionTarget;
+
+  nsIContent* originalDragContent = dragContent;
+
+  // If a selection isn't being dragged, look for an ancestor with the
+  // draggable property set. If one is found, use that as the target of the
+  // drag instead of the node that was clicked on. If a draggable node wasn't
+  // found, just use the clicked node.
+  if (!*aIsSelection) {
+    while (dragContent) {
+      nsCOMPtr<nsIDOMNSHTMLElement> htmlElement = do_QueryInterface(dragContent);
+      if (htmlElement) {
+        PRBool draggable = PR_FALSE;
+        htmlElement->GetDraggable(&draggable);
+        if (draggable)
+          break;
+      }
+      else {
+        nsCOMPtr<nsIDOMXULElement> xulElement = do_QueryInterface(dragContent);
+        if (xulElement) {
+          // All XUL elements are draggable, so if a XUL element is
+          // encountered, stop looking for draggable nodes and just use the
+          // original clicked node instead.
+          // XXXndeakin
+          // In the future, we will want to improve this so that XUL has a
+          // better way to specify whether something is draggable than just
+          // on/off.
+          dragContent = mGestureDownContent;
+          break;
+        }
+        // otherwise, it's not an HTML or XUL element, so just keep looking
+      }
+      dragContent = dragContent->GetParent();
+    }
+  }
+
+  // if no node in the hierarchy was found to drag, but the GetDragData method
+  // returned a node, use that returned node. Otherwise, just use the original
+  // node that was clicked.
+  if (!dragContent) {
+    if (dragDataNode)
+      dragContent = originalDragContent;
+    else
+      dragContent = mGestureDownContent;
+  }
+
+  if (dragContent) {
+    // if an ancestor node was used instead, clear the drag data
+    // XXXndeakin rework this a bit. Find a way to just not call GetDragData if we don't need to.
+    if (dragContent != originalDragContent)
+      aDataTransfer->ClearAll();
+    *aTargetNode = dragContent;
+    NS_ADDREF(*aTargetNode);
+  }
+}
+
+void
+nsEventStateManager::DoDefaultDragStart(nsPresContext* aPresContext,
+                                        nsDragEvent* aDragEvent,
+                                        nsDOMDataTransfer* aDataTransfer,
+                                        nsIContent* aDragTarget,
+                                        PRBool aIsSelection)
+{
+  nsCOMPtr<nsIDragService> dragService =
+    do_GetService("@mozilla.org/widget/dragservice;1");
+  if (!dragService)
+    return;
+
+  // Default handling for the draggesture/dragstart event.
+  //
+  // First, check if a drag session already exists. This means that the drag
+  // service was called directly within a draggesture handler. In this case,
+  // don't do anything more, as it is assumed that the handler is managing
+  // drag and drop manually.
+  nsCOMPtr<nsIDragSession> dragSession;
+  dragService->GetCurrentSession(getter_AddRefs(dragSession));
+  if (dragSession)
+    return; // already a drag in progress
+
+  // No drag session is currently active, so check if a handler added
+  // any items to be dragged. If not, there isn't anything to drag.
+  PRUint32 count = 0;
+  if (aDataTransfer)
+    aDataTransfer->GetMozItemCount(&count);
+  if (!count)
+    return;
+
+  // Get the target being dragged, which may not be the same as the
+  // target of the mouse event. If one wasn't set in the
+  // aDataTransfer during the event handler, just use the original
+  // target instead.
+  nsCOMPtr<nsIDOMNode> dragTarget;
+  nsCOMPtr<nsIDOMElement> dragTargetElement;
+  aDataTransfer->GetDragTarget(getter_AddRefs(dragTargetElement));
+  dragTarget = do_QueryInterface(dragTargetElement);
+  if (!dragTarget) {
+    dragTarget = do_QueryInterface(aDragTarget);
+    if (!dragTarget)
+      return;
+  }
+
+  // check which drag effect should initially be used
+  PRUint32 effectAllowed;
+  aDataTransfer->GetEffectAllowedInt(&effectAllowed);
+
+  PRInt32 action = 0;
+  if (effectAllowed != nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
+    action = effectAllowed;
+
+  // get any custom drag image that was set
+  PRInt32 imageX, imageY;
+  nsIDOMElement* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
+
+  // If a selection is being dragged, and no custom drag image was
+  // set, get the selection so that the drag region can be created
+  // from the selection area. If a custom image was set, it doesn't
+  // matter what the selection is since the image will be used instead.
+  nsISelection* selection = nsnull;
+  if (aIsSelection && !dragImage) {
+    nsIDocument* doc = aDragTarget->GetCurrentDoc();
+    if (doc) {
+      nsIPresShell* presShell = doc->GetPrimaryShell();
+      if (presShell) {
+        selection = presShell->GetCurrentSelection(
+                      nsISelectionController::SELECTION_NORMAL);
+      }
+    }
+  }
+
+  nsCOMPtr<nsISupportsArray> transArray;
+  aDataTransfer->GetTransferables(getter_AddRefs(transArray));
+  if (!transArray)
+    return;
+
+  // XXXndeakin don't really want to create a new drag DOM event
+  // here, but we need something to pass to the InvokeDragSession
+  // methods.
+  nsCOMPtr<nsIDOMEvent> domEvent;
+  NS_NewDOMDragEvent(getter_AddRefs(domEvent), aPresContext, aDragEvent);
+
+  nsCOMPtr<nsIDOMDragEvent> domDragEvent = do_QueryInterface(domEvent);
+  // if creating a drag event failed, starting a drag session will
+  // just fail.
+  if (selection) {
+    dragService->InvokeDragSessionWithSelection(selection, transArray,
+                                                action, domDragEvent,
+                                                aDataTransfer);
+  }
+  else {
+    // if dragging within a XUL tree and no custom drag image was
+    // set, the region argument to InvokeDragSessionWithImage needs
+    // to be set to the area encompassing the selected rows of the
+    // tree to ensure that the drag feedback gets clipped to those
+    // rows. For other content, region should be null.
+    nsCOMPtr<nsIScriptableRegion> region;
+#ifdef MOZ_XUL
+    if (dragTarget && !dragImage) {
+      nsCOMPtr<nsIContent> content = do_QueryInterface(dragTarget);
+      if (content->NodeInfo()->Equals(nsGkAtoms::treechildren,
+                                      kNameSpaceID_XUL)) {
+        nsIDocument* doc = content->GetCurrentDoc();
+        if (doc) {
+          nsIPresShell* presShell = doc->GetPrimaryShell();
+          if (presShell) {
+            nsIFrame* frame = presShell->GetPrimaryFrameFor(content);
+            if (frame) {
+              nsITreeBoxObject* treeBoxObject;
+              CallQueryInterface(frame, &treeBoxObject);
+              treeBoxObject->GetSelectionRegion(getter_AddRefs(region));
+            }
+          }
+        }
+      }
+    }
+#endif
+
+    dragService->InvokeDragSessionWithImage(dragTarget, transArray,
+                                            region, action, dragImage,
+                                            imageX, imageY, domDragEvent,
+                                            aDataTransfer);
+  }
+}
+
 nsresult
 nsEventStateManager::GetMarkupDocumentViewer(nsIMarkupDocumentViewer** aMv)
 {
   *aMv = nsnull;
 
   if(!gLastFocusedDocument) return NS_ERROR_FAILURE;
 
   nsPIDOMWindow* ourWindow = gLastFocusedDocument->GetWindow();
@@ -2534,20 +2806,134 @@ nsEventStateManager::PostHandleEvent(nsP
         break;
       }
       *aStatus = nsEventStatus_eConsumeNoDefault;
 
     }
 
     break;
 
+  case NS_DRAGDROP_ENTER:
+  case NS_DRAGDROP_OVER:
+    {
+      NS_ASSERTION(aEvent->eventStructType == NS_DRAG_EVENT, "Expected a drag event");
+
+      nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
+      if (!dragSession)
+        break;
+
+      // the initial dataTransfer is the one from the dragstart event that
+      // was set on the dragSession when the drag began.
+      nsCOMPtr<nsIDOMNSDataTransfer> dataTransfer;
+      nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
+      dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
+
+      nsCOMPtr<nsIDOMNSDataTransfer> initialDataTransferNS = 
+        do_QueryInterface(initialDataTransfer);
+
+      // cancelling a dragenter or dragover event means that a drop should be
+      // allowed, so update the dropEffect and the canDrop state to indicate
+      // that a drag is allowed. If the event isn't cancelled, a drop won't be
+      // allowed. Essentially, to allow a drop somewhere, specify the effects
+      // using the effectAllowed and dropEffect properties in a dragenter or
+      // dragover event and cancel the event. To not allow a drop somewhere,
+      // don't cancel the event or set the effectAllowed or dropEffect to
+      // "none". This way, if the event is just ignored, no drop will be
+      // allowed.
+      PRUint32 dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
+      if (nsEventStatus_eConsumeNoDefault == *aStatus) {
+        // if the event has a dataTransfer set, use it.
+        nsDragEvent *dragEvent = (nsDragEvent*)aEvent;
+        if (dragEvent->dataTransfer) {
+          // get the dataTransfer and the dropEffect that was set on it
+          dataTransfer = do_QueryInterface(dragEvent->dataTransfer);
+          dataTransfer->GetDropEffectInt(&dropEffect);
+        }
+        else {
+          // if dragEvent->dataTransfer is null, it means that no attempt was
+          // made to access the dataTransfer during the event, yet the event
+          // was cancelled. Instead, use the initial data transfer available
+          // from the drag session. The drop effect would not have been
+          // initialized (which is done in nsDOMDragEvent::GetDataTransfer),
+          // so set it from the drag action. We'll still want to filter it
+          // based on the effectAllowed below.
+          dataTransfer = initialDataTransferNS;
+
+          PRUint32 action;
+          dragSession->GetDragAction(&action);
+
+          // filter the drop effect based on the action. Use UNINITIALIZED as
+          // any effect is allowed.
+          dropEffect = nsDOMDragEvent::FilterDropEffect(action,
+                         nsIDragService::DRAGDROP_ACTION_UNINITIALIZED);
+        }
+
+        // At this point, if the dataTransfer is null, it means that the
+        // drag was originally started by directly calling the drag service.
+        // Just assume that all effects are allowed.
+        PRUint32 effectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
+        if (dataTransfer)
+          dataTransfer->GetEffectAllowedInt(&effectAllowed);
+
+        // set the drag action based on the drop effect and effect allowed.
+        // The drop effect field on the drag transfer object specifies the
+        // desired current drop effect. However, it cannot be used if the
+        // effectAllowed state doesn't include that type of action. If the
+        // dropEffect is "none", then the action will be 'none' so a drop will
+        // not be allowed.
+        PRUint32 action = nsIDragService::DRAGDROP_ACTION_NONE;
+        if (effectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED ||
+            dropEffect & effectAllowed)
+          action = dropEffect;
+
+        if (action == nsIDragService::DRAGDROP_ACTION_NONE)
+          dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
+
+        // inform the drag session that a drop is allowed on this node.
+        dragSession->SetDragAction(action);
+        dragSession->SetCanDrop(action != nsIDragService::DRAGDROP_ACTION_NONE);
+      }
+
+      // now set the drop effect in the initial dataTransfer. This ensures
+      // that we can get the desired drop effect in the drop event.
+      if (initialDataTransferNS)
+        initialDataTransferNS->SetDropEffectInt(dropEffect);
+    }
+    break;
+
   case NS_DRAGDROP_DROP:
+    {
+      // now fire the dragdrop event, for compatibility with XUL
+      if (mCurrentTarget && nsEventStatus_eConsumeNoDefault != *aStatus) {
+        nsCOMPtr<nsIContent> targetContent;
+        mCurrentTarget->GetContentForEvent(presContext, aEvent,
+                                           getter_AddRefs(targetContent));
+
+        nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetWindow();
+        nsDragEvent event(NS_IS_TRUSTED_EVENT(aEvent), NS_DRAGDROP_DRAGDROP, widget);
+
+        nsMouseEvent* mouseEvent = 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);
+        }
+      }
+      break;
+    }
   case NS_DRAGDROP_EXIT:
-    // clean up after ourselves. make sure we do this _after_ the event, else we'll
-    // clean up too early!
+     // make sure to fire the enter and exit_synth events after the
+     // NS_DRAGDROP_EXIT event, otherwise we'll clean up too early
     GenerateDragDropEnterExit(presContext, (nsGUIEvent*)aEvent);
     break;
 
   case NS_KEY_UP:
     break;
 
   case NS_KEY_PRESS:
     if (nsEventStatus_eConsumeNoDefault != *aStatus) {
@@ -3171,16 +3557,18 @@ nsEventStateManager::GenerateDragDropEnt
         nsCOMPtr<nsIContent> lastContent;
         nsCOMPtr<nsIContent> targetContent;
         mCurrentTarget->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(targetContent));
 
         if ( mLastDragOverFrame ) {
           //The frame has changed but the content may not have. Check before dispatching to content
           mLastDragOverFrame->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(lastContent));
 
+          FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_LEAVE_SYNTH,
+                              targetContent, lastContent, mLastDragOverFrame);
           FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_EXIT_SYNTH,
                               targetContent, lastContent, mLastDragOverFrame);
         }
 
         FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_ENTER,
                             lastContent, targetContent, mCurrentTarget);
 
         mLastDragOverFrame = mCurrentTarget;
@@ -3191,16 +3579,18 @@ nsEventStateManager::GenerateDragDropEnt
   case NS_DRAGDROP_DROP:
   case NS_DRAGDROP_EXIT:
     {
       //This is actually the window mouse exit event.
       if ( mLastDragOverFrame ) {
         nsCOMPtr<nsIContent> lastContent;
         mLastDragOverFrame->GetContentForEvent(aPresContext, aEvent, getter_AddRefs(lastContent));
 
+        FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_LEAVE_SYNTH,
+                            nsnull, lastContent, mLastDragOverFrame);
         FireDragEnterOrExit(aPresContext, aEvent, NS_DRAGDROP_EXIT_SYNTH,
                             nsnull, lastContent, mLastDragOverFrame);
 
         mLastDragOverFrame = nsnull;
       }
     }
     break;
   }
@@ -3216,18 +3606,17 @@ 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);
+  nsDragEvent event(NS_IS_TRUSTED_EVENT(aEvent), aMsg, aEvent->widget);
   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;
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -55,16 +55,17 @@
 
 class nsIScrollableView;
 class nsIPresShell;
 class nsIDocShell;
 class nsIDocShellTreeNode;
 class nsIDocShellTreeItem;
 class nsIFocusController;
 class imgIContainer;
+class nsDOMDataTransfer;
 
 // mac uses click-hold context menus, a holdover from 4.x
 #ifdef XP_MACOSX
 #define CLICK_HOLD_CONTEXT_MENUS 1
 #endif
 
 
 /*
@@ -313,16 +314,48 @@ protected:
   nsresult ChangeFullZoom(PRInt32 change);
   // end mousewheel functions
 
   // routines for the d&d gesture tracking state machine
   void BeginTrackingDragGesture ( nsPresContext* aPresContext, nsMouseEvent* inDownEvent,
                                   nsIFrame* inDownFrame ) ;
   void StopTrackingDragGesture ( ) ;
   void GenerateDragGesture ( nsPresContext* aPresContext, nsMouseEvent *aEvent ) ;
+
+  /**
+   * Determine which node the drag should be targeted at.
+   * This is either the node clicked when there is a selection, or, for HTML,
+   * the element with a draggable property set to true.
+   *
+   * aSelectionTarget - target to check for selection
+   * aDataTransfer - data transfer object that will contain the data to drag
+   * aIsSelection - [out] set to true if a selection is being dragged
+   * aTargetNode - [out] the draggable node, or null if there isn't one
+   */
+  void DetermineDragTarget(nsPresContext* aPresContext,
+                           nsIContent* aSelectionTarget,
+                           nsDOMDataTransfer* aDataTransfer,
+                           PRBool* aIsSelection,
+                           nsIContent** aTargetNode);
+
+  /*
+   * Perform the default handling for the dragstart/draggesture event and set up a
+   * drag for aDataTransfer if it contains any data.
+   *
+   * aDragEvent - the dragstart/draggesture event
+   * aDataTransfer - the data transfer that holds the data to be dragged
+   * aDragTarget - the target of the drag
+   * aIsSelection - true if a selection is being dragged
+   */
+  void DoDefaultDragStart(nsPresContext* aPresContext,
+                          nsDragEvent* aDragEvent,
+                          nsDOMDataTransfer* aDataTransfer,
+                          nsIContent* aDragTarget,
+                          PRBool aIsSelection);
+
   PRBool IsTrackingDragGesture ( ) const { return mGestureDownContent != nsnull; }
   /**
    * Set the fields of aEvent to reflect the mouse position and modifier keys
    * that were set when the user first pressed the mouse button (stored by
    * BeginTrackingDragGesture). aEvent->widget must be
    * mCurrentTarget->GetWindow().
    */
   void FillInEventFromGestureDown(nsMouseEvent* aEvent);
--- a/content/events/test/Makefile.in
+++ b/content/events/test/Makefile.in
@@ -56,16 +56,18 @@ include $(topsrcdir)/config/rules.mk
 		test_bug379120.html \
 		test_bug391568.xhtml \
 		test_bug402089.html \
 		test_bug405632.html \
 		test_bug409604.html \
 		test_bug412567.html \
 		test_bug443985.html \
 		test_bug447736.html \
+		test_draggableprop.html \
+		test_dragstart.html \
 		$(NULL)
 
 _CHROME_FILES = \
 		test_bug415498.xul \
 		bug415498-doc1.html \
 		bug415498-doc2.html \
 		$(NULL)
 
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_draggableprop.html
@@ -0,0 +1,91 @@
+<html>
+<head>
+  <title>Tests for the draggable property on HTML elements</title>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/MochiKit/packed.js"></script>
+  <script type="application/javascript" 
+          src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>      
+
+<body>
+<p id="display"></p>
+<div id="content" style="display: none">
+</div>
+
+<span id="elem1">One</span>
+<span id="elem2" draggable="true">Two</span>
+<span id="elem3" draggable="">Three</span>
+<span id="elem4" draggable="false">Four</span>
+<span id="elem5" draggable="other">Five</span>
+
+<img id="img1" src="../happy.png">
+<img id="img2" src="../happy.png" draggable="true">
+<img id="img3" src="../happy.png" draggable="">
+<img id="img4" src="../happy.png" draggable="false">
+<img id="img5" src="../happy.png" draggable="other">
+
+<a id="a1">One</a>
+<a id="a2" draggable="true">Two</a>
+<a id="a3" draggable="">Three</a>
+<a id="a4" draggable="false">Four</a>
+<a id="a5" draggable="other">Five</a>
+
+<a id="ahref1" href="http://www.mozilla.org">One</a>
+<a id="ahref2" href="http://www.mozilla.org" draggable="true">Two</a>
+<a id="ahref3" href="http://www.mozilla.org" draggable="">Three</a>
+<a id="ahref4" href="http://www.mozilla.org" draggable="false">Four</a>
+<a id="ahref5" href="http://www.mozilla.org" draggable="other">Five</a>
+
+<script>
+function check()
+{
+  try {
+    checkElements(1, false, true, false, true);
+    checkElements(2, true, true, true, true);
+    checkElements(3, false, true, false, true);
+    checkElements(4, false, false, false, false);
+    checkElements(5, false, true, false, true);
+  }
+  catch (ex) {
+    is("script error", ex, "fail");
+  }
+}
+
+function checkElements(idx, estate, istate, astate, ahrefstate)
+{
+  checkElement("elem" + idx, estate, false);
+  checkElement("img" + idx, istate, true);
+  checkElement("a" + idx, astate, false);
+  checkElement("ahref" + idx, ahrefstate, true);
+}
+
+function checkElement(elemid, state, statedef)
+{
+  var elem = document.getElementById(elemid);
+
+  is(elem.draggable, state, elemid + "-initial");
+  elem.draggable = true;
+  is(elem.draggable, true, elemid + "-true");
+  elem.draggable = false;
+  is(elem.draggable, false, elemid + "-false");
+
+  elem.setAttribute("draggable", "true");
+  is(elem.draggable, true, elemid + "-attr-true");
+  elem.setAttribute("draggable", "false");
+  is(elem.draggable, false, elemid + "-attr-false");
+  elem.setAttribute("draggable", "other");
+  is(elem.draggable, statedef, elemid + "-attr-other");
+  elem.setAttribute("draggable", "");
+  is(elem.draggable, statedef, elemid + "-attr-empty");
+  elem.removeAttribute("draggable");
+  is(elem.draggable, statedef, elemid + "-attr-removed");
+}
+
+check();
+
+</script>
+
+</body>
+</html>
+
+
new file mode 100644
--- /dev/null
+++ b/content/events/test/test_dragstart.html
@@ -0,0 +1,526 @@
+<html>
+<head>
+  <title>Tests for the dragstart event</title>
+  <link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
+  <script type="application/javascript" src="/MochiKit/packed.js"></script>
+  <script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>      
+  <script type="application/javascript" src="/tests/SimpleTest/EventUtils.js"></script>      
+
+<!--
+ This test checks the dragstart event and the DataTransfer object
+  -->
+ 
+<script>
+
+SimpleTest.waitForExplicitFinish();
+
+var gDragInfo;
+var gDataTransfer = null;
+
+function runTests()
+{
+  // first, create a selection and try dragging it
+  var draggable = $("draggable");
+  window.getSelection().selectAllChildren(draggable);
+  synthesizeMouse(draggable, 6, 6, { type: "mousedown" });
+  synthesizeMouse(draggable, 14, 14, { type: "mousemove" });
+
+  // the dragstart should have occured due to moving the mouse. gDataTransfer
+  // caches the dataTransfer that was used, however it should now be empty and
+  // be read only.
+  ok(gDataTransfer instanceof DataTransfer, "DataTransfer after dragstart event");
+  checkTypes(gDataTransfer, [], 0, "after dragstart event");
+
+  expectError(function() gDataTransfer.setData("text/plain", "Some Text"),
+              "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setData when read only");
+  expectError(function() gDataTransfer.clearData("text/plain"),
+              "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "clearData when read only");
+  expectError(function() gDataTransfer.mozSetDataAt("text/plain", "Some Text", 0),
+              "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setDataAt when read only");
+  expectError(function() gDataTransfer.mozClearDataAt("text/plain", 0),
+              "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "clearDataAt when read only");
+  expectError(function() gDataTransfer.setDragImage(draggable, 10, 10),
+              "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "setDragImage when read only");
+  expectError(function() gDataTransfer.addElement(draggable),
+              "NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR", "addElement when read only");
+
+  // next, dragging links and images
+  sendMouseEventsForDrag("link");
+  sendMouseEventsForDrag("image");
+
+//  disable testing input dragging for now, as it doesn't seem to be testable 
+//  draggable = $("input");
+//  draggable.setSelectionRange(0, 4);
+//  synthesizeMouse(draggable, 8, 8, { type: "mousedown" });
+//  synthesizeMouse(draggable, 15, 15, { type: "mousemove" });
+//  sendMouseEventsForDrag("input");
+
+  // next, check if the draggable attribute can be used to adjust the drag target
+  sendMouseEventsForDrag("dragtrue");
+  ok(gDragInfo.itemCount == 0 && gDragInfo.types.length == 0 && gDragInfo.target == $("dragtrue"), "draggable true node");
+  sendMouseEventsForDrag("spantrue");
+  ok(gDragInfo.itemCount == 0 && gDragInfo.types.length == 0 && gDragInfo.target == $("dragtrue"), "draggable true child");
+  sendMouseEventsForDrag("dragfalse");
+  ok(gDragInfo.itemCount == 0 && gDragInfo.types.length == 0 && gDragInfo.target == $("dragfalse").firstChild,
+     "draggable false node");
+  sendMouseEventsForDrag("spanfalse");
+  ok(gDragInfo.itemCount == 0 && gDragInfo.types.length == 0 && gDragInfo.target == $("spanfalse").firstChild,
+     "draggable false child");
+
+  var evt = document.createEvent("dragevent");
+  ok(evt instanceof DragEvent, "synthetic dragevent class")
+  ok(evt instanceof MouseEvent, "synthetic event inherits from MouseEvent")
+  evt.initDragEvent("dragstart", true, true, window, 1, null);
+  $("synthetic").dispatchEvent(evt);
+
+  var evt = document.createEvent("dragevents");
+  ok(evt instanceof DragEvent, "synthetic dragevents class")
+  evt.initDragEvent("dragover", true, true, window, 0, null);
+  $("synthetic2").dispatchEvent(evt);
+
+  SimpleTest.finish();
+}
+
+function sendMouseEventsForDrag(nodeid)
+{
+  gDragInfo = null;
+
+  var draggable = $(nodeid);
+  synthesizeMouse(draggable, 3, 3, { type: "mousedown" });
+  synthesizeMouse(draggable, 10, 10, { type: "mousemove" });
+}
+
+function doDragStartSelection(event)
+{
+  is(event.type, "dragstart", "dragstart event type");
+  is(event.target, $("draggable").firstChild, "dragstart event target");
+  is(event.bubbles, true, "dragstart event bubbles");
+  is(event.cancelable, true, "dragstart event cancelable");
+
+  var dt = event.dataTransfer;
+  ok(dt instanceof DataTransfer, "dataTransfer is DataTransfer");
+  gDataTransfer = dt;
+
+  var types = dt.types;
+  is(types instanceof DOMStringList, true, "initial types is a DOMStringList");
+  checkTypes(dt, ["text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial selection");
+
+  is(dt.getData("text/plain"), "This is a draggable bit of text.", "initial selection text/plain");
+  is(dt.getData("text/html"), "<div id=\"draggable\" ondragstart=\"doDragStartSelection(event)\">This is a <em>draggable</em> bit of text.</div>",
+     "initial selection text/html");
+
+  // text/unicode and Text are available for compatibility. They retrieve the
+  // text/plain data
+  is(dt.getData("text/unicode"), "This is a draggable bit of text.", "initial selection text/unicode");
+  is(dt.getData("Text"), "This is a draggable bit of text.", "initial selection Text");
+
+  is(dt.mozItemCount, 1, "initial selection item count");
+
+  dt.clearData("text/plain");
+  dt.clearData("text/html");
+  dt.clearData("text/_moz_htmlinfo");
+  dt.clearData("text/_moz_htmlcontext");
+
+  test_DataTransfer(dt);
+}
+
+function test_DataTransfer(dt)
+{
+  is(dt.mozItemCount, 0, "empty itemCount");
+
+  var types = dt.types;
+  is(types instanceof DOMStringList, true, "empty types is a DOMStringList");
+  checkTypes(dt, [], 0, "empty");
+  is(dt.getData("text/plain"), "", "empty data is empty");
+
+  // calling setDataAt requires an index that is 0 <= index <= dt.itemCount
+  expectError(function() dt.mozSetDataAt("text/plain", "Some Text", 1),
+              "NS_ERROR_DOM_INDEX_SIZE_ERR", "setDataAt index too high");
+
+  // because an exception occured, the data should not have been added
+  is(dt.mozItemCount, 0, "empty setDataAt index too high itemCount");
+  dt.getData("text/plain", "", "empty setDataAt index too high getData");
+
+  // if the type is '', do nothing, or return ''
+  dt.setData("", "Invalid Type");
+  is(dt.types.length, 0, "invalid type setData");
+  is(dt.getData(""), "", "invalid type getData"),
+  dt.mozSetDataAt("", "Invalid Type", 0);
+  is(dt.types.length, 0, "invalid type setDataAt");
+  is(dt.mozGetDataAt("", 0), null, "invalid type getDataAt"),
+
+  // similar with clearDataAt and getDataAt
+  expectError(function() dt.mozGetDataAt("text/plain", 1),
+              "NS_ERROR_DOM_INDEX_SIZE_ERR", "getDataAt index too high");
+  expectError(function() dt.mozClearDataAt("text/plain", 1),
+              "NS_ERROR_DOM_INDEX_SIZE_ERR", "clearDataAt index too high");
+
+  dt.setData("text/plain", "Sample Text");
+  is(dt.mozItemCount, 1, "added plaintext itemCount");
+  checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "added plaintext");
+
+   // after all those exceptions, the data should still be the same
+  checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "added plaintext after exception");
+
+  // modifying the data associated with the format should give it the new value
+  dt.setData("text/plain", "Modified Text");
+  is(dt.mozItemCount, 1, "modified plaintext itemCount");
+  checkOneDataItem(dt, ["text/plain"], ["Modified Text"], 0, "modified plaintext");
+
+  dt.setData("text/html", "<strong>Modified Text</strong>");
+  is(dt.mozItemCount, 1, "modified html itemCount");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                       ["Modified Text", "<strong>Modified Text</strong>"],
+                       0, "modified html");
+
+  // modifying data for a type that already exists should adjust it in place,
+  // not reinsert it at the beginning
+  dt.setData("text/plain", "New Text");
+  is(dt.mozItemCount, 1, "modified text again itemCount");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                       ["New Text", "<strong>Modified Text</strong>"],
+                       0, "modified text again");
+
+  var draggable = $("draggable");
+  dt.setData("application/-moz-node", draggable);
+  checkOneDataItem(dt, ["text/plain", "text/html", "application/-moz-node"],
+                       ["New Text", "<strong>Modified Text</strong>", draggable],
+                       0, "added node");
+
+  dt.clearData(""); // null means clear all
+  is(dt.mozItemCount, 0, "itemCount after clearData empty string");
+  checkTypes(dt, [], 0, "empty after clearData empty string");
+  
+  dt.setData("text/plain", 22);
+  dt.setData("text/html", 5.6);
+  dt.setData("text/xml", 5.6);
+  checkTypes(dt, ["text/plain", "text/html", "text/xml"], ["22", "5.6", ""], 0, "add numeric and empty data");
+
+  dt.clearData(); // no argument means clear all
+  is(dt.mozItemCount, 0, "itemCount after clearData no argument");
+  checkTypes(dt, [], 0, "empty after clearData no argument");
+
+  // check 'Text' type which should convert into text/plain
+  dt.setData("Text", "Sample Text");
+  checkOneDataItem(dt, ["text/plain"], ["Sample Text"], 0, "set Text");
+  is(dt.getData("Text"), "Sample Text", "getData Text");
+  is(dt.mozGetDataAt("Text", 0), "Sample Text", "getDataAt Text");
+  dt.setData("text/plain", "More Text");
+  checkOneDataItem(dt, ["text/plain"], ["More Text"], 0, "set text/plain after set Text");
+
+  dt.mozClearDataAt("", 0); // null means clear all
+  is(dt.mozItemCount, 0, "itemCount after clearDataAt empty string");
+  checkTypes(dt, [], 0, "empty after clearDataAt empty string");
+
+  // check text/uri-list type
+  dt.setData("text/uri-list", "http://www.mozilla.org");
+  checkURL(dt, "http://www.mozilla.org", "http://www.mozilla.org", 0, "set text/uri-list");
+
+  // check URL type which should add text/uri-list data
+  dt.setData("URL", "ftp://ftp.example.com");
+  checkURL(dt, "ftp://ftp.example.com", "ftp://ftp.example.com", 0, "set URL");
+  checkTypes(dt, ["text/uri-list"], ["ftp://ftp.example.com"], "url types");
+
+  // clearing text/uri-list data
+  dt.clearData("text/uri-list");
+  is(dt.mozItemCount, 0, "itemCount after clear url-list");
+  is(dt.getData("text/uri-list"), "", "text/uri-list after clear url-list");
+  is(dt.getData("URL"), "", "URL after clear url-list");
+
+  // check text/uri-list parsing
+  dt.setData("text/uri-list", "#http://www.mozilla.org\nhttp://www.xulplanet.com\nhttp://www.example.com");
+  checkURL(dt, "http://www.xulplanet.com",
+           "#http://www.mozilla.org\nhttp://www.xulplanet.com\nhttp://www.example.com",
+           0, "uri-list 3 lines");
+
+  dt.setData("text/uri-list", "#http://www.mozilla.org");
+  is(dt.getData("URL"), "", "uri-list commented");
+  dt.setData("text/uri-list", "#http://www.mozilla.org\n");
+  is(dt.getData("URL"), "", "uri-list commented with newline");
+
+  // check that clearing the URL type also clears the text/uri-list type
+  dt.clearData("URL");
+  is(dt.getData("text/uri-list"), "", "clear URL");
+
+  dt.setData("text/uri-list", "#http://www.mozilla.org\n\n\n\n\n");
+  is(dt.getData("URL"), "", "uri-list with blank lines");
+  dt.setData("text/uri-list", "");
+  is(dt.getData("URL"), "", "empty uri-list");
+  dt.setData("text/uri-list", "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com  \r\n");
+  is(dt.getData("URL"), "http://www.xulplanet.com", "uri-list mix");
+  dt.setData("text/uri-list", "\nhttp://www.mozilla.org");
+  is(dt.getData("URL"), "", "empty line to start uri-list");
+  dt.setData("text/uri-list", "  http://www.mozilla.org#anchor  ");
+  is(dt.getData("URL"), "http://www.mozilla.org#anchor", "uri-list with spaces and hash");
+
+  // ensure that setDataAt works the same way
+  dt.mozSetDataAt("text/uri-list", "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com  \r\n", 0);
+  checkURL(dt, "http://www.xulplanet.com",
+           "#http://www.mozilla.org\n#Sample\nhttp://www.xulplanet.com  \r\n",
+           0, "uri-list mix setDataAt");
+
+  // now test adding multiple items to be dragged using the setDataAt method
+  dt.clearData();
+  dt.mozSetDataAt("text/plain", "First Item", 0);
+  dt.mozSetDataAt("text/plain", "Second Item", 1);
+  expectError(function() dt.mozSetDataAt("text/plain", "Some Text", 3),
+              "NS_ERROR_DOM_INDEX_SIZE_ERR", "setDataAt index too high with two items");
+  is(dt.mozItemCount, 2, "setDataAt item itemCount");
+  checkOneDataItem(dt, ["text/plain"], ["First Item"], 0, "setDataAt item at index 0");
+  checkOneDataItem(dt, ["text/plain"], ["Second Item"], 1, "setDataAt item at index 1");
+
+  dt.mozSetDataAt("text/html", "<em>First Item</em>", 0);
+  dt.mozSetDataAt("text/html", "<em>Second Item</em>", 1);
+  is(dt.mozItemCount, 2, "setDataAt two types item itemCount");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["First Item", "<em>First Item</em>"], 0, "setDataAt two types item at index 0");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["Second Item", "<em>Second Item</em>"], 1, "setDataAt two types item at index 1");
+
+  dt.mozSetDataAt("text/html", "<em>Changed First Item</em>", 0);
+  dt.mozSetDataAt("text/plain", "Changed Second Item", 1);
+  is(dt.mozItemCount, 2, "changed with setDataAt item itemCount");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["First Item", "<em>Changed First Item</em>"], 0, "changed with setDataAt item at index 0");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["Changed Second Item", "<em>Second Item</em>"], 1, "changed with setDataAt item at index 1");
+
+  dt.setData("text/html", "Changed with setData");
+  is(dt.mozItemCount, 2, "changed with setData");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["First Item", "Changed with setData"], 0, "changed with setData item at index 0");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["Changed Second Item", "<em>Second Item</em>"], 1, "changed with setData item at index 1");
+
+  dt.mozSetDataAt("application/-moz-node", draggable, 2);
+  is(dt.mozItemCount, 3, "setDataAt node itemCount");
+  checkOneDataItem(dt, ["application/-moz-node"], [draggable], 2, "setDataAt node item at index 2");
+
+  dt.mozClearDataAt("text/html", 1);
+  is(dt.mozItemCount, 3, "clearDataAt itemCount");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["First Item", "Changed with setData"], 0, "clearDataAt item at index 0");
+  checkOneDataItem(dt, ["text/plain"], ["Changed Second Item"], 1, "clearDataAt item at index 1");
+
+  dt.mozClearDataAt("text/plain", 1);
+  is(dt.mozItemCount, 2, "clearDataAt last type itemCount");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["First Item", "Changed with setData"], 0, "clearDataAt last type at index 0");
+  checkOneDataItem(dt, ["application/-moz-node"], [draggable], 1, "clearDataAt last type item at index 2");
+  expectError(function() dt.mozGetDataAt("text/plain", 2),
+              "NS_ERROR_DOM_INDEX_SIZE_ERR", "getDataAt after item removed index too high");
+
+  dt.mozSetDataAt("text/unknown", "Unknown type", 2);
+  dt.mozSetDataAt("text/unknown", "Unknown type", 1);
+  is(dt.mozItemCount, 3, "add unknown type");
+  checkOneDataItem(dt, ["application/-moz-node", "text/unknown"],
+                   [draggable, "Unknown type"], 1, "add unknown type item at index 1");
+  checkOneDataItem(dt, ["text/unknown"], ["Unknown type"], 2, "add unknown type item at index 2");
+
+  dt.mozClearDataAt("", 1);
+  is(dt.mozItemCount, 2, "clearDataAt empty string");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["First Item", "Changed with setData"], 0, "clearDataAt empty string item at index 0");
+  checkOneDataItem(dt, ["text/unknown"],
+                   ["Unknown type"], 1, "clearDataAt empty string item at index 1");
+
+  // passing a format that doesn't exist to clearData or clearDataAt should just
+  // do nothing
+  dt.clearData("text/something");
+  dt.mozClearDataAt("text/something", 1);
+  is(dt.mozItemCount, 2, "clearData type that does not exist");
+  checkOneDataItem(dt, ["text/plain", "text/html"],
+                   ["First Item", "Changed with setData"], 0, "clearData type that does not exist item at index 0");
+  checkOneDataItem(dt, ["text/unknown"],
+                   ["Unknown type"], 1, "clearData type that does not exist item at index 1");
+
+  expectError(function() dt.mozClearDataAt("text/plain", 3),
+              "NS_ERROR_DOM_INDEX_SIZE_ERR", "clearData index too high with two items");
+
+  // ensure that clearData() removes all data associated with the first item
+  dt.clearData();
+  is(dt.mozItemCount, 1, "clearData no argument with multiple items itemCount");
+  checkOneDataItem(dt, ["text/unknown"],
+                   ["Unknown type"], 0, "clearData no argument with multiple items item at index 1");
+
+  // remove tha remaining data
+  dt.mozClearDataAt("", 0);
+  is(dt.mozItemCount, 0, "all data cleared");
+
+  // now check the effectAllowed and dropEffect properties
+  is(dt.dropEffect, "none", "initial dropEffect");
+  is(dt.effectAllowed, "uninitialized", "initial effectAllowed");
+
+  ["copy", "none", "link", "", "other", "copyMove", "all", "uninitialized", "move"].forEach(
+    function (i) {
+      dt.dropEffect = i;
+      is(dt.dropEffect, i == "" || i == "other" || i == "copyMove" ||
+                        i == "all" || i == "uninitialized" ? "link" : i,
+         "dropEffect set to " + i);
+      is(dt.effectAllowed, "uninitialized", "effectAllowed not modified by dropEffect set to " + i);
+    }
+  );
+
+  ["move", "copy", "link", "", "other", "moveCopy", "copyMove",
+   "linkMove", "copyLink", "all", "uninitialized", "none"].forEach(
+    function (i) {
+      dt.effectAllowed = i;
+      is(dt.dropEffect, "move", "dropEffect not modified by effectAllowed set to " + i);
+      is(dt.effectAllowed, i == "" || i == "other" || i == "moveCopy" ? "link" : i,
+         "effectAllowed set to " + i);
+    }
+  );
+}
+
+function doDragStartLink(event)
+{
+  var dt = event.dataTransfer;
+  checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
+                  "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial link");
+
+  is(dt.mozItemCount, 1, "initial link item count");
+  is(dt.getData("text/uri-list"), "http://www.mozilla.org/", "link text/uri-list");
+  is(dt.getData("text/plain"), "http://www.mozilla.org/", "link text/plain");
+
+  event.preventDefault();
+}
+
+function doDragStartImage(event)
+{
+  var dataurl = $("image").src;
+
+  var dt = event.dataTransfer;
+  checkTypes(dt, ["text/x-moz-url", "text/x-moz-url-data", "text/x-moz-url-desc", "text/uri-list",
+                  "text/_moz_htmlcontext", "text/_moz_htmlinfo", "text/html", "text/plain"], 0, "initial image");
+
+  is(dt.mozItemCount, 1, "initial image item count");
+  is(dt.getData("text/uri-list"), dataurl, "image text/uri-list");
+  is(dt.getData("text/plain"), dataurl, "image text/plain");
+
+  event.preventDefault();
+}
+
+function doDragStartInput(event)
+{
+  var dt = event.dataTransfer;
+  checkTypes(dt, ["text/plain"], 0, "initial input");
+
+  is(dt.mozItemCount, 1, "initial input item count");
+//  is(dt.getData("text/plain"), "Text", "input text/plain");
+
+//  event.preventDefault();
+}
+
+function doDragStartSynthetic(event)
+{
+  is(event.type, "dragstart", "synthetic dragstart event type");
+return;
+  var dt = event.dataTransfer;
+  ok(dt instanceof DataTransfer, "synthetic dragstart dataTransfer is DataTransfer");
+
+  checkTypes(dt, [], 0, "synthetic dragstart");
+
+  is(event.detail, 1, "synthetic dragstart detail");
+
+  dt.setData("text/plain", "Text");
+  is(dt.getData("text/plain"), "Text", "synthetic dragstart data is set after adding");
+}
+
+function doDragOverSynthetic(event)
+{
+  is(event.type, "dragover", "synthetic dragover event type");
+return;
+
+  var dt = event.dataTransfer;
+  ok(dt instanceof DataTransfer, "synthetic dragover dataTransfer is DataTransfer");
+
+  checkTypes(dt, [], 0, "synthetic dragover");
+
+  dt.setData("text/plain", "Text");
+  is(dt.getData("text/plain"), "Text", "synthetic dragover data is set after adding");
+}
+
+function onDragStartDraggable(event)
+{
+  var dt = event.dataTransfer;
+  gDragInfo = { itemCount: dt.mozItemCount, types: dt.types, target: event.originalTarget };
+}
+
+function checkOneDataItem(dt, expectedtypes, expecteddata, index, testid)
+{
+  checkTypes(dt, expectedtypes, index, testid);
+  for (var f = 0; f < expectedtypes.length; f++) {
+    if (index == 0)
+      is(dt.getData(expectedtypes[f]), expecteddata[f], testid + " getData " + expectedtypes[f]);
+    is(dt.mozGetDataAt(expectedtypes[f], index), expecteddata[f] ? expecteddata[f] : null,
+       testid + " getDataAt " + expectedtypes[f]);
+  }
+}
+
+function checkTypes(dt, expectedtypes, index, testid)
+{
+  if (index == 0) {
+    var types = dt.types;
+    is(types.length, expectedtypes.length, testid + " types length");
+    for (var f = 0; f < expectedtypes.length; f++) {
+      is(types[f], expectedtypes[f], testid + " " + types[f] + " check");
+    }
+  }
+
+  types = dt.mozTypesAt(index);
+  is(types.length, expectedtypes.length, testid + " typesAt length");
+  for (var f = 0; f < expectedtypes.length; f++) {
+    is(types[f], expectedtypes[f], testid + " " + types[f] + " at " + index + " check");
+  }
+}
+
+function checkURL(dt, url, fullurllist, index, testid)
+{
+  is(dt.getData("text/uri-list"), fullurllist, testid + " text/uri-list");
+  is(dt.getData("URL"), url, testid + " URL");
+  is(dt.mozGetDataAt("text/uri-list", 0), fullurllist, testid + " text/uri-list");
+  is(dt.mozGetDataAt("URL", 0), fullurllist, testid + " URL");
+}
+
+function expectError(fn, eid, testid)
+{
+  var error = "";
+  try {
+    fn();
+  } catch (ex) {
+    error = ex.name;
+  }
+  is(error, eid, testid + " causes exception " + eid);
+}
+
+</script>
+
+</head>
+
+<body style="height: 300px; overflow: auto;" onload="setTimeout(runTests, 0)">
+
+<div id="draggable" ondragstart="doDragStartSelection(event)">This is a <em>draggable</em> bit of text.</div>
+
+<a id="link" href="http://www.mozilla.org/" ondragstart="doDragStartLink(event)">mozilla.org</a>
+
+<img id="image" src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82"
+     ondragstart="doDragStartImage(event)">
+
+<input id="input" value="Text in a box" ondragstart="doDragStartInput(event)">
+
+<div style="-moz-user-select: none;" ondragstart="onDragStartDraggable(event)">
+  <div id="dragtrue" draggable="true">
+    This is a <span id="spantrue">draggable</span> area.
+  </div>
+  <div id="dragfalse" draggable="false">
+    This is a <span id="spanfalse">non-draggable</span> area.
+  </div>
+</div>
+
+<!--iframe src="http://www.mozilla.org" width="400" height="400"></iframe-->
+
+<div id="synthetic" ondragstart="doDragStartSynthetic(event)">Synthetic Event Dispatch</div>
+<div id="synthetic2" ondragover="doDragOverSynthetic(event)">Synthetic Event Dispatch</div>
+
+</body>
+</html>
--- a/content/html/content/src/nsGenericHTMLElement.cpp
+++ b/content/html/content/src/nsGenericHTMLElement.cpp
@@ -851,16 +851,32 @@ nsGenericHTMLElement::SetSpellcheck(PRBo
 {
   if (aSpellcheck) {
     return SetAttrHelper(nsGkAtoms::spellcheck, NS_LITERAL_STRING("true"));
   }
 
   return SetAttrHelper(nsGkAtoms::spellcheck, NS_LITERAL_STRING("false"));
 }
 
+NS_IMETHODIMP
+nsGenericHTMLElement::GetDraggable(PRBool* aDraggable)
+{
+  *aDraggable = AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
+                             nsGkAtoms::_true, eIgnoreCase);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsGenericHTMLElement::SetDraggable(PRBool aDraggable)
+{
+  return SetAttrHelper(nsGkAtoms::draggable,
+                       aDraggable ? NS_LITERAL_STRING("true") :
+                                    NS_LITERAL_STRING("false"));
+}
+
 PRBool
 nsGenericHTMLElement::InNavQuirksMode(nsIDocument* aDoc)
 {
   return aDoc && aDoc->GetCompatibilityMode() == eCompatibility_NavQuirks;
 }
 
 void
 nsGenericHTMLElement::UpdateEditableState()
--- a/content/html/content/src/nsGenericHTMLElement.h
+++ b/content/html/content/src/nsGenericHTMLElement.h
@@ -140,25 +140,27 @@ public:
   nsresult GetOffsetTop(PRInt32* aOffsetTop);
   nsresult GetOffsetLeft(PRInt32* aOffsetLeft);
   nsresult GetOffsetWidth(PRInt32* aOffsetWidth);
   nsresult GetOffsetHeight(PRInt32* aOffsetHeight);
   nsresult GetOffsetParent(nsIDOMElement** aOffsetParent);
   virtual nsresult GetInnerHTML(nsAString& aInnerHTML);
   virtual nsresult SetInnerHTML(const nsAString& aInnerHTML);
   nsresult ScrollIntoView(PRBool aTop);
-  // Declare Focus(), Blur(), GetTabIndex(), SetTabIndex(), GetSpellcheck() and
-  // SetSpellcheck() such that classes that inherit interfaces with those 
-  // methods properly override them
+  // Declare Focus(), Blur(), GetTabIndex(), SetTabIndex(), GetSpellcheck(),
+  // SetSpellcheck(), and GetDraggable() such that classes that inherit interfaces
+  // with those methods properly override them
   NS_IMETHOD Focus();
   NS_IMETHOD Blur();
   NS_IMETHOD GetTabIndex(PRInt32 *aTabIndex);
   NS_IMETHOD SetTabIndex(PRInt32 aTabIndex);
   NS_IMETHOD GetSpellcheck(PRBool* aSpellcheck);
   NS_IMETHOD SetSpellcheck(PRBool aSpellcheck);
+  NS_IMETHOD GetDraggable(PRBool* aDraggable);
+  NS_IMETHOD SetDraggable(PRBool aDraggable);
   nsresult GetContentEditable(nsAString &aContentEditable);
   nsresult SetContentEditable(const nsAString &aContentEditable);
 
   // Implementation for nsIContent
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
--- a/content/html/content/src/nsHTMLAnchorElement.cpp
+++ b/content/html/content/src/nsHTMLAnchorElement.cpp
@@ -98,16 +98,19 @@ public:
 
   // nsILink
   NS_IMETHOD GetLinkState(nsLinkState &aState);
   NS_IMETHOD SetLinkState(nsLinkState aState);
   NS_IMETHOD GetHrefURI(nsIURI** aURI);
   NS_IMETHOD LinkAdded() { return NS_OK; }
   NS_IMETHOD LinkRemoved() { return NS_OK; }
 
+  // override from nsGenericHTMLElement
+  NS_IMETHOD GetDraggable(PRBool* aDraggable);
+
   virtual nsresult BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                               nsIContent* aBindingParent,
                               PRBool aCompileEventHandlers);
   virtual void UnbindFromTree(PRBool aDeep = PR_TRUE,
                               PRBool aNullParent = PR_TRUE);
   virtual void SetFocus(nsPresContext* aPresContext);
   virtual PRBool IsHTMLFocusable(PRBool *aIsFocusable, PRInt32 *aTabIndex);
 
@@ -173,16 +176,30 @@ NS_IMPL_STRING_ATTR(nsHTMLAnchorElement,
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Name, name)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rel, rel)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Rev, rev)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Shape, shape)
 NS_IMPL_INT_ATTR_DEFAULT_VALUE(nsHTMLAnchorElement, TabIndex, tabindex, 0)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, Type, type)
 NS_IMPL_STRING_ATTR(nsHTMLAnchorElement, AccessKey, accesskey)
 
+NS_IMETHODIMP
+nsHTMLAnchorElement::GetDraggable(PRBool* aDraggable)
+{
+  // links can be dragged as long as there is an href and the
+  // draggable attribute isn't false
+  if (HasAttr(kNameSpaceID_None, nsGkAtoms::href)) {
+    *aDraggable = !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
+                               nsGkAtoms::_false, eIgnoreCase);
+    return NS_OK;
+  }
+
+  // no href, so just use the same behavior as other elements
+  return nsGenericHTMLElement::GetDraggable(aDraggable);
+}
 
 nsresult
 nsHTMLAnchorElement::BindToTree(nsIDocument* aDocument, nsIContent* aParent,
                                 nsIContent* aBindingParent,
                                 PRBool aCompileEventHandlers)
 {
   nsresult rv = nsGenericHTMLElement::BindToTree(aDocument, aParent,
                                                  aBindingParent,
--- a/content/html/content/src/nsHTMLImageElement.cpp
+++ b/content/html/content/src/nsHTMLImageElement.cpp
@@ -102,16 +102,19 @@ public:
   NS_FORWARD_NSIDOMHTMLELEMENT(nsGenericHTMLElement::)
 
   // nsIDOMHTMLImageElement
   NS_DECL_NSIDOMHTMLIMAGEELEMENT
 
   // nsIDOMNSHTMLImageElement
   NS_DECL_NSIDOMNSHTMLIMAGEELEMENT
 
+  // override from nsGenericHTMLElement
+  NS_IMETHOD GetDraggable(PRBool* aDraggable);
+
   // nsIJSNativeInitializer
   NS_IMETHOD Initialize(nsISupports* aOwner, JSContext* aContext,
                         JSObject* aObj, PRUint32 argc, jsval* argv);
 
   // nsIContent
   virtual PRBool ParseAttribute(PRInt32 aNamespaceID,
                                 nsIAtom* aAttribute,
                                 const nsAString& aValue,
@@ -213,16 +216,25 @@ NS_IMPL_INT_ATTR(nsHTMLImageElement, Hsp
 NS_IMPL_BOOL_ATTR(nsHTMLImageElement, IsMap, ismap)
 NS_IMPL_URI_ATTR(nsHTMLImageElement, LongDesc, longdesc)
 NS_IMPL_STRING_ATTR(nsHTMLImageElement, Lowsrc, lowsrc)
 NS_IMPL_URI_ATTR(nsHTMLImageElement, Src, src)
 NS_IMPL_STRING_ATTR(nsHTMLImageElement, UseMap, usemap)
 NS_IMPL_INT_ATTR(nsHTMLImageElement, Vspace, vspace)
 
 NS_IMETHODIMP
+nsHTMLImageElement::GetDraggable(PRBool* aDraggable)
+{
+  // images may be dragged unless the draggable attribute is false
+  *aDraggable = !AttrValueIs(kNameSpaceID_None, nsGkAtoms::draggable,
+                             nsGkAtoms::_false, eIgnoreCase);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsHTMLImageElement::GetComplete(PRBool* aComplete)
 {
   NS_PRECONDITION(aComplete, "Null out param!");
   *aComplete = PR_TRUE;
 
   if (!mCurrentRequest) {
     return NS_OK;
   }
--- a/dom/public/coreEvents/nsIDOMDragListener.h
+++ b/dom/public/coreEvents/nsIDOMDragListener.h
@@ -39,21 +39,24 @@
 #ifndef nsIDOMDragListener_h__
 #define nsIDOMDragListener_h__
 
 #include "nsIDOMEvent.h"
 #include "nsIDOMEventListener.h"
 
 /*
  * 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 \
-{ /* CD5186C4-228F-4413-AFD9-B65DAA105714 */ \
-0xcd5186c4, 0x228f, 0x4413, \
-{0xaf, 0xd9, 0xb6, 0x5d, 0xaa, 0x10, 0x57, 0x14} }
+{ /* 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)
@@ -124,13 +127,18 @@ public:
   /**
    * 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/dom/public/idl/events/Makefile.in
+++ b/dom/public/idl/events/Makefile.in
@@ -62,16 +62,18 @@ SDK_XPIDLSRCS =                         
 	$(NULL)
 
 XPIDLSRCS =					\
 	nsIDOMNSEvent.idl			\
 	nsIDOMDataContainerEvent.idl	\
 	nsIDOMKeyEvent.idl			\
 	nsIDOMMutationEvent.idl			\
 	nsIDOMNSUIEvent.idl			\
+	nsIDOMDragEvent.idl  			\
+	nsIDOMDataTransfer.idl  			\
 	nsIDOMPopupBlockedEvent.idl		\
 	nsIDOMBeforeUnloadEvent.idl		\
 	nsIDOMNSEventTarget.idl			\
 	nsIDOMSmartCardEvent.idl                \
 	nsIDOMPageTransitionEvent.idl		\
 	nsIDOMCommandEvent.idl			\
 	nsIDOMMessageEvent.idl			\
 	$(NULL)
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/events/nsIDOMDataTransfer.idl
@@ -0,0 +1,237 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "domstubs.idl"
+
+interface nsIVariant;
+
+[scriptable, uuid(B5947DD0-8E86-4B9C-AA65-C86303EFCF94)]
+interface nsIDOMDataTransfer : nsISupports
+{
+  /**
+   * The actual effect that will be used, and should always be one of the
+   * possible values of effectAllowed.
+   *
+   * For dragstart, drag and dragleave events, the dropEffect is initialized
+   * to none. Any value assigned to the dropEffect will be set, but the value
+   * isn't used for anything.
+   *
+   * For the dragenter and dragover events, the dropEffect will be initialized
+   * based on what action the user is requesting. How this is determined is
+   * platform specific, but typically the user can press modifier keys to
+   * adjust which action is desired. Within an event handler for the dragenter
+   * and dragover events, the dropEffect should be modified if the action the
+   * user is requesting is not the one that is desired.
+   *
+   * For the drop and dragend events, the dropEffect will be initialized to
+   * the action that was desired, which will be the value that the dropEffect
+   * had after the last dragenter or dragover event.
+   *
+   * Possible values:
+   *  copy - a copy of the source item is made at the new location
+   *  move - an item is moved to a new location
+   *  link - a link is established to the source at the new location
+   *  none - the item may not be dropped
+   *
+   * Assigning any other value has no effect and retains the old value.
+   */
+  attribute DOMString dropEffect;
+
+  /*
+   * Specifies the effects that are allowed for this drag. You may set this in
+   * the dragstart event to set the desired effects for the source, and within
+   * the dragenter and dragover events to set the desired effects for the
+   * target. The value is not used for other events.
+   *
+   * Possible values:
+   *  copy - a copy of the source item is made at the new location
+   *  move - an item is moved to a new location
+   *  link - a link is established to the source at the new location
+   *  copyLink, copyMove, linkMove, all - combinations of the above
+   *  none - the item may not be dropped
+   *  uninitialized - the default value when the effect has not been set,
+   *                  equivalent to all.
+   *
+   * Assigning any other value has no effect and retains the old value.
+   */
+  attribute DOMString effectAllowed;
+
+  /**
+   * Holds a list of the format types of the data that is stored for the first
+   * item, in the same order the data was added. An empty list will be
+   * returned if no data was added.
+   */
+  readonly attribute nsIDOMDOMStringList types;
+
+  /**
+   * Remove the data associated with a given format. If format is empty or not
+   * specified, the data associated with all formats is removed. If data for
+   * the specified format does not exist, or the data transfer contains no
+   * data, this method will have no effect.
+   */
+  void clearData([optional] in DOMString format);
+
+  /**
+   * Set the data for a given format. If data for the format does not exist,
+   * it is added at the end, such that the last item in the types list will be
+   * the new format. If data for the format already exists, the existing data
+   * is replaced in the same position. That is, the order of the types list is
+   * not changed.
+   *
+   * @throws NS_ERROR_NULL_POINTER if the data is null
+   */
+  void setData(in DOMString format, in DOMString data);
+
+  /**
+   * Retrieves the data for a given format, or an empty string if data for
+   * that format does not exist or the data transfer contains no data.
+   */
+  DOMString getData(in DOMString format);
+
+  /**
+   * Set the image to be used for dragging if a custom one is desired. Most of
+   * the time, this would not be set, as a default image is created from the
+   * node that was dragged.
+   *
+   * If the node is an HTML img element, an HTML canvas element or a XUL image
+   * element, the image data is used. Otherwise, image should be a visible
+   * node and the drag image will be created from this. If image is null, any
+   * custom drag image is cleared and the default is used instead.
+   *
+   * The coordinates specify the offset into the image where the mouse cursor
+   * should be. To center the image for instance, use values that are half the
+   * width and height.
+   *
+   * @param image a node to use 
+   * @param x the horizontal offset
+   * @param y the vertical offset
+   * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
+   */
+  void setDragImage(in nsIDOMElement image, in long x, in long y);
+
+  /*
+   * Set the drag source. Usually you would not change this, but it will
+   * affect which node the drag and dragend events are fired at. The
+   * default target is the node that was dragged.
+   *
+   * @param element drag source to use
+   * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
+   */
+  void addElement(in nsIDOMElement element);
+};
+
+[scriptable, uuid(A884E56C-1678-4978-AD20-142EE94108F5)]
+interface nsIDOMNSDataTransfer : nsISupports
+{
+  /*
+   * Integer version of dropEffect, set to one of the constants in nsIDragService.
+   */
+  [noscript] attribute unsigned long dropEffectInt;
+
+  /*
+   * Integer version of effectAllowed, set to one or a combination of the
+   * constants in nsIDragService.
+   */
+  [noscript] attribute unsigned long effectAllowedInt;
+
+  /**
+   * Creates a copy of the data transfer object
+   */
+  [noscript] nsIDOMDataTransfer clone(in PRUint32 aEventType);
+
+  /**
+   * The number of items being dragged.
+   */
+  readonly attribute unsigned long mozItemCount;
+
+  /**
+   * Holds a list of the format types of the data that is stored for an item
+   * at the specified index. If the index is not in the range from 0 to
+   * itemCount - 1, an empty string list is returned.
+   */
+  nsIDOMDOMStringList mozTypesAt(in unsigned long index);
+
+  /**
+   * Remove the data associated with the given format for an item at the
+   * specified index. The index is in the range from zero to itemCount - 1.
+   *
+   * If the last format for the item is removed, the entire item is removed,
+   * reducing the itemCount by one.
+   *
+   * If format is empty, then the data associated with all formats is removed.
+   * If the format is not found, then this method has no effect.
+   *
+   * @param format the format to remove
+   * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater or equal than itemCount
+   * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
+   */
+  void mozClearDataAt(in DOMString format, in unsigned long index);
+
+  /*
+   * A data transfer may store multiple items, each at a given zero-based
+   * index. setDataAt may only be called with an index argument less than
+   * itemCount in which case an existing item is modified, or equal to
+   * itemCount in which case a new item is added, and the itemCount is
+   * incremented by one.
+   *
+   * Data should be added in order of preference, with the most specific
+   * format added first and the least specific format added last. If data of
+   * the given format already exists, it is replaced in the same position as
+   * the old data.
+   *
+   * The data should be either a string, a primitive boolean or number type
+   * (which will be converted into a string) or an nsISupports.
+   *
+   * @param format the format to add
+   * @param data the data to add
+   * @throws NS_ERROR_NULL_POINTER if the data is null
+   * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater than itemCount
+   * @throws NO_MODIFICATION_ALLOWED_ERR if the item cannot be modified
+   */
+  void mozSetDataAt(in DOMString format, in nsIVariant data, in unsigned long index);
+
+  /**
+   * Retrieve the data associated with the given format for an item at the
+   * specified index, or null if it does not exist. The index should be in the
+   * range from zero to itemCount - 1.
+   *
+   * @param format the format of the data to look up
+   * @returns the data of the given format, or null if it doesn't exist.
+   * @throws NS_ERROR_DOM_INDEX_SIZE_ERR if index is greater or equal than itemCount
+   */
+  nsIVariant mozGetDataAt(in DOMString format, in unsigned long index);
+};
new file mode 100644
--- /dev/null
+++ b/dom/public/idl/events/nsIDOMDragEvent.idl
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is mozilla.org code.
+ *
+ * The Initial Developer of the Original Code is the Mozilla Corporation.
+ * Portions created by the Initial Developer are Copyright (C) 2008
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Neil Deakin <enndeakin@gmail.com>
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either of the GNU General Public License Version 2 or later (the "GPL"),
+ * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include "domstubs.idl"
+#include "nsIDOMMouseEvent.idl"
+
+interface nsIDOMAbstractView;
+interface nsIDOMDataTransfer;
+
+[scriptable, uuid(18FEEFD7-A461-4865-BCF1-4DC8A2F30584)]
+interface nsIDOMDragEvent : nsIDOMMouseEvent
+{
+  readonly attribute nsIDOMDataTransfer dataTransfer;
+
+  void initDragEvent(in DOMString typeArg,
+                     in boolean canBubbleArg,
+                     in boolean cancelableArg,
+                     in nsIDOMAbstractView aView,
+                     in PRInt32 aDetail,
+                     in nsIDOMDataTransfer aDataTransfer);
+
+  void initDragEventNS(in DOMString namespaceURIArg,
+                       in DOMString typeArg,
+                       in boolean canBubbleArg,
+                       in boolean cancelableArg,
+                       in nsIDOMAbstractView aView,
+                       in PRInt32 aDetail,
+                       in nsIDOMDataTransfer aDataTransfer);
+};
--- a/dom/public/idl/html/nsIDOMNSHTMLElement.idl
+++ b/dom/public/idl/html/nsIDOMNSHTMLElement.idl
@@ -33,30 +33,33 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "domstubs.idl"
 
-[scriptable, uuid(5D090306-86AA-43F8-A78F-7346084DA4C5)]
+[scriptable, uuid(7F142F9A-FBA7-4949-93D6-CF08A974AC51)]
 interface nsIDOMNSHTMLElement : nsISupports
 {
   readonly attribute long             offsetTop;
   readonly attribute long             offsetLeft;
   readonly attribute long             offsetWidth;
   readonly attribute long             offsetHeight;
   readonly attribute nsIDOMElement    offsetParent;
            attribute DOMString        innerHTML;
 
            attribute long             tabIndex;
 
            attribute DOMString        contentEditable;
 
+           // for WHAT-WG drag and drop
+           attribute boolean          draggable;
+
   void blur();
   void focus();
 
   // |top| is optional in JS, scriptability of this method is done in
   // nsHTMLElementSH
   void scrollIntoView(in boolean top);
 
            attribute boolean         spellcheck;
--- a/dom/public/nsDOMClassInfoID.h
+++ b/dom/public/nsDOMClassInfoID.h
@@ -79,16 +79,17 @@ enum nsDOMClassInfoID {
   eDOMClassInfo_DocumentStyleSheetList_id,
 
   // Event classes
   eDOMClassInfo_Event_id,
   eDOMClassInfo_MutationEvent_id,
   eDOMClassInfo_UIEvent_id,
   eDOMClassInfo_MouseEvent_id,
   eDOMClassInfo_MouseScrollEvent_id,
+  eDOMClassInfo_DragEvent_id,
   eDOMClassInfo_KeyboardEvent_id,
   eDOMClassInfo_PopupBlockedEvent_id,
 
   // HTML classes
   eDOMClassInfo_HTMLDocument_id,
   eDOMClassInfo_HTMLOptionsCollection_id,
   eDOMClassInfo_HTMLFormControlCollection_id,
   eDOMClassInfo_HTMLGenericCollection_id,
@@ -443,16 +444,18 @@ enum nsDOMClassInfoID {
 #endif
   eDOMClassInfo_ProgressEvent_id,
 
   eDOMClassInfo_XMLHttpRequestUpload_id,
 
   // DOM Traversal NodeIterator class
   eDOMClassInfo_NodeIterator_id,
 
+  eDOMClassInfo_DataTransfer_id,
+
   // This one better be the last one in this list
   eDOMClassInfoIDCount
 };
 
 /**
  * nsIClassInfo helper macros
  */
 
--- a/dom/src/base/nsDOMClassInfo.cpp
+++ b/dom/src/base/nsDOMClassInfo.cpp
@@ -230,16 +230,17 @@
 #include "nsIDOMCDATASection.h"
 #include "nsIDOMProcessingInstruction.h"
 #include "nsIDOMNotation.h"
 #include "nsIDOMNSEvent.h"
 #include "nsIDOMDataContainerEvent.h"
 #include "nsIDOMKeyEvent.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMMouseScrollEvent.h"
+#include "nsIDOMDragEvent.h"
 #include "nsIDOMCommandEvent.h"
 #include "nsIDOMPopupBlockedEvent.h"
 #include "nsIDOMBeforeUnloadEvent.h"
 #include "nsIDOMMutationEvent.h"
 #include "nsIDOMSmartCardEvent.h"
 #include "nsIDOMXULCommandEvent.h"
 #include "nsIDOMPageTransitionEvent.h"
 #include "nsIDOMMessageEvent.h"
@@ -444,16 +445,19 @@
 // Storage includes
 #include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 #include "nsIDOMStorageList.h"
 #include "nsIDOMStorageItem.h"
 #include "nsIDOMStorageEvent.h"
 #include "nsIDOMToString.h"
 
+// Drag and drop
+#include "nsIDOMDataTransfer.h"
+
 // Offline includes
 #include "nsIDOMLoadStatusList.h"
 #include "nsIDOMLoadStatus.h"
 #include "nsIDOMLoadStatusEvent.h"
 
 // Geolocation
 #include "nsIDOMGeoGeolocation.h"
 #include "nsIDOMGeoPosition.h"
@@ -648,16 +652,18 @@ static nsDOMClassInfoData sClassInfoData
   NS_DEFINE_CLASSINFO_DATA(MutationEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(UIEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MouseEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(MouseScrollEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+  NS_DEFINE_CLASSINFO_DATA(DragEvent, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(KeyboardEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CLASSINFO_DATA(PopupBlockedEvent, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // Misc HTML classes
   NS_DEFINE_CLASSINFO_DATA(HTMLDocument, nsHTMLDocumentSH,
                            DOCUMENT_SCRIPTABLE_FLAGS)
@@ -1276,16 +1282,21 @@ static nsDOMClassInfoData sClassInfoData
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   NS_DEFINE_CLASSINFO_DATA(XMLHttpRequestUpload, nsEventTargetSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
   // DOM Traversal NodeIterator class  
   NS_DEFINE_CLASSINFO_DATA(NodeIterator, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
+  // data transfer for drag and drop
+  NS_DEFINE_CLASSINFO_DATA(DataTransfer, nsDOMGenericSH,
+                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
+
 };
 
 // Objects that shuld be constructable through |new Name();|
 struct nsContractIDMapData
 {
   PRInt32 mDOMClassInfoID;
   const char *mContractID;
 };
@@ -2117,16 +2128,21 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(MouseScrollEvent, nsIDOMMouseScrollEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMMouseScrollEvent)
     DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(DragEvent, nsIDOMDragEvent)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDragEvent)
+    DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
+  DOM_CLASSINFO_MAP_END
+
   DOM_CLASSINFO_MAP_BEGIN(HTMLDocument, nsIDOMHTMLDocument)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLDocument)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSHTMLDocument)
     DOM_CLASSINFO_DOCUMENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(HTMLOptionsCollection, nsIDOMHTMLOptionsCollection)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLOptionsCollection)
@@ -3488,28 +3504,34 @@ nsDOMClassInfo::Init()
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLMediaError)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(HTMLAudioElement, nsIDOMHTMLAudioElement)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMHTMLAudioElement)
     DOM_CLASSINFO_GENERIC_HTML_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
+
 #endif
   DOM_CLASSINFO_MAP_BEGIN(ProgressEvent, nsIDOMProgressEvent)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMProgressEvent)
     DOM_CLASSINFO_EVENT_MAP_ENTRIES
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN(XMLHttpRequestUpload, nsIXMLHttpRequestUpload)
     DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequestEventTarget)
     DOM_CLASSINFO_MAP_ENTRY(nsIXMLHttpRequestUpload)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 
+  DOM_CLASSINFO_MAP_BEGIN(DataTransfer, nsIDOMDataTransfer)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataTransfer)
+    DOM_CLASSINFO_MAP_ENTRY(nsIDOMNSDataTransfer)
+  DOM_CLASSINFO_MAP_END
+
 #ifdef NS_DEBUG
   {
     PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);
 
     if (i != eDOMClassInfoIDCount) {
       NS_ERROR("The number of items in sClassInfoData doesn't match the "
                "number of nsIDOMClassInfo ID's, this is bad! Fix it!");
 
--- a/editor/libeditor/base/nsEditorUtils.cpp
+++ b/editor/libeditor/base/nsEditorUtils.cpp
@@ -249,105 +249,16 @@ nsEditorHookUtils::GetHookEnumeratorFrom
   nsCOMPtr<nsIDocShell> docShell = do_QueryInterface(container);
   nsCOMPtr<nsIClipboardDragDropHookList> hookObj = do_GetInterface(docShell);
   if (!hookObj) return NS_ERROR_FAILURE;
 
   return hookObj->GetHookEnumerator(aResult);
 }
 
 PRBool
-nsEditorHookUtils::DoAllowDragHook(nsIDOMDocument *aDoc, nsIDOMEvent *aDragEvent)
-{
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
-  GetHookEnumeratorFromDocument(aDoc, getter_AddRefs(enumerator));
-  if (!enumerator)
-    return PR_TRUE;
-
-  PRBool hasMoreHooks = PR_FALSE;
-  while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks)
-  {
-    nsCOMPtr<nsISupports> isupp;
-    if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
-      break;
-
-    nsCOMPtr<nsIClipboardDragDropHooks> override = do_QueryInterface(isupp);
-    if (override)
-    {
-      PRBool canDrag = PR_TRUE;
-      nsresult hookres = override->AllowStartDrag(aDragEvent, &canDrag);
-      NS_ASSERTION(NS_SUCCEEDED(hookres), "hook failure in AllowStartDrag");
-      if (!canDrag)
-        return PR_FALSE;
-    }
-  }
-
-  return PR_TRUE;
-}
-
-PRBool
-nsEditorHookUtils::DoDragHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,
-                              nsITransferable *aTrans)
-{
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
-  GetHookEnumeratorFromDocument(aDoc, getter_AddRefs(enumerator));
-  if (!enumerator)
-    return PR_TRUE;
-
-  PRBool hasMoreHooks = PR_FALSE;
-  while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks)
-  {
-    nsCOMPtr<nsISupports> isupp;
-    if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
-      break;
-
-    nsCOMPtr<nsIClipboardDragDropHooks> override = do_QueryInterface(isupp);
-    if (override)
-    {
-      PRBool canInvokeDrag = PR_TRUE;
-      nsresult hookResult = override->OnCopyOrDrag(aEvent, aTrans, &canInvokeDrag);
-      NS_ASSERTION(NS_SUCCEEDED(hookResult), "hook failure in OnCopyOrDrag");
-      if (!canInvokeDrag)
-        return PR_FALSE;
-    }
-  }
-
-  return PR_TRUE;
-}
-
-PRBool
-nsEditorHookUtils::DoAllowDropHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,   
-                                   nsIDragSession *aSession)
-{
-  nsCOMPtr<nsISimpleEnumerator> enumerator;
-  GetHookEnumeratorFromDocument(aDoc, getter_AddRefs(enumerator));
-  if (!enumerator)
-    return PR_TRUE;
-
-  PRBool hasMoreHooks = PR_FALSE;
-  while (NS_SUCCEEDED(enumerator->HasMoreElements(&hasMoreHooks)) && hasMoreHooks)
-  {
-    nsCOMPtr<nsISupports> isupp;
-    if (NS_FAILED(enumerator->GetNext(getter_AddRefs(isupp))))
-      break;
-
-    nsCOMPtr<nsIClipboardDragDropHooks> override = do_QueryInterface(isupp);
-    if (override)
-    {
-      PRBool allowDrop = PR_TRUE;
-      nsresult hookResult = override->AllowDrop(aEvent, aSession, &allowDrop);
-      NS_ASSERTION(NS_SUCCEEDED(hookResult), "hook failure in AllowDrop");
-      if (!allowDrop)
-        return PR_FALSE;
-    }
-  }
-
-  return PR_TRUE;
-}
-
-PRBool
 nsEditorHookUtils::DoInsertionHook(nsIDOMDocument *aDoc, nsIDOMEvent *aDropEvent,  
                                    nsITransferable *aTrans)
 {
   nsCOMPtr<nsISimpleEnumerator> enumerator;
   GetHookEnumeratorFromDocument(aDoc, getter_AddRefs(enumerator));
   if (!enumerator)
     return PR_TRUE;
 
--- a/editor/libeditor/base/nsEditorUtils.h
+++ b/editor/libeditor/base/nsEditorUtils.h
@@ -269,21 +269,16 @@ class nsEditorUtils
 class nsIDragSession;
 class nsITransferable;
 class nsIDOMEvent;
 class nsISimpleEnumerator;
 
 class nsEditorHookUtils
 {
   public:
-    static PRBool   DoAllowDragHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent);
-    static PRBool   DoDragHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,
-                                    nsITransferable *aTrans);
-    static PRBool   DoAllowDropHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,
-                                    nsIDragSession *aSession);
     static PRBool   DoInsertionHook(nsIDOMDocument *aDoc, nsIDOMEvent *aEvent,
                                     nsITransferable *aTrans);
   private:
     static nsresult GetHookEnumeratorFromDocument(nsIDOMDocument *aDoc,
                                                   nsISimpleEnumerator **aEnumerator);
 };
 
 #endif // nsEditorUtils_h__
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -1492,18 +1492,16 @@ NS_IMETHODIMP nsHTMLEditor::InsertFromDr
 
   nsCOMPtr<nsIDragSession> dragSession;
   dragService->GetCurrentSession(getter_AddRefs(dragSession)); 
   if (!dragSession) return NS_OK;
 
   // transferable hooks here
   nsCOMPtr<nsIDOMDocument> domdoc;
   GetDocument(getter_AddRefs(domdoc));
-  if (!nsEditorHookUtils::DoAllowDropHook(domdoc, aDropEvent, dragSession))
-    return NS_OK;
 
   // find out if we have our internal html flavor on the clipboard.  We don't want to mess
   // around with cfhtml if we do.
   PRBool bHavePrivateHTMLFlavor = PR_FALSE;
   rv = dragSession->IsDataFlavorSupported(kHTMLContext, &bHavePrivateHTMLFlavor);
   if (NS_FAILED(rv)) return rv;
   
   // Get the nsITransferable interface for getting the data from the drop
--- a/editor/libeditor/text/nsEditorEventListeners.cpp
+++ b/editor/libeditor/text/nsEditorEventListeners.cpp
@@ -565,23 +565,16 @@ nsTextEditorDragListener::DragOver(nsIDO
     if (!dropParent)
       return NS_ERROR_FAILURE;
 
     if (!dropParent->IsEditable())
       return NS_OK;
   }
 
   PRBool canDrop = CanDrop(aDragEvent);
-  if (canDrop)
-  {
-    nsCOMPtr<nsIDOMDocument> domdoc;
-    mEditor->GetDocument(getter_AddRefs(domdoc));
-    canDrop = nsEditorHookUtils::DoAllowDropHook(domdoc, aDragEvent, dragSession);
-  }
-
   dragSession->SetCanDrop(canDrop);
 
   // We need to consume the event to prevent the browser's
   // default drag listeners from being fired. (Bug 199133)
 
   aDragEvent->PreventDefault(); // consumed
     
   if (canDrop)
--- a/editor/libeditor/text/nsPlaintextDataTransfer.cpp
+++ b/editor/libeditor/text/nsPlaintextDataTransfer.cpp
@@ -40,16 +40,17 @@
 
 #include "nsIDOMDocument.h"
 #include "nsIDocument.h"
 #include "nsIContent.h"
 #include "nsIFormControl.h"
 #include "nsIDOMEventTarget.h" 
 #include "nsIDOMNSEvent.h"
 #include "nsIDOMMouseEvent.h"
+#include "nsIDOMDragEvent.h"
 #include "nsISelection.h"
 #include "nsCRT.h"
 #include "nsServiceManagerUtils.h"
 
 #include "nsIDOMRange.h"
 #include "nsIDOMNSRange.h"
 #include "nsIDocumentEncoder.h"
 #include "nsISupportsPrimitives.h"
@@ -158,20 +159,16 @@ NS_IMETHODIMP nsPlaintextEditor::InsertF
   dragService->GetCurrentSession(getter_AddRefs(dragSession));
   if (!dragSession) return NS_OK;
 
   // Current doc is destination
   nsCOMPtr<nsIDOMDocument> destdomdoc; 
   rv = GetDocument(getter_AddRefs(destdomdoc)); 
   if (NS_FAILED(rv)) return rv;
 
-  // transferable hooks
-  if (!nsEditorHookUtils::DoAllowDropHook(destdomdoc, aDropEvent, dragSession))
-    return NS_OK;
-
   // Get the nsITransferable interface for getting the data from the drop
   nsCOMPtr<nsITransferable> trans;
   rv = PrepareTransferable(getter_AddRefs(trans));
   if (NS_FAILED(rv)) return rv;
   if (!trans) return NS_OK;  // NS_ERROR_FAILURE; SHOULD WE FAIL?
 
   PRUint32 numItems = 0; 
   rv = dragSession->GetNumDropItems(&numItems);
@@ -296,19 +293,16 @@ NS_IMETHODIMP nsPlaintextEditor::InsertF
 
   PRUint32 i; 
   for (i = 0; i < numItems; ++i)
   {
     rv = dragSession->GetData(trans, i);
     if (NS_FAILED(rv)) return rv;
     if (!trans) return NS_OK; // NS_ERROR_FAILURE; Should we fail?
 
-    if (!nsEditorHookUtils::DoInsertionHook(destdomdoc, aDropEvent, trans))
-      return NS_OK;
-
     // Beware! This may flush notifications via synchronous
     // ScrollSelectionIntoView.
     rv = InsertTextFromTransferable(trans, newSelectionParent, newSelectionOffset, deleteSelection);
   }
 
   return rv;
 }
 
@@ -358,23 +352,17 @@ NS_IMETHODIMP nsPlaintextEditor::CanDrag
       PRBool isTargetedCorrectly = PR_FALSE;
       res = selection->ContainsNode(eventTargetDomNode, PR_FALSE, &isTargetedCorrectly);
       if (NS_FAILED(res)) return res;
 
       *aCanDrag = isTargetedCorrectly;
     }
   }
 
-  if (NS_FAILED(res)) return res;
-  if (!*aCanDrag) return NS_OK;
-
-  nsCOMPtr<nsIDOMDocument> domdoc;
-  GetDocument(getter_AddRefs(domdoc));
-  *aCanDrag = nsEditorHookUtils::DoAllowDragHook(domdoc, aDragEvent);
-  return NS_OK;
+  return res;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::DoDrag(nsIDOMEvent *aDragEvent)
 {
   nsresult rv;
 
   nsCOMPtr<nsITransferable> trans;
   rv = PutDragDataInTransferable(getter_AddRefs(trans));
@@ -394,37 +382,35 @@ NS_IMETHODIMP nsPlaintextEditor::DoDrag(
 
   /* add the transferable to the array */
   rv = transferableArray->AppendElement(trans);
   if (NS_FAILED(rv)) return rv;
 
   // check our transferable hooks (if any)
   nsCOMPtr<nsIDOMDocument> domdoc;
   GetDocument(getter_AddRefs(domdoc));
-  if (!nsEditorHookUtils::DoDragHook(domdoc, aDragEvent, trans))
-    return NS_OK;
 
   /* invoke drag */
   nsCOMPtr<nsIDOMEventTarget> eventTarget;
   rv = aDragEvent->GetTarget(getter_AddRefs(eventTarget));
   if (NS_FAILED(rv)) return rv;
   nsCOMPtr<nsIDOMNode> domnode = do_QueryInterface(eventTarget);
 
   nsCOMPtr<nsIScriptableRegion> selRegion;
   nsCOMPtr<nsISelection> selection;
   rv = GetSelection(getter_AddRefs(selection));
   if (NS_FAILED(rv)) return rv;
 
   unsigned int flags;
   // in some cases we'll want to cut rather than copy... hmmmmm...
   flags = nsIDragService::DRAGDROP_ACTION_COPY + nsIDragService::DRAGDROP_ACTION_MOVE;
 
-  nsCOMPtr<nsIDOMMouseEvent> mouseEvent(do_QueryInterface(aDragEvent));
+  nsCOMPtr<nsIDOMDragEvent> dragEvent(do_QueryInterface(aDragEvent));
   rv = dragService->InvokeDragSessionWithSelection(selection, transferableArray,
-                                                   flags, mouseEvent);
+                                                   flags, dragEvent, nsnull);
   if (NS_FAILED(rv)) return rv;
 
   aDragEvent->StopPropagation();
 
   return rv;
 }
 
 NS_IMETHODIMP nsPlaintextEditor::Paste(PRInt32 aSelectionType)
--- a/layout/base/nsLayoutUtils.cpp
+++ b/layout/base/nsLayoutUtils.cpp
@@ -621,17 +621,18 @@ nsLayoutUtils::GetDOMEventCoordinatesRel
     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
   return GetEventCoordinatesRelativeTo(event, aFrame);
 }
 
 nsPoint
 nsLayoutUtils::GetEventCoordinatesRelativeTo(const nsEvent* aEvent, nsIFrame* aFrame)
 {
   if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT && 
-                  aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT))
+                  aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
+                  aEvent->eventStructType != NS_DRAG_EVENT))
     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
 
   const nsGUIEvent* GUIEvent = static_cast<const nsGUIEvent*>(aEvent);
   if (!GUIEvent->widget)
     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
 
   // If it is, or is a descendant of, an SVG foreignobject frame,
   // then we need to do extra work
@@ -664,17 +665,18 @@ nsLayoutUtils::GetEventCoordinatesRelati
 }
 
 nsPoint
 nsLayoutUtils::GetEventCoordinatesForNearestView(nsEvent* aEvent,
                                                  nsIFrame* aFrame,
                                                  nsIView** aView)
 {
   if (!aEvent || (aEvent->eventStructType != NS_MOUSE_EVENT && 
-                  aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT))
+                  aEvent->eventStructType != NS_MOUSE_SCROLL_EVENT &&
+                  aEvent->eventStructType != NS_DRAG_EVENT))
     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
 
   nsGUIEvent* GUIEvent = static_cast<nsGUIEvent*>(aEvent);
   if (!GUIEvent->widget)
     return nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE);
 
   nsPoint viewToFrame;
   nsIView* frameView;
--- a/layout/xul/base/src/tree/public/nsITreeBoxObject.idl
+++ b/layout/xul/base/src/tree/public/nsITreeBoxObject.idl
@@ -41,18 +41,19 @@
 
 #include "nsISupports.idl"
 #include "domstubs.idl"
 
 interface nsITreeView;
 interface nsITreeSelection;
 interface nsITreeColumn;
 interface nsITreeColumns;
+interface nsIScriptableRegion;
 
-[scriptable, uuid(a264f607-9d90-469e-b770-1ae7284fde05)]
+[scriptable, uuid(64BA5199-C4F4-4498-BBDC-F8E4C369086C)]
 interface nsITreeBoxObject : nsISupports
 {
   /**
    * Obtain the columns.
    */
   readonly attribute nsITreeColumns columns;
 
   /**
@@ -83,16 +84,21 @@ interface nsITreeBoxObject : nsISupports
   readonly attribute long rowWidth;
 
   /**
    * Get the pixel position of the horizontal scrollbar. 
    */
   readonly attribute long horizontalPosition;
 
   /**
+   * Return the region for the visible parts of the selection, in device pixels.
+   */
+  readonly attribute nsIScriptableRegion selectionRegion;
+
+  /**
    * Get the index of the first visible row.
    */
   long getFirstVisibleRow();
 
   /**
    * Get the index of the last visible row.
    */
   long getLastVisibleRow();
--- a/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBodyFrame.cpp
@@ -638,16 +638,58 @@ nsTreeBodyFrame::GetHorizontalPosition(P
 NS_IMETHODIMP
 nsTreeBodyFrame::GetPageLength(PRInt32 *_retval)
 {
   *_retval = mPageLength;
   return NS_OK;
 }
 
 NS_IMETHODIMP
+nsTreeBodyFrame::GetSelectionRegion(nsIScriptableRegion **aRegion)
+{
+  *aRegion = nsnull;
+
+  nsCOMPtr<nsITreeSelection> selection;
+  mView->GetSelection(getter_AddRefs(selection));
+  NS_ENSURE_TRUE(selection, NS_OK);
+
+  nsCOMPtr<nsIScriptableRegion> region = do_CreateInstance("@mozilla.org/gfx/region;1");
+  NS_ENSURE_TRUE(region, NS_ERROR_FAILURE);
+  region->Init();
+
+  nsRefPtr<nsPresContext> presContext = PresContext();
+  nsRect rect = mRect;
+  rect.ScaleRoundOut(1.0 / presContext->AppUnitsPerCSSPixel());
+
+  nsIFrame* rootFrame = presContext->PresShell()->GetRootFrame();
+  nsPoint origin = GetOffsetTo(rootFrame);
+
+  // iterate through the visible rows and add the selected ones to the
+  // drag region
+  PRInt32 x = nsPresContext::AppUnitsToIntCSSPixels(origin.x);
+  PRInt32 y = nsPresContext::AppUnitsToIntCSSPixels(origin.y);
+  PRInt32 top = y;
+  PRInt32 end = GetLastVisibleRow();
+  PRInt32 rowHeight = nsPresContext::AppUnitsToIntCSSPixels(mRowHeight);
+  for (PRInt32 i = mTopRowIndex; i <= end; i++) {
+    PRBool isSelected;
+    selection->IsSelected(i, &isSelected);
+    if (isSelected)
+      region->UnionRect(x, y, rect.width, rowHeight);
+    y += rowHeight;
+  }
+
+  // clip to the tree boundary in case one row extends past it
+  region->IntersectRect(x, top, rect.width, rect.height);
+
+  NS_ADDREF(*aRegion = region);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
 nsTreeBodyFrame::Invalidate()
 {
   if (mUpdateBatchNest)
     return NS_OK;
 
   nsIFrame::Invalidate(GetOverflowRect(), PR_FALSE);
 
   return NS_OK;
@@ -2565,16 +2607,18 @@ nsTreeBodyFrame::HandleEvent(nsPresConte
              do_GetService("@mozilla.org/widget/dragservice;1");
     dragService->GetCurrentSession(getter_AddRefs(mSlots->mDragSession));
     NS_ASSERTION(mSlots->mDragSession, "can't get drag session");
 
     if (mSlots->mDragSession)
       mSlots->mDragSession->GetDragAction(&mSlots->mDragAction);
     else
       mSlots->mDragAction = 0;
+    mSlots->mDropRow = -1;
+    mSlots->mDropOrient = -1;
   }
   else if (aEvent->message == NS_DRAGDROP_OVER) {
     // The mouse is hovering over this tree. If we determine things are
     // different from the last time, invalidate the drop feedback at the old
     // position, query the view to see if the current location is droppable,
     // and then invalidate the drop feedback at the new location if it is.
     // The mouse may or may not have changed position from the last time
     // we were called, so optimize out a lot of the extra notifications by
@@ -2689,35 +2733,38 @@ nsTreeBodyFrame::HandleEvent(nsPresConte
     PRInt32 parentIndex;
     nsresult rv = mView->GetParentIndex(mSlots->mDropRow, &parentIndex);
     while (NS_SUCCEEDED(rv) && parentIndex >= 0) {
       mSlots->mArray.RemoveElement(parentIndex);
       rv = mView->GetParentIndex(parentIndex, &parentIndex);
     }
 
     mView->Drop(mSlots->mDropRow, mSlots->mDropOrient);
+    mSlots->mDropRow = -1;
+    mSlots->mDropOrient = -1;
+    *aEventStatus = nsEventStatus_eConsumeNoDefault; // already handled the drop
   }
   else if (aEvent->message == NS_DRAGDROP_EXIT) {
     // this event was meant for another frame, so ignore it
     if (!mSlots)
       return NS_OK;
 
     // Clear out all our tracking vars.
 
     if (mSlots->mDropAllowed) {
       mSlots->mDropAllowed = PR_FALSE;
       InvalidateDropFeedback(mSlots->mDropRow, mSlots->mDropOrient);
     }
     else
       mSlots->mDropAllowed = PR_FALSE;
-    mSlots->mDropRow = -1;
-    mSlots->mDropOrient = -1;
     mSlots->mDragSession = nsnull;
     mSlots->mScrollLines = 0;
-
+    // If a drop is occuring, the exit event will fire just before the drop
+    // event, so don't reset mDropRow or mDropOrient as these fields are used
+    // by the drop event.
     if (mSlots->mTimer) {
       mSlots->mTimer->Cancel();
       mSlots->mTimer = nsnull;
     }
 
     if (!mSlots->mArray.IsEmpty()) {
       // Close all spring loaded folders except the drop folder.
       CreateTimer(nsILookAndFeel::eMetric_TreeCloseDelay,
--- a/layout/xul/base/src/tree/src/nsTreeBoxObject.cpp
+++ b/layout/xul/base/src/tree/src/nsTreeBoxObject.cpp
@@ -289,16 +289,25 @@ NS_IMETHODIMP nsTreeBoxObject::GetPageLe
 {
   *aPageLength = 0;
   nsITreeBoxObject* body = GetTreeBody();
   if (body)
     return body->GetPageLength(aPageLength);
   return NS_OK;
 }
 
+NS_IMETHODIMP nsTreeBoxObject::GetSelectionRegion(nsIScriptableRegion **aRegion)
+{
+ *aRegion = nsnull;
+  nsITreeBoxObject* body = GetTreeBody();
+  if (body)
+    return body->GetSelectionRegion(aRegion);
+  return NS_OK;
+}
+
 NS_IMETHODIMP
 nsTreeBoxObject::EnsureRowIsVisible(PRInt32 aRow)
 {
   nsITreeBoxObject* body = GetTreeBody();
   if (body)
     return body->EnsureRowIsVisible(aRow);
   return NS_OK;
 }
--- a/testing/mochitest/tests/SimpleTest/EventUtils.js
+++ b/testing/mochitest/tests/SimpleTest/EventUtils.js
@@ -203,18 +203,19 @@ function synthesizeMouse(aTarget, aOffse
 
   var utils = aWindow.QueryInterface(Components.interfaces.nsIInterfaceRequestor).
                       getInterface(Components.interfaces.nsIDOMWindowUtils);
   if (utils) {
     var button = aEvent.button || 0;
     var clickCount = aEvent.clickCount || 1;
     var modifiers = _parseModifiers(aEvent);
 
-    var left = aTarget.boxObject.x;
-    var top = aTarget.boxObject.y;
+    var rect = aTarget.getBoundingClientRect();
+    var left = rect.left;
+    var top = rect.top;
 
     if (aEvent.type) {
       utils.sendMouseEvent(aEvent.type, left + aOffsetX, top + aOffsetY, button, clickCount, modifiers);
     }
     else {
       utils.sendMouseEvent("mousedown", left + aOffsetX, top + aOffsetY, button, clickCount, modifiers);
       utils.sendMouseEvent("mouseup", left + aOffsetX, top + aOffsetY, button, clickCount, modifiers);
     }
--- a/toolkit/content/nsDragAndDrop.js
+++ b/toolkit/content/nsDragAndDrop.js
@@ -32,16 +32,19 @@
 # use your version of this file under the terms of the MPL, indicate your
 # decision by deleting the provisions above and replace them with the notice
 # and other provisions required by the GPL or the LGPL. If you do not delete
 # the provisions above, a recipient may use your version of this file under
 # the terms of any one of the MPL, the GPL or the LGPL.
 #
 # ***** END LICENSE BLOCK *****
 
+// non-strings need some non-zero value used for their data length
+const kNonStringDataLength = 4;
+
 /** 
  *  nsTransferable - a wrapper for nsITransferable that simplifies
  *                   javascript clipboard and drag&drop. for use in
  *                   these situations you should use the nsClipboard
  *                   and nsDragAndDrop wrappers for more convenience
  **/ 
  
 var nsTransferable = {
@@ -262,27 +265,25 @@ function FlavourData(aData, aLength, aFl
   this.flavour = aFlavour || null;
   
   this._XferID = "FlavourData";
 }
 
 FlavourData.prototype = {
   get data ()
   {
-    if (this.flavour && 
-        this.flavour.dataIIDKey != "nsISupportsString" )
+    if (this.flavour &&
+        this.flavour.dataIIDKey != "nsISupportsString")
       return this.supports.QueryInterface(Components.interfaces[this.flavour.dataIIDKey]); 
-    else {
-      var unicode = this.supports.QueryInterface(Components.interfaces.nsISupportsString);
-      if (unicode) 
-        return unicode.data.substring(0, this.contentLength/2);
+
+    var supports = this.supports;
+    if (supports instanceof Components.interfaces.nsISupportsString)
+      return supports.data.substring(0, this.contentLength/2);
      
-      return this.supports;
-    }
-    return "";
+    return supports;
   }
 }
 
 /** 
  * Create a TransferData object with a single FlavourData entry. Used when 
  * unwrapping data of a specific flavour from the drag service. 
  */
 function FlavourToXfer(aData, aLength, aFlavour) 
@@ -293,17 +294,17 @@ function FlavourToXfer(aData, aLength, a
 var transferUtils = {
 
   retrieveURLFromData: function (aData, flavour)
   {
     switch (flavour) {
       case "text/unicode":
         return aData.replace(/^\s+|\s+$/g, "");
       case "text/x-moz-url":
-        return aData.toString().split("\n")[0];
+        return ((aData instanceof Components.interfaces.nsISupportsString) ? aData.toString() : aData).split("\n")[0];
       case "application/x-moz-file":
         var ioService = Components.classes["@mozilla.org/network/io-service;1"]
                                   .getService(Components.interfaces.nsIIOService);
         var fileHandler = ioService.getProtocolHandler("file")
                                    .QueryInterface(Components.interfaces.nsIFileProtocolHandler);
         return fileHandler.getURLSpecFromFile(aData);
     }
     return null;                                                   
@@ -376,73 +377,43 @@ var nsDragAndDrop = {
         }
       catch (e) 
         {
           return;  // not a draggable item, bail!
         }
 
       if (!transferData.data) return;
       transferData = transferData.data;
-      
-      var transArray = Components.classes["@mozilla.org/supports-array;1"]
-                                 .createInstance(Components.interfaces.nsISupportsArray);
-
-      var region = null;
-      if (aEvent.originalTarget.localName == "treechildren") {
-        // let's build the drag region
-        var tree = aEvent.originalTarget.parentNode;
-        try {
-          region = Components.classes["@mozilla.org/gfx/region;1"].createInstance(Components.interfaces.nsIScriptableRegion);
-          region.init();
-          var obo = tree.treeBoxObject;
-          var bo = obo.treeBody.boxObject;
-          var sel= obo.view.selection;
-
-          var rowX = bo.x;
-          var rowY = bo.y;
-          var rowHeight = obo.rowHeight;
-          var rowWidth = bo.width;
 
-          //add a rectangle for each visible selected row
-          for (var i = obo.getFirstVisibleRow(); i <= obo.getLastVisibleRow(); i ++)
-          {
-            if (sel.isSelected(i))
-              region.unionRect(rowX, rowY, rowWidth, rowHeight);
-            rowY = rowY + rowHeight;
-          }
-      
-          //and finally, clip the result to be sure we don't spill over...
-          region.intersectRect(bo.x, bo.y, bo.width, bo.height);
-        } catch(ex) {
-          dump("Error while building selection region: " + ex + "\n");
-          region = null;
+      var dt = aEvent.dataTransfer;
+      var count = 0;
+      do {
+        var tds = transferData._XferID == "TransferData" 
+                                         ? transferData 
+                                         : transferData.dataList[count]
+        for (var i = 0; i < tds.dataList.length; ++i) 
+        {
+          var currData = tds.dataList[i];
+          var currFlavour = currData.flavour.contentType;
+          var value = currData.supports;
+          if (value instanceof Components.interfaces.nsISupportsString)
+            value = value.toString();
+          dt.mozSetDataAt(currFlavour, value, count);
         }
+
+        count++;
       }
-
-      var count = 0;
-      do 
-        {
-          var trans = nsTransferable.set(transferData._XferID == "TransferData" 
-                                         ? transferData 
-                                         : transferData.dataList[count++]);
-          transArray.AppendElement(trans.QueryInterface(Components.interfaces.nsISupports));
-        }
       while (transferData._XferID == "TransferDataSet" && 
              count < transferData.dataList.length);
-      
-      try {
-        this.mDragService.invokeDragSessionWithImage(aEvent.target, transArray,
-                                                     region, dragAction.action,
-                                                     null, 0, 0, aEvent);
-      }
-      catch(ex) {
-        // this could be because the user pressed escape to
-        // cancel the drag. even if it's not, there's not much
-        // we can do, so be silent.
-      }
+
+      dt.effectAllowed = "all";
+      // a drag targeted at a tree should instead use the treechildren so that
+      // the current selection is used as the drag feedback
+      dt.addElement(aEvent.originalTarget.localName == "treechildren" ?
+                    aEvent.originalTarget : aEvent.target);
       aEvent.stopPropagation();
     },
 
   /** 
    * void dragOver (DOMEvent aEvent, Object aDragDropObserver) ;
    *
    * called when a drag passes over this element
    *
@@ -462,16 +433,17 @@ var nsDragAndDrop = {
       for (var flavour in flavourSet.flavourTable)
         {
           if (this.mDragSession.isDataFlavorSupported(flavour))
             {
               aDragDropObserver.onDragOver(aEvent, 
                                            flavourSet.flavourTable[flavour], 
                                            this.mDragSession);
               aEvent.stopPropagation();
+              aEvent.preventDefault();
               break;
             }
         }
     },
 
   mDragSession: null,
 
   /** 
@@ -486,24 +458,47 @@ var nsDragAndDrop = {
    *        the way in which the element responds to drag events.
    **/
   drop: function (aEvent, aDragDropObserver)
     {
       if (!("onDrop" in aDragDropObserver))
         return;
       if (!this.checkCanDrop(aEvent, aDragDropObserver))
         return;  
-      if (this.mDragSession.canDrop) {
-        var flavourSet = aDragDropObserver.getSupportedFlavours();
-        var transferData = nsTransferable.get(flavourSet, this.getDragData, true);
-        // hand over to the client to respond to dropped data
-        var multiple = "canHandleMultipleItems" in aDragDropObserver && aDragDropObserver.canHandleMultipleItems;
-        var dropData = multiple ? transferData : transferData.first.first;
-        aDragDropObserver.onDrop(aEvent, dropData, this.mDragSession);
+
+      var flavourSet = aDragDropObserver.getSupportedFlavours();
+
+      var dt = aEvent.dataTransfer;
+      var dataArray = [];
+      var count = dt.mozItemCount;
+      for (var i = 0; i < count; ++i) {
+        var types = dt.mozTypesAt(i);
+        for (var j = 0; j < types.length; ++j) {
+          var type = types[j];
+          // dataTransfer uses text/plain but older code used text/unicode, so
+          // switch this for compatibility
+          if (type == "text/plain")
+            type = "text/unicode";
+          if (type in flavourSet.flavourTable) {
+            var data = dt.mozGetDataAt(type, i);
+            if (data) {
+              var length = (typeof data == "string") ? data.length : kNonStringDataLength;
+              dataArray[i] = FlavourToXfer(data, length, flavourSet.flavourTable[type]);
+              break;
+            }
+          }
+        }
       }
+
+      var transferData = new TransferDataSet(dataArray)
+
+      // hand over to the client to respond to dropped data
+      var multiple = "canHandleMultipleItems" in aDragDropObserver && aDragDropObserver.canHandleMultipleItems;
+      var dropData = multiple ? transferData : transferData.first.first;
+      aDragDropObserver.onDrop(aEvent, dropData, this.mDragSession);
       aEvent.stopPropagation();
     },
 
   /** 
    * void dragExit (DOMEvent aEvent, Object aDragDropObserver) ;
    *
    * called when a drag leaves this element
    *
@@ -534,41 +529,16 @@ var nsDragAndDrop = {
    **/
   dragEnter: function (aEvent, aDragDropObserver)
     {
       if (!this.checkCanDrop(aEvent, aDragDropObserver))
         return;
       if ("onDragEnter" in aDragDropObserver)
         aDragDropObserver.onDragEnter(aEvent, this.mDragSession);
     },  
-    
-  /** 
-   * nsISupportsArray getDragData (Object aFlavourList)
-   *
-   * Creates a nsISupportsArray of all droppable items for the given
-   * set of supported flavours.
-   * 
-   * @param FlavourSet aFlavourSet
-   *        formatted flavour list.
-   **/  
-  getDragData: function (aFlavourSet)
-    {
-      var supportsArray = Components.classes["@mozilla.org/supports-array;1"]
-                                    .createInstance(Components.interfaces.nsISupportsArray);
-
-      for (var i = 0; i < nsDragAndDrop.mDragSession.numDropItems; ++i)
-        {
-          var trans = nsTransferable.createTransferable();
-          for (var j = 0; j < aFlavourSet.flavours.length; ++j)
-            trans.addDataFlavor(aFlavourSet.flavours[j].contentType);
-          nsDragAndDrop.mDragSession.getData(trans, i);
-          supportsArray.AppendElement(trans);
-        }
-      return supportsArray;
-    },
 
   /** 
    * Boolean checkCanDrop (DOMEvent aEvent, Object aDragDropObserver) ;
    *
    * Sets the canDrop attribute for the drag session.
    * returns false if there is no current drag session.
    *
    * @param DOMEvent aEvent
--- a/widget/public/nsGUIEvent.h
+++ b/widget/public/nsGUIEvent.h
@@ -49,16 +49,17 @@
 // which conflicts with something that Windows defines somewhere.
 // So, undefine it:
 #ifdef WIN32
 #undef ERROR
 #endif
 #include "nsCOMPtr.h"
 #include "nsIAtom.h"
 #include "nsIDOMKeyEvent.h"
+#include "nsIDOMDataTransfer.h"
 #include "nsWeakPtr.h"
 #include "nsIWidget.h"
 #include "nsTArray.h"
 #include "nsTraceRefcnt.h"
 
 class nsIRenderingContext;
 class nsIRegion;
 class nsIMenuItem;
@@ -101,16 +102,17 @@ class nsHashKey;
 #define NS_SVG_EVENT                      30
 #define NS_SVGZOOM_EVENT                  31
 #endif // MOZ_SVG
 #define NS_XUL_COMMAND_EVENT              32
 #define NS_QUERY_CONTENT_EVENT            33
 #ifdef MOZ_MEDIA
 #define NS_MEDIA_EVENT                    34
 #endif // MOZ_MEDIA
+#define NS_DRAG_EVENT                     35
 
 // These flags are sort of a mess. They're sort of shared between event
 // listener flags and event flags, but only some of them. You've been
 // warned!
 #define NS_EVENT_FLAG_NONE                0x0000
 #define NS_EVENT_FLAG_TRUSTED             0x0001
 #define NS_EVENT_FLAG_BUBBLE              0x0002
 #define NS_EVENT_FLAG_CAPTURE             0x0004
@@ -243,27 +245,29 @@ class nsHashKey;
 #define NS_FORM_SELECTED                (NS_FORM_EVENT_START + 3)
 #define NS_FORM_INPUT                   (NS_FORM_EVENT_START + 4)
 
 //Need separate focus/blur notifications for non-native widgets
 #define NS_FOCUS_EVENT_START            1300
 #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)
@@ -673,16 +677,30 @@ public:
 class nsMouseEvent : public nsMouseEvent_base
 {
 public:
   enum buttonType  { eLeftButton = 0, eMiddleButton = 1, eRightButton = 2 };
   enum reasonType  { eReal, eSynthesized };
   enum contextType { eNormal, eContextMenuKey };
   enum exitType    { eChild, eTopLevel };
 
+protected:
+  nsMouseEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w,
+               PRUint8 structType, reasonType aReason)
+    : nsMouseEvent_base(isTrusted, msg, w, structType),
+      acceptActivation(PR_FALSE), reason(aReason), context(eNormal),
+      exit(eChild), clickCount(0)
+  {
+    if (msg == NS_MOUSE_MOVE) {
+      flags |= NS_EVENT_FLAG_CANT_CANCEL;
+    }
+  }
+
+public:
+
   nsMouseEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w,
                reasonType aReason, contextType aContext = eNormal)
     : nsMouseEvent_base(isTrusted, msg, w, NS_MOUSE_EVENT),
       acceptActivation(PR_FALSE), reason(aReason), context(aContext),
       exit(eChild), clickCount(0)
   {
     if (msg == NS_MOUSE_MOVE) {
       flags |= NS_EVENT_FLAG_CANT_CANCEL;
@@ -706,16 +724,36 @@ public:
   contextType  context : 4;
   exitType     exit;
 
   /// The number of mouse clicks
   PRUint32     clickCount;
 };
 
 /**
+ * Drag event
+ */
+
+class nsDragEvent : public nsMouseEvent
+{
+public:
+  nsDragEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
+    : nsMouseEvent(isTrusted, msg, w, NS_DRAG_EVENT, eReal)
+  {
+    if (msg == NS_DRAGDROP_EXIT_SYNTH ||
+        msg == NS_DRAGDROP_LEAVE_SYNTH ||
+        msg == NS_DRAGDROP_END) {
+      flags |= NS_EVENT_FLAG_CANT_CANCEL;
+    }
+  }
+
+  nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
+};
+
+/**
  * Accessible event
  */
 
 class nsAccessibleEvent : public nsInputEvent
 {
 public:
   nsAccessibleEvent(PRBool isTrusted, PRUint32 msg, nsIWidget *w)
     : nsInputEvent(isTrusted, msg, w, NS_ACCESSIBLE_EVENT),
@@ -1054,20 +1092,23 @@ enum nsDragDropEventStatus {
        ((evnt)->eventStructType == NS_MOUSE_EVENT && \
         (evnt)->message == NS_CONTEXTMENU && \
         static_cast<nsMouseEvent*>((evnt))->context == nsMouseEvent::eContextMenuKey)
 
 #define NS_IS_DRAG_EVENT(evnt) \
        (((evnt)->message == NS_DRAGDROP_ENTER) || \
         ((evnt)->message == NS_DRAGDROP_OVER) || \
         ((evnt)->message == NS_DRAGDROP_EXIT) || \
-        ((evnt)->message == NS_DRAGDROP_DROP) || \
+        ((evnt)->message == NS_DRAGDROP_DRAGDROP) || \
         ((evnt)->message == NS_DRAGDROP_GESTURE) || \
-        ((evnt)->message == NS_DRAGDROP_OVER_SYNTH) || \
-        ((evnt)->message == NS_DRAGDROP_EXIT_SYNTH))
+        ((evnt)->message == NS_DRAGDROP_DRAG) || \
+        ((evnt)->message == NS_DRAGDROP_END) || \
+        ((evnt)->message == NS_DRAGDROP_START) || \
+        ((evnt)->message == NS_DRAGDROP_DROP) || \
+        ((evnt)->message == NS_DRAGDROP_LEAVE_SYNTH))
 
 #define NS_IS_KEY_EVENT(evnt) \
        (((evnt)->message == NS_KEY_DOWN) ||  \
         ((evnt)->message == NS_KEY_PRESS) || \
         ((evnt)->message == NS_KEY_UP))
 
 #define NS_IS_IME_EVENT(evnt) \
        (((evnt)->message == NS_TEXT_TEXT) ||  \
--- a/widget/public/nsIDragService.idl
+++ b/widget/public/nsIDragService.idl
@@ -39,26 +39,28 @@
 
 #include "nsISupports.idl"
 #include "nsISupportsArray.idl"
 #include "nsIDragSession.idl"
 #include "nsIScriptableRegion.idl"
 
 
 interface nsIDOMNode;
-interface nsIDOMMouseEvent;
+interface nsIDOMDragEvent;
+interface nsIDOMDataTransfer;
 interface nsISelection;
 
-[scriptable, uuid(034c44a4-604b-44a2-9205-676d5135f359)]
+[scriptable, uuid(82B58ADA-F490-4C3D-B737-1057C4F1D052)]
 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;
+  const long DRAGDROP_ACTION_UNINITIALIZED = 64;
 
   /**
     * Starts a modal drag session with an array of transaferables 
     *
     * @param  aTransferables - an array of transferables to be dragged
     * @param  aRegion - a region containing rectangles for cursor feedback, 
     *            in window coordinates.
     * @param  aActionType - specified which of copy/move/link are allowed
@@ -93,27 +95,29 @@ interface nsIDragService : nsISupports
    */
   void invokeDragSessionWithImage(in nsIDOMNode aDOMNode,
                                   in nsISupportsArray aTransferableArray,
                                   in nsIScriptableRegion aRegion,
                                   in unsigned long aActionType,
                                   in nsIDOMNode aImage,
                                   in long aImageX,
                                   in long aImageY,
-                                  in nsIDOMMouseEvent aDragEvent);
+                                  in nsIDOMDragEvent aDragEvent,
+                                  in nsIDOMDataTransfer aDataTransfer);
 
   /**
    * Start a modal drag session using the selection as the drag image.
    * The aDragEvent must be supplied as the current screen coordinates of the
    * event are needed to calculate the image location.
    */
   void invokeDragSessionWithSelection(in nsISelection aSelection,
                                       in nsISupportsArray aTransferableArray,
                                       in unsigned long aActionType,
-                                      in nsIDOMMouseEvent aDragEvent);
+                                      in nsIDOMDragEvent aDragEvent,
+                                      in nsIDOMDataTransfer aDataTransfer);
 
   /**
     * Returns the current Drag Session  
     */
   nsIDragSession getCurrentSession ( ) ;
 
   /**
     * Tells the Drag Service to start a drag session. This is called when
--- a/widget/public/nsIDragSession.idl
+++ b/widget/public/nsIDragSession.idl
@@ -46,18 +46,19 @@
 #include "nsSize.h"
 %}
 
 native nsSize (nsSize);
 
 
 interface nsIDOMDocument;
 interface nsIDOMNode;
+interface nsIDOMDataTransfer;
 
-[scriptable, uuid(CBA22C53-FCCE-11d2-96D4-0060B0FB9956)]
+[scriptable, uuid(15860D52-FE2C-4DDD-AC50-9C23E24916C4)]
 interface nsIDragSession : nsISupports
 {
   /**
     * Set the current state of the drag, whether it can be dropped or not.
     * usually the target "frame" sets this so the native system can render the correct feedback
     */
   attribute boolean canDrop;
   
@@ -86,16 +87,21 @@ interface nsIDragSession : nsISupports
 
   /**
     * The dom node that was originally dragged to start the session, which will be null if the
     * drag originated outside the application.
     */
   readonly attribute nsIDOMNode sourceNode;
 
   /**
+   * The data transfer object for the current drag.
+   */
+  attribute nsIDOMDataTransfer dataTransfer;
+
+  /**
     * Get data from a Drag&Drop. Can be called while the drag is in process
     * or after the drop has completed.  
     *
     * @param  aTransferable the transferable for the data to be put into
     * @param  aItemIndex which of multiple drag items, zero-based
     */
   void getData ( in nsITransferable aTransferable, in unsigned long aItemIndex ) ;
 
--- a/widget/src/beos/nsWindow.cpp
+++ b/widget/src/beos/nsWindow.cpp
@@ -1932,17 +1932,17 @@ bool nsWindow::CallMethod(MethodInfo *in
 		    	               ((int32 *)info->args)[3]);
 		}
 		break;
 
 	case nsSwitchToUIThread::ONDROP :
 		{
 			NS_ASSERTION(info->nArgs == 4, "Wrong number of arguments to CallMethod");
 
-			nsMouseEvent event(PR_TRUE, (int32)  info->args[0], this, nsMouseEvent::eReal);
+			nsDragEvent event(PR_TRUE, (int32)  info->args[0], this);
 			nsPoint point(((int32 *)info->args)[1], ((int32 *)info->args)[2]);
 			InitEvent (event, &point);
 			uint32 mod = (uint32) info->args[3];
 			event.isShift   = mod & B_SHIFT_KEY;
 			event.isControl = mod & B_CONTROL_KEY;
 			event.isAlt     = mod & B_COMMAND_KEY;
 			event.isMeta     = mod & B_OPTION_KEY;
 
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -5748,48 +5748,55 @@ static BOOL keyUpAlreadySentKeyDown = NO
       // 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 assumption 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)
+      if (!NS_SUCCEEDED(dragSession->GetCanDrop(&canDrop)) || !canDrop) {
+        nsCOMPtr<nsIDOMNode> sourceNode;
+        dragSession->GetSourceNode(getter_AddRefs(sourceNode));
+        if (!sourceNode) {
+          mDragService->EndDragSession(PR_FALSE);
+        }
         return NO;
+      }
     }
     
     unsigned int modifierFlags = [[NSApp currentEvent] modifierFlags];
     PRUint32 action = nsIDragService::DRAGDROP_ACTION_MOVE;
     // force copy = option, alias = cmd-option, default is move
     if (modifierFlags & NSAlternateKeyMask) {
       if (modifierFlags & NSCommandKeyMask)
         action = nsIDragService::DRAGDROP_ACTION_LINK;
       else
         action = nsIDragService::DRAGDROP_ACTION_COPY;
     }
     dragSession->SetDragAction(action);
   }
 
   // set up gecko event
-  nsMouseEvent geckoEvent(PR_TRUE, aMessage, nsnull, nsMouseEvent::eReal);
+  nsDragEvent geckoEvent(PR_TRUE, aMessage, nsnull);
   [self convertGenericCocoaEvent:nil toGeckoEvent:&geckoEvent];
 
   // Use our own coordinates in the gecko event.
   // Convert event from gecko global coords to gecko view coords.
   NSPoint localPoint = [self convertPoint:[aSender draggingLocation] fromView:nil];
   geckoEvent.refPoint.x = static_cast<nscoord>(localPoint.x);
   geckoEvent.refPoint.y = static_cast<nscoord>(localPoint.y);
 
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
   mGeckoChild->DispatchWindowEvent(geckoEvent);
   if (!mGeckoChild)
     return YES;
 
-  if (aMessage == NS_DRAGDROP_EXIT && dragSession) {
+  if ((aMessage == NS_DRAGDROP_EXIT || aMessage == NS_DRAGDROP_DROP) &&
+      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(PR_FALSE);
--- a/widget/src/cocoa/nsDragService.mm
+++ b/widget/src/cocoa/nsDragService.mm
@@ -566,13 +566,14 @@ nsDragService::EndDragSession(PRBool aDo
     [mNativeDragView release];
     mNativeDragView = nil;
   }
   if (mNativeDragEvent) {
     [mNativeDragEvent release];
     mNativeDragEvent = nil;
   }
 
+  nsresult rv = nsBaseDragService::EndDragSession(aDoneDrag);
   mDataItems = nsnull;
-  return nsBaseDragService::EndDragSession(aDoneDrag);
+  return rv;
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
 }
--- a/widget/src/gtk2/nsWindow.cpp
+++ b/widget/src/gtk2/nsWindow.cpp
@@ -2915,18 +2915,17 @@ nsWindow::OnDragMotionEvent(GtkWidget *a
     // 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);
+    nsDragEvent event(PR_TRUE, NS_DRAGDROP_OVER, innerMostWidget);
 
     InitDragEvent(event);
 
     // now that we have initialized the event update our drag status
     UpdateDragStatus(event, aDragContext, dragService);
 
     event.refPoint.x = retx;
     event.refPoint.y = rety;
@@ -3024,18 +3023,17 @@ nsWindow::OnDragDropEvent(GtkWidget *aWi
 
     // set the last window to this
     mLastDragMotionWindow = innerMostWidget;
 
     // What we do here is dispatch a new drag motion event to
     // re-validate the drag target and then we do the drop.  The events
     // look the same except for the type.
 
-    nsMouseEvent event(PR_TRUE, NS_DRAGDROP_OVER, innerMostWidget,
-                       nsMouseEvent::eReal);
+    nsDragEvent event(PR_TRUE, NS_DRAGDROP_OVER, innerMostWidget);
 
     InitDragEvent(event);
 
     // now that we have initialized the event update our drag status
     UpdateDragStatus(event, aDragContext, dragService);
 
     event.refPoint.x = retx;
     event.refPoint.y = rety;
@@ -3096,17 +3094,17 @@ nsWindow::OnDragDataReceivedEvent(GtkWid
                                        aSelectionData, aInfo, aTime);
 }
 
 void
 nsWindow::OnDragLeave(void)
 {
     LOG(("nsWindow::OnDragLeave(%p)\n", this));
 
-    nsMouseEvent event(PR_TRUE, NS_DRAGDROP_EXIT, this, nsMouseEvent::eReal);
+    nsDragEvent event(PR_TRUE, NS_DRAGDROP_EXIT, this);
 
     nsEventStatus status;
     DispatchEvent(&event, status);
 
     nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
 
     if (dragService) {
         nsCOMPtr<nsIDragSession> currentDragSession;
@@ -3136,17 +3134,17 @@ nsWindow::OnDragEnter(nscoord aX, nscoor
 
     nsCOMPtr<nsIDragService> dragService = do_GetService(kCDragServiceCID);
 
     if (dragService) {
         // Make sure that the drag service knows we're now dragging.
         dragService->StartDragSession();
     }
 
-    nsMouseEvent event(PR_TRUE, NS_DRAGDROP_ENTER, this, nsMouseEvent::eReal);
+    nsDragEvent event(PR_TRUE, NS_DRAGDROP_ENTER, this);
 
     event.refPoint.x = aX;
     event.refPoint.y = aY;
 
     nsEventStatus status;
     DispatchEvent(&event, status);
 }
 
@@ -5123,34 +5121,34 @@ theme_changed_cb (GtkSettings *settings,
     nsRefPtr<nsWindow> window = data;
     window->ThemeChanged();
 }
 
 //////////////////////////////////////////////////////////////////////
 // These are all of our drag and drop operations
 
 void
-nsWindow::InitDragEvent(nsMouseEvent &aEvent)
+nsWindow::InitDragEvent(nsDragEvent &aEvent)
 {
     // set the keyboard modifiers
     gint x, y;
     GdkModifierType state = (GdkModifierType)0;
     gdk_window_get_pointer(NULL, &x, &y, &state);
     aEvent.isShift = (state & GDK_SHIFT_MASK) ? PR_TRUE : PR_FALSE;
     aEvent.isControl = (state & GDK_CONTROL_MASK) ? PR_TRUE : PR_FALSE;
     aEvent.isAlt = (state & GDK_MOD1_MASK) ? PR_TRUE : PR_FALSE;
     aEvent.isMeta = PR_FALSE; // GTK+ doesn't support the meta key
 }
 
 // This will update the drag action based on the information in the
 // drag context.  Gtk gets this from a combination of the key settings
 // and what the source is offering.
 
 void
-nsWindow::UpdateDragStatus(nsMouseEvent   &aEvent,
+nsWindow::UpdateDragStatus(nsDragEvent   &aEvent,
                            GdkDragContext *aDragContext,
                            nsIDragService *aDragService)
 {
     // default is to do nothing
     int action = nsIDragService::DRAGDROP_ACTION_NONE;
 
     // set the default just in case nothing matches below
     if (aDragContext->actions & GDK_ACTION_DEFAULT)
--- a/widget/src/gtk2/nsWindow.h
+++ b/widget/src/gtk2/nsWindow.h
@@ -437,18 +437,18 @@ private:
     // This bitmap tracks which pixels are transparent. We don't support
     // full translucency at this time; each pixel is either fully opaque
     // or fully transparent.
     gchar*       mTransparencyBitmap;
  
     // all of our DND stuff
     // this is the last window that had a drag event happen on it.
     static nsWindow    *mLastDragMotionWindow;
-    void   InitDragEvent         (nsMouseEvent &aEvent);
-    void   UpdateDragStatus      (nsMouseEvent &aEvent,
+    void   InitDragEvent         (nsDragEvent &aEvent);
+    void   UpdateDragStatus      (nsDragEvent &aEvent,
                                   GdkDragContext *aDragContext,
                                   nsIDragService *aDragService);
 
     // this is everything we need to be able to fire motion events
     // repeatedly
     GtkWidget         *mDragMotionWidget;
     GdkDragContext    *mDragMotionContext;
     gint               mDragMotionX;
--- a/widget/src/os2/nsWindow.cpp
+++ b/widget/src/os2/nsWindow.cpp
@@ -522,17 +522,17 @@ PRBool nsWindow::DispatchCommandEvent(PR
 //-------------------------------------------------------------------------
 //
 // Dispatch DragDrop (target) event
 //
 //-------------------------------------------------------------------------
 
 PRBool nsWindow::DispatchDragDropEvent(PRUint32 aMsg)
 {
-  nsMouseEvent event(PR_TRUE, aMsg, this, nsMouseEvent::eReal);
+  nsDragEvent event(PR_TRUE, aMsg, this);
   InitEvent(event);
 
   event.isShift   = WinIsKeyDown(VK_SHIFT);
   event.isControl = WinIsKeyDown(VK_CTRL);
   event.isAlt     = WinIsKeyDown(VK_ALT) || WinIsKeyDown(VK_ALTGRAF);
   event.isMeta    = PR_FALSE;
 
   return DispatchWindowEvent(&event);
--- a/widget/src/photon/nsWidget.cpp
+++ b/widget/src/photon/nsWidget.cpp
@@ -1237,17 +1237,17 @@ void nsWidget::ProcessDrag( PhEvent_t *e
 	event->subtype = old_subtype;
 
 	// Clear the cached value
 	currSession->SetCanDrop(PR_FALSE);
 	}
 
 void nsWidget::DispatchDragDropEvent( PhEvent_t *phevent, PRUint32 aEventType, PhPoint_t *pos ) {
   nsEventStatus status;
-  nsMouseEvent event(PR_TRUE, 0, nsnull, nsMouseEvent::eReal);
+  nsDragEvent event(PR_TRUE, 0, nsnull);
 
   InitEvent( event, aEventType );
 
   event.refPoint.x = pos->x;
   event.refPoint.y = pos->y;
 
 	PhDndEvent_t *dnd = ( PhDndEvent_t * ) PhGetData( phevent );
   event.isControl = ( dnd->key_mods & Pk_KM_Ctrl ) ? PR_TRUE : PR_FALSE;
--- a/widget/src/windows/nsNativeDragTarget.cpp
+++ b/widget/src/windows/nsNativeDragTarget.cpp
@@ -188,17 +188,17 @@ IsKeyDown(char key)
 }
 
 
 //-----------------------------------------------------
 void
 nsNativeDragTarget::DispatchDragDropEvent(PRUint32 aEventType, POINTL aPT)
 {
   nsEventStatus status;
-  nsMouseEvent event(PR_TRUE, aEventType, mWindow, nsMouseEvent::eReal);
+  nsDragEvent event(PR_TRUE, aEventType, mWindow);
 
   nsWindow * win = static_cast<nsWindow *>(mWindow);
   win->InitEvent(event);
   POINT cpos;
 
   cpos.x = aPT.x;
   cpos.y = aPT.y;
 
--- a/widget/src/xpwidgets/nsBaseDragService.cpp
+++ b/widget/src/xpwidgets/nsBaseDragService.cpp
@@ -50,20 +50,21 @@
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIFrame.h"
 #include "nsIDocument.h"
 #include "nsIContent.h"
 #include "nsIPresShell.h"
 #include "nsIViewManager.h"
 #include "nsIScrollableView.h"
 #include "nsIDOMNode.h"
-#include "nsIDOMMouseEvent.h"
+#include "nsIDOMDragEvent.h"
 #include "nsISelection.h"
 #include "nsISelectionPrivate.h"
 #include "nsPresContext.h"
+#include "nsIDOMDataTransfer.h"
 #include "nsIEventStateManager.h"
 #include "nsICanvasElement.h"
 #include "nsIImage.h"
 #include "nsIImageLoadingContent.h"
 #include "gfxIImageFrame.h"
 #include "imgIContainer.h"
 #include "imgIRequest.h"
 #include "nsIViewObserver.h"
@@ -190,16 +191,31 @@ nsBaseDragService::GetData(nsITransferab
 //-------------------------------------------------------------------------
 NS_IMETHODIMP
 nsBaseDragService::IsDataFlavorSupported(const char *aDataFlavor,
                                          PRBool *_retval)
 {
   return NS_ERROR_FAILURE;
 }
 
+NS_IMETHODIMP
+nsBaseDragService::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
+{
+  *aDataTransfer = mDataTransfer;
+  NS_IF_ADDREF(*aDataTransfer);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+nsBaseDragService::SetDataTransfer(nsIDOMDataTransfer* aDataTransfer)
+{
+  mDataTransfer = aDataTransfer;
+  return NS_OK;
+}
+
 //-------------------------------------------------------------------------
 NS_IMETHODIMP
 nsBaseDragService::InvokeDragSession(nsIDOMNode *aDOMNode,
                                      nsISupportsArray* aTransferableArray,
                                      nsIScriptableRegion* aDragRgn,
                                      PRUint32 aActionType)
 {
   NS_ENSURE_TRUE(aDOMNode, NS_ERROR_INVALID_ARG);
@@ -234,53 +250,60 @@ nsBaseDragService::InvokeDragSession(nsI
 
 NS_IMETHODIMP
 nsBaseDragService::InvokeDragSessionWithImage(nsIDOMNode* aDOMNode,
                                               nsISupportsArray* aTransferableArray,
                                               nsIScriptableRegion* aRegion,
                                               PRUint32 aActionType,
                                               nsIDOMNode* aImage,
                                               PRInt32 aImageX, PRInt32 aImageY,
-                                              nsIDOMMouseEvent* aDragEvent)
+                                              nsIDOMDragEvent* aDragEvent,
+                                              nsIDOMDataTransfer* aDataTransfer)
 {
   NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
+  NS_ENSURE_TRUE(aDataTransfer, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
 
+  mDataTransfer = aDataTransfer;
   mSelection = nsnull;
   mHasImage = PR_TRUE;
   mImage = aImage;
   mImageX = aImageX;
   mImageY = aImageY;
 
   aDragEvent->GetScreenX(&mScreenX);
   aDragEvent->GetScreenY(&mScreenY);
 
   return InvokeDragSession(aDOMNode, aTransferableArray, aRegion, aActionType);
 }
 
 NS_IMETHODIMP
 nsBaseDragService::InvokeDragSessionWithSelection(nsISelection* aSelection,
                                                   nsISupportsArray* aTransferableArray,
                                                   PRUint32 aActionType,
-                                                  nsIDOMMouseEvent* aDragEvent)
+                                                  nsIDOMDragEvent* aDragEvent,
+                                                  nsIDOMDataTransfer* aDataTransfer)
 {
   NS_ENSURE_TRUE(aSelection, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(aDragEvent, NS_ERROR_NULL_POINTER);
   NS_ENSURE_TRUE(mSuppressLevel == 0, NS_ERROR_FAILURE);
 
+  mDataTransfer = aDataTransfer;
   mSelection = aSelection;
   mHasImage = PR_TRUE;
   mImage = nsnull;
   mImageX = 0;
   mImageY = 0;
 
   aDragEvent->GetScreenX(&mScreenX);
   aDragEvent->GetScreenY(&mScreenY);
 
   // just get the focused node from the selection
+  // XXXndeakin this should actually be the deepest node that contains both
+  // endpoints of the selection
   nsCOMPtr<nsIDOMNode> node;
   aSelection->GetFocusNode(getter_AddRefs(node));
 
   return InvokeDragSession(node, aTransferableArray, nsnull, aActionType);
 }
 
 //-------------------------------------------------------------------------
 NS_IMETHODIMP
@@ -324,16 +347,17 @@ nsBaseDragService::EndDragSession(PRBool
     FireDragEventAtSource(NS_DRAGDROP_END);
 
   mDoingDrag = PR_FALSE;
 
   // release the source we've been holding on to.
   mSourceDocument = nsnull;
   mSourceNode = nsnull;
   mSelection = nsnull;
+  mDataTransfer = nsnull;
   mHasImage = PR_FALSE;
   mImage = nsnull;
   mImageX = 0;
   mImageY = 0;
   mScreenX = -1;
   mScreenY = -1;
 
   return NS_OK;
@@ -343,17 +367,17 @@ NS_IMETHODIMP
 nsBaseDragService::FireDragEventAtSource(PRUint32 aMsg)
 {
   if (mSourceNode && !mSuppressLevel) {
     nsCOMPtr<nsIDocument> doc = do_QueryInterface(mSourceDocument);
     if (doc) {
       nsCOMPtr<nsIPresShell> presShell = doc->GetPrimaryShell();
       if (presShell) {
         nsEventStatus status = nsEventStatus_eIgnore;
-        nsMouseEvent event(PR_TRUE, aMsg, nsnull, nsMouseEvent::eReal);
+        nsDragEvent event(PR_TRUE, aMsg, nsnull);
 
         nsCOMPtr<nsIContent> content = do_QueryInterface(mSourceNode);
         return presShell->HandleDOMEventWithTarget(content, &event, &status);
       }
     }
   }
 
   return NS_OK;
--- a/widget/src/xpwidgets/nsBaseDragService.h
+++ b/widget/src/xpwidgets/nsBaseDragService.h
@@ -40,16 +40,17 @@
 
 #include "nsIDragService.h"
 #include "nsIDragSession.h"
 #include "nsITransferable.h"
 #include "nsISupportsArray.h"
 #include "nsIDOMDocument.h"
 #include "nsCOMPtr.h"
 #include "nsIRenderingContext.h"
+#include "nsIDOMDataTransfer.h"
 
 #include "gfxImageSurface.h"
 
 // translucency level for drag images
 #define DRAG_TRANSLUCENCY 0.65
 
 class nsIDOMNode;
 class nsIFrame;
@@ -126,16 +127,17 @@ protected:
   // true if mImage should be used to set a drag image
   PRPackedBool mHasImage;
 
   PRUint32 mDragAction;
   nsSize mTargetSize;
   nsCOMPtr<nsIDOMNode> mSourceNode;
   nsCOMPtr<nsIDOMDocument> mSourceDocument;       // the document at the drag source. will be null
                                                   //  if it came from outside the app.
+  nsCOMPtr<nsIDOMDataTransfer> mDataTransfer;
 
   // used to determine the image to appear on the cursor while dragging
   nsCOMPtr<nsIDOMNode> mImage;
   // offset of cursor within the image 
   PRInt32 mImageX;
   PRInt32 mImageY;
 
   // set if a selection is being dragged
--- a/widget/src/xpwidgets/nsPrimitiveHelpers.cpp
+++ b/widget/src/xpwidgets/nsPrimitiveHelpers.cpp
@@ -129,16 +129,18 @@ nsPrimitiveHelpers :: CreatePrimitiveFor
 //
 void
 nsPrimitiveHelpers :: CreateDataFromPrimitive ( const char* aFlavor, nsISupports* aPrimitive, 
                                                    void** aDataBuff, PRUint32 aDataLen )
 {
   if ( !aDataBuff )
     return;
 
+  *aDataBuff = nsnull;
+
   if ( strcmp(aFlavor,kTextMime) == 0 ) {
     nsCOMPtr<nsISupportsCString> plainText ( do_QueryInterface(aPrimitive) );
     if ( plainText ) {
       nsCAutoString data;
       plainText->GetData ( data );
       *aDataBuff = ToNewCString(data);
     }
   }
--- a/xpfe/global/jar.mn
+++ b/xpfe/global/jar.mn
@@ -14,18 +14,18 @@ toolkit.jar:
         content/global/mozilla.xhtml                (resources/content/mozilla.xhtml) 
         content/global/charsetOverlay.xul           (resources/content/charsetOverlay.xul) 
         content/global/charsetOverlay.js            (resources/content/charsetOverlay.js) 
         content/global/selectDialog.js              (resources/content/selectDialog.js) 
         content/global/selectDialog.xul             (resources/content/selectDialog.xul) 
         content/global/nsTreeController.js          (resources/content/nsTreeController.js) 
         content/global/nsTreeSorting.js             (resources/content/nsTreeSorting.js) 
         content/global/nsClipboard.js               (resources/content/nsClipboard.js) 
-        content/global/nsDragAndDrop.js             (resources/content/nsDragAndDrop.js) 
-        content/global/nsTransferable.js            (resources/content/nsDragAndDrop.js) 
+*       content/global/nsDragAndDrop.js             (/toolkit/content/nsDragAndDrop.js) 
+*       content/global/nsTransferable.js            (/toolkit/content/nsDragAndDrop.js) 
         content/global/nsUserSettings.js            (resources/content/nsUserSettings.js) 
         content/global/xul.css                      (resources/content/xul.css) 
 *       content/global/inlineSpellCheckUI.js        (/toolkit/content/inlineSpellCheckUI.js)
         content/global/plugins.html                 (/toolkit/content/plugins.html)
         content/global/plugins.css                  (/toolkit/content/plugins.css)
         content/global/printdialog.xul              (resources/content/printdialog.xul)
         content/global/printdialog.js               (resources/content/printdialog.js)
         content/global/printPageSetup.xul           (resources/content/printPageSetup.xul)