Bug 923054 part 2 - Convert DataTransfer to WebIDL bindings, add WebIDL API and switch to the WebIDL binding. r=smaug
authorPeter Van der Beken <peterv@propagandism.org>
Wed, 26 Feb 2014 22:23:31 -0500
changeset 171237 a40bcf02bb6025f64ee57cf702c7926edc2f3782
parent 171236 6dacf81bb5255357276102eb305f57b2f3bfdd02
child 171238 023aed557989b2cf12825c82b701814ef1bf4b09
push id270
push userpvanderbeken@mozilla.com
push dateThu, 06 Mar 2014 09:24:21 +0000
reviewerssmaug
bugs923054
milestone30.0a1
Bug 923054 part 2 - Convert DataTransfer to WebIDL bindings, add WebIDL API and switch to the WebIDL binding. r=smaug
browser/base/content/test/newtab/head.js
content/base/src/nsContentUtils.cpp
content/base/src/nsCopySupport.cpp
dom/base/nsDOMClassInfo.cpp
dom/base/nsDOMClassInfoClasses.h
dom/bindings/Bindings.conf
dom/events/ClipboardEvent.cpp
dom/events/ClipboardEvent.h
dom/events/DataTransfer.cpp
dom/events/DataTransfer.h
dom/events/nsDOMDragEvent.cpp
dom/events/nsDOMDragEvent.h
dom/events/nsEventStateManager.cpp
dom/events/nsEventStateManager.h
dom/interfaces/events/nsIDOMDataTransfer.idl
dom/webidl/ClipboardEvent.webidl
dom/webidl/DataTransfer.webidl
dom/webidl/DragEvent.webidl
dom/webidl/moz.build
editor/libeditor/base/nsEditorEventListener.cpp
editor/libeditor/html/nsHTMLDataTransfer.cpp
layout/forms/nsFileControlFrame.cpp
widget/ContentEvents.h
widget/MouseEvents.h
--- a/browser/base/content/test/newtab/head.js
+++ b/browser/base/content/test/newtab/head.js
@@ -487,34 +487,18 @@ function sendDragEvent(aEventType, aTarg
 
 /**
  * Creates a custom drag event.
  * @param aEventType The drag event's type.
  * @param aData The event's drag data (optional).
  * @return The drag event.
  */
 function createDragEvent(aEventType, aData) {
-  let dataTransfer = {
-    mozUserCancelled: false,
-    setData: function () null,
-    setDragImage: function () null,
-    getData: function () aData,
-
-    types: {
-      contains: function (aType) aType == "text/x-moz-url"
-    },
-
-    mozGetDataAt: function (aType, aIndex) {
-      if (aIndex || aType != "text/x-moz-url")
-        return null;
-
-      return aData;
-    }
-  };
-
+  let dataTransfer = new getContentWindow().DataTransfer("dragstart", false);
+  dataTransfer.mozSetDataAt("text/x-moz-url", aData, 0);
   let event = getContentDocument().createEvent("DragEvents");
   event.initDragEvent(aEventType, true, true, getContentWindow(), 0, 0, 0, 0, 0,
                       false, false, false, false, 0, null, dataTransfer);
 
   return event;
 }
 
 /**
--- a/content/base/src/nsContentUtils.cpp
+++ b/content/base/src/nsContentUtils.cpp
@@ -4969,39 +4969,43 @@ nsContentUtils::SetDataTransferInEvent(W
   // drag events, get the object from the drag session.
   NS_ASSERTION(aDragEvent->message != NS_DRAGDROP_GESTURE &&
                aDragEvent->message != NS_DRAGDROP_START,
                "draggesture event created without a dataTransfer");
 
   nsCOMPtr<nsIDragSession> dragSession = GetDragSession();
   NS_ENSURE_TRUE(dragSession, NS_OK); // no drag in progress
 
-  nsCOMPtr<nsIDOMDataTransfer> initialDataTransfer;
-  dragSession->GetDataTransfer(getter_AddRefs(initialDataTransfer));
-  if (!initialDataTransfer) {
+  nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
+  nsCOMPtr<DataTransfer> initialDataTransfer;
+  dragSession->GetDataTransfer(getter_AddRefs(dataTransfer));
+  if (dataTransfer) {
+    initialDataTransfer = do_QueryInterface(dataTransfer);
+    if (!initialDataTransfer) {
+      return NS_ERROR_FAILURE;
+    }
+  } else {
     // 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.
-    initialDataTransfer = new DataTransfer(aDragEvent->message, true, -1);
-
-    NS_ENSURE_TRUE(initialDataTransfer, NS_ERROR_OUT_OF_MEMORY);
+    initialDataTransfer = new DataTransfer(aDragEvent->target, aDragEvent->message, true, -1);
 
     // now set it in the drag session so we don't need to create it again
     dragSession->SetDataTransfer(initialDataTransfer);
   }
 
   bool isCrossDomainSubFrameDrop = false;
   if (aDragEvent->message == NS_DRAGDROP_DROP ||
       aDragEvent->message == NS_DRAGDROP_DRAGDROP) {
     isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
   }
 
   // each event should use a clone of the original dataTransfer.
-  initialDataTransfer->Clone(aDragEvent->message, aDragEvent->userCancelled,
+  initialDataTransfer->Clone(aDragEvent->target, aDragEvent->message, aDragEvent->userCancelled,
                              isCrossDomainSubFrameDrop,
                              getter_AddRefs(aDragEvent->dataTransfer));
   NS_ENSURE_TRUE(aDragEvent->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 (aDragEvent->message == NS_DRAGDROP_ENTER ||
--- a/content/base/src/nsCopySupport.cpp
+++ b/content/base/src/nsCopySupport.cpp
@@ -641,17 +641,18 @@ nsCopySupport::FireClipboardEvent(int32_
   nsCOMPtr<nsIDocShell> docShell = do_GetInterface(piWindow);
   const bool chromeShell =
     docShell && docShell->ItemType() == nsIDocShellTreeItem::typeChrome;
 
   // next, fire the cut, copy or paste event
   bool doDefault = true;
   nsRefPtr<DataTransfer> clipboardData;
   if (chromeShell || Preferences::GetBool("dom.event.clipboardevents.enabled", true)) {
-    clipboardData = new DataTransfer(aType, aType == NS_PASTE, aClipboardType);
+    clipboardData =
+      new DataTransfer(piWindow, aType, aType == NS_PASTE, aClipboardType);
 
     nsEventStatus status = nsEventStatus_eIgnore;
     InternalClipboardEvent evt(true, aType);
     evt.clipboardData = clipboardData;
     nsEventDispatcher::Dispatch(content, presShell->GetPresContext(), &evt, nullptr,
                                 &status);
     // If the event was cancelled, don't do the clipboard operation
     doDefault = (status != nsEventStatus_eConsumeNoDefault);
--- a/dom/base/nsDOMClassInfo.cpp
+++ b/dom/base/nsDOMClassInfo.cpp
@@ -126,18 +126,16 @@
 #include "nsIDOMSVGLength.h"
 #include "nsIDOMSVGNumber.h"
 
 // Storage includes
 #include "nsIDOMStorage.h"
 #include "nsPIDOMStorage.h"
 
 // Drag and drop
-#include "nsIDOMDataTransfer.h"
-
 #include "nsIDOMFile.h"
 #include "nsDOMBlobBuilder.h" // nsDOMMultipartFile
 
 #include "nsIEventListenerService.h"
 #include "nsIMessageManager.h"
 
 #include "nsDOMTouchEvent.h"
 
@@ -436,20 +434,16 @@ static nsDOMClassInfoData sClassInfoData
 #ifdef MOZ_B2G_RIL
   NS_DEFINE_CLASSINFO_DATA(MozMobileConnection, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 #endif
 
   NS_DEFINE_CLASSINFO_DATA(CSSFontFaceRule, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
 
-  // data transfer for drag and drop
-  NS_DEFINE_CLASSINFO_DATA(DataTransfer, nsDOMGenericSH,
-                           DOM_DEFAULT_SCRIPTABLE_FLAGS)
-
   NS_DEFINE_CLASSINFO_DATA(EventListenerInfo, nsDOMGenericSH,
                            DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ContentFrameMessageManager, nsEventTargetSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS |
                                        nsIXPCScriptable::IS_GLOBAL_OBJECT)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageBroadcaster, nsDOMGenericSH,
                                        DOM_DEFAULT_SCRIPTABLE_FLAGS)
   NS_DEFINE_CHROME_ONLY_CLASSINFO_DATA(ChromeMessageSender, nsDOMGenericSH,
@@ -1118,20 +1112,16 @@ nsDOMClassInfo::Init()
      DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
   DOM_CLASSINFO_MAP_END
 #endif // MOZ_B2G_RIL
 
   DOM_CLASSINFO_MAP_BEGIN(CSSFontFaceRule, nsIDOMCSSFontFaceRule)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMCSSFontFaceRule)
   DOM_CLASSINFO_MAP_END
 
-  DOM_CLASSINFO_MAP_BEGIN(DataTransfer, nsIDOMDataTransfer)
-    DOM_CLASSINFO_MAP_ENTRY(nsIDOMDataTransfer)
-  DOM_CLASSINFO_MAP_END
-
   DOM_CLASSINFO_MAP_BEGIN(EventListenerInfo, nsIEventListenerInfo)
     DOM_CLASSINFO_MAP_ENTRY(nsIEventListenerInfo)
   DOM_CLASSINFO_MAP_END
 
   DOM_CLASSINFO_MAP_BEGIN_NO_CLASS_IF(ContentFrameMessageManager, nsISupports)
     DOM_CLASSINFO_MAP_ENTRY(nsIDOMEventTarget)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageListenerManager)
     DOM_CLASSINFO_MAP_ENTRY(nsIMessageSender)
--- a/dom/base/nsDOMClassInfoClasses.h
+++ b/dom/base/nsDOMClassInfoClasses.h
@@ -81,18 +81,16 @@ DOMCI_CLASS(MozMobileMessageThread)
 
 #ifdef MOZ_B2G_RIL
 DOMCI_CLASS(MozMobileConnection)
 #endif
 
 // @font-face in CSS
 DOMCI_CLASS(CSSFontFaceRule)
 
-DOMCI_CLASS(DataTransfer)
-
 DOMCI_CLASS(EventListenerInfo)
 
 DOMCI_CLASS(ContentFrameMessageManager)
 DOMCI_CLASS(ChromeMessageBroadcaster)
 DOMCI_CLASS(ChromeMessageSender)
 
 DOMCI_CLASS(MozCSSKeyframeRule)
 DOMCI_CLASS(MozCSSKeyframesRule)
--- a/dom/bindings/Bindings.conf
+++ b/dom/bindings/Bindings.conf
@@ -1964,9 +1964,8 @@ addExternalIface('SVGLength')
 addExternalIface('SVGNumber')
 addExternalIface('URI', nativeType='nsIURI', headerFile='nsIURI.h',
                  notflattened=True)
 addExternalIface('UserDataHandler')
 addExternalIface('XPathResult', nativeType='nsISupports')
 addExternalIface('XPathExpression')
 addExternalIface('XPathNSResolver')
 addExternalIface('XULCommandDispatcher')
-addExternalIface('DataTransfer', notflattened=True)
--- a/dom/events/ClipboardEvent.cpp
+++ b/dom/events/ClipboardEvent.cpp
@@ -33,22 +33,37 @@ NS_IMPL_ADDREF_INHERITED(ClipboardEvent,
 NS_IMPL_RELEASE_INHERITED(ClipboardEvent, nsDOMEvent)
 
 nsresult
 ClipboardEvent::InitClipboardEvent(const nsAString& aType,
                                    bool aCanBubble,
                                    bool aCancelable,
                                    nsIDOMDataTransfer* aClipboardData)
 {
-  nsresult rv = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
-  NS_ENSURE_SUCCESS(rv, rv);
+  nsCOMPtr<DataTransfer> clipboardData = do_QueryInterface(aClipboardData);
+  // Null clipboardData is OK
+
+  ErrorResult rv;
+  InitClipboardEvent(aType, aCanBubble, aCancelable, clipboardData, rv);
+
+  return rv.ErrorCode();
+}
+
+void
+ClipboardEvent::InitClipboardEvent(const nsAString& aType, bool aCanBubble,
+                                   bool aCancelable,
+                                   DataTransfer* aClipboardData,
+                                   ErrorResult& aError)
+{
+  aError = nsDOMEvent::InitEvent(aType, aCanBubble, aCancelable);
+  if (aError.Failed()) {
+    return;
+  }
 
   mEvent->AsClipboardEvent()->clipboardData = aClipboardData;
-
-  return NS_OK;
 }
 
 already_AddRefed<ClipboardEvent>
 ClipboardEvent::Constructor(const GlobalObject& aGlobal,
                             const nsAString& aType,
                             const ClipboardEventInit& aParam,
                             ErrorResult& aRv)
 {
@@ -58,45 +73,48 @@ ClipboardEvent::Constructor(const Global
 
   nsRefPtr<DataTransfer> clipboardData;
   if (e->mEventIsInternal) {
     InternalClipboardEvent* event = e->mEvent->AsClipboardEvent();
     if (event) {
       // Always create a clipboardData for the copy event. If this is changed to
       // support other types of events, make sure that read/write privileges are
       // checked properly within DataTransfer.
-      clipboardData = new DataTransfer(NS_COPY, false, -1);
+      clipboardData = new DataTransfer(ToSupports(e), NS_COPY, false, -1);
       clipboardData->SetData(aParam.mDataType, aParam.mData);
     }
   }
 
-  aRv = e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable,
-                              clipboardData);
+  e->InitClipboardEvent(aType, aParam.mBubbles, aParam.mCancelable,
+                        clipboardData, aRv);
   e->SetTrusted(trusted);
   return e.forget();
 }
 
 NS_IMETHODIMP
 ClipboardEvent::GetClipboardData(nsIDOMDataTransfer** aClipboardData)
 {
   NS_IF_ADDREF(*aClipboardData = GetClipboardData());
   return NS_OK;
 }
 
-nsIDOMDataTransfer*
+DataTransfer*
 ClipboardEvent::GetClipboardData()
 {
   InternalClipboardEvent* event = mEvent->AsClipboardEvent();
 
   if (!event->clipboardData) {
     if (mEventIsInternal) {
-      event->clipboardData = new DataTransfer(NS_COPY, false, -1);
+      event->clipboardData =
+        new DataTransfer(ToSupports(this), NS_COPY, false, -1);
     } else {
       event->clipboardData =
-        new DataTransfer(event->message, event->message == NS_PASTE, nsIClipboard::kGlobalClipboard);
+        new DataTransfer(ToSupports(this), event->message,
+                         event->message == NS_PASTE,
+                         nsIClipboard::kGlobalClipboard);
     }
   }
 
   return event->clipboardData;
 }
 
 } // namespace dom
 } // namespace mozilla
--- a/dom/events/ClipboardEvent.h
+++ b/dom/events/ClipboardEvent.h
@@ -8,16 +8,17 @@
 
 #include "nsIDOMClipboardEvent.h"
 #include "nsDOMEvent.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/ClipboardEventBinding.h"
 
 namespace mozilla {
 namespace dom {
+class DataTransfer;
 
 class ClipboardEvent : public nsDOMEvent,
                        public nsIDOMClipboardEvent
 {
 public:
   ClipboardEvent(EventTarget* aOwner,
                  nsPresContext* aPresContext,
                  InternalClipboardEvent* aEvent);
@@ -36,15 +37,20 @@ public:
   }
 
   static already_AddRefed<ClipboardEvent>
   Constructor(const GlobalObject& aGlobal,
               const nsAString& aType,
               const ClipboardEventInit& aParam,
               ErrorResult& aRv);
 
-  nsIDOMDataTransfer* GetClipboardData();
+  DataTransfer* GetClipboardData();
+
+  void InitClipboardEvent(const nsAString& aType, bool aCanBubble,
+                          bool aCancelable,
+                          DataTransfer* aClipboardData,
+                          ErrorResult& aError);
 };
 
 } // namespace dom
 } // namespace mozilla
 
 #endif // mozilla_dom_ClipboardEvent_h_
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -7,134 +7,183 @@
 #include "mozilla/ArrayUtils.h"
 #include "mozilla/BasicEvents.h"
 
 #include "DataTransfer.h"
 
 #include "nsIDOMDocument.h"
 #include "nsIVariant.h"
 #include "nsISupportsPrimitives.h"
-#include "nsDOMClassInfoID.h"
 #include "nsIScriptSecurityManager.h"
 #include "nsDOMLists.h"
 #include "nsError.h"
 #include "nsIDragService.h"
 #include "nsIClipboard.h"
 #include "nsContentUtils.h"
 #include "nsIContent.h"
 #include "nsCRT.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIDocument.h"
 #include "nsIScriptGlobalObject.h"
-
-DOMCI_DATA(DataTransfer, mozilla::dom::DataTransfer)
+#include "mozilla/dom/DataTransferBinding.h"
+#include "mozilla/dom/Element.h"
+#include "mozilla/dom/BindingUtils.h"
 
 namespace mozilla {
 namespace dom {
 
 NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
   if (tmp->mFiles) {
     tmp->mFiles->Disconnect();
     NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles)
   }
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
+  NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
+  NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
+NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DataTransfer)
 
 NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer)
 NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer)
 
 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer)
+  NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer)
   NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer)
   NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer)
-  NS_DOM_INTERFACE_MAP_ENTRY_CLASSINFO(DataTransfer)
 NS_INTERFACE_MAP_END
 
 // the size of the array
 const char DataTransfer::sEffects[8][9] = {
   "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"
 };
 
-DataTransfer::DataTransfer(uint32_t aEventType, bool aIsExternal, int32_t aClipboardType)
-  : mEventType(aEventType),
+DataTransfer::DataTransfer(nsISupports* aParent, uint32_t aEventType,
+                           bool aIsExternal, int32_t aClipboardType)
+  : mParent(aParent),
+    mEventType(aEventType),
     mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
     mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED),
     mCursorState(false),
     mReadOnly(true),
     mIsExternal(aIsExternal),
     mUserCancelled(false),
     mIsCrossDomainSubFrameDrop(false),
     mClipboardType(aClipboardType),
     mDragImageX(0),
     mDragImageY(0)
 {
+  MOZ_ASSERT(mParent);
+  SetIsDOMBinding();
   // For these events, we want to be able to add data to the data transfer, so
   // clear the readonly state. Otherwise, the data is already present. For
   // external usage, cache the data from the native clipboard or drag.
   if (aEventType == NS_CUT ||
       aEventType == NS_COPY ||
       aEventType == NS_DRAGDROP_START ||
       aEventType == NS_DRAGDROP_GESTURE) {
     mReadOnly = false;
-} else if (mIsExternal) {
+  } else if (mIsExternal) {
     if (aEventType == NS_PASTE) {
       CacheExternalClipboardFormats();
     } else if (aEventType >= NS_DRAGDROP_EVENT_START && aEventType <= NS_DRAGDROP_LEAVE_SYNTH) {
       CacheExternalDragFormats();
     }
   }
 }
 
-DataTransfer::DataTransfer(uint32_t aEventType,
+DataTransfer::DataTransfer(nsISupports* aParent,
+                           uint32_t aEventType,
                            const uint32_t aEffectAllowed,
                            bool aCursorState,
                            bool aIsExternal,
                            bool aUserCancelled,
                            bool aIsCrossDomainSubFrameDrop,
                            int32_t aClipboardType,
                            nsTArray<nsTArray<TransferItem> >& aItems,
-                           nsIDOMElement* aDragImage,
+                           Element* aDragImage,
                            uint32_t aDragImageX,
                            uint32_t aDragImageY)
-  : mEventType(aEventType),
+  : mParent(aParent),
+    mEventType(aEventType),
     mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE),
     mEffectAllowed(aEffectAllowed),
     mCursorState(aCursorState),
     mReadOnly(true),
     mIsExternal(aIsExternal),
     mUserCancelled(aUserCancelled),
     mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop),
     mClipboardType(aClipboardType),
     mItems(aItems),
     mDragImage(aDragImage),
     mDragImageX(aDragImageX),
     mDragImageY(aDragImageY)
 {
+  MOZ_ASSERT(mParent);
+  SetIsDOMBinding();
   // 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 DataTransfer constructor");
 }
 
+DataTransfer::~DataTransfer()
+{
+  if (mFiles) {
+    mFiles->Disconnect();
+  }
+}
+
+// static
+already_AddRefed<DataTransfer>
+DataTransfer::Constructor(const GlobalObject& aGlobal,
+                          const nsAString& aEventType, bool aIsExternal,
+                          ErrorResult& aRv)
+{
+  nsAutoCString onEventType("on");
+  AppendUTF16toUTF8(aEventType, onEventType);
+  nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(onEventType);
+  if (!eventTypeAtom) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return nullptr;
+  }
+
+  uint32_t eventType = nsContentUtils::GetEventId(eventTypeAtom);
+  nsRefPtr<DataTransfer> transfer = new DataTransfer(aGlobal.GetAsSupports(),
+                                                     eventType, aIsExternal,
+                                                     -1);
+  return transfer.forget();
+}
+
+JSObject*
+DataTransfer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
+{
+  return DataTransferBinding::Wrap(aCx, aScope, this);
+}
+
 NS_IMETHODIMP
 DataTransfer::GetDropEffect(nsAString& aDropEffect)
 {
-  aDropEffect.AssignASCII(sEffects[mDropEffect]);
+  nsString dropEffect;
+  GetDropEffect(dropEffect);
+  aDropEffect = dropEffect;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DataTransfer::SetDropEffect(const nsAString& aDropEffect)
 {
   // the drop effect can only be 'none', 'copy', 'move' or 'link'.
   for (uint32_t e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
@@ -148,20 +197,19 @@ DataTransfer::SetDropEffect(const nsAStr
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DataTransfer::GetEffectAllowed(nsAString& aEffectAllowed)
 {
-  if (mEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
-    aEffectAllowed.AssignLiteral("uninitialized");
-  else
-    aEffectAllowed.AssignASCII(sEffects[mEffectAllowed]);
+  nsString effectAllowed;
+  GetEffectAllowed(effectAllowed);
+  aEffectAllowed = effectAllowed;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed)
 {
   if (aEffectAllowed.EqualsLiteral("uninitialized")) {
     mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
@@ -213,115 +261,127 @@ DataTransfer::SetEffectAllowedInt(uint32
 {
   mEffectAllowed = aEffectAllowed;
   return  NS_OK;
 }
 
 NS_IMETHODIMP
 DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
 {
-  *aUserCancelled = mUserCancelled;
+  *aUserCancelled = MozUserCancelled();
   return NS_OK;
 }
 
-NS_IMETHODIMP
-DataTransfer::GetFiles(nsIDOMFileList** aFileList)
+nsDOMFileList*
+DataTransfer::GetFiles(ErrorResult& aRv)
 {
-  *aFileList = nullptr;
-
   if (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP &&
       mEventType != NS_PASTE) {
-    return NS_OK;
+    return nullptr;
   }
 
   if (!mFiles) {
     mFiles = new nsDOMFileList(static_cast<nsIDOMDataTransfer*>(this));
 
     uint32_t count = mItems.Length();
 
     for (uint32_t i = 0; i < count; i++) {
       nsCOMPtr<nsIVariant> variant;
-      nsresult rv = MozGetDataAt(NS_ConvertUTF8toUTF16(kFileMime), i, getter_AddRefs(variant));
-      NS_ENSURE_SUCCESS(rv, rv);
+      aRv = MozGetDataAt(NS_ConvertUTF8toUTF16(kFileMime), i, getter_AddRefs(variant));
+      if (aRv.Failed()) {
+        return nullptr;
+      }
 
       if (!variant)
         continue;
 
       nsCOMPtr<nsISupports> supports;
-      rv = variant->GetAsISupports(getter_AddRefs(supports));
+      nsresult rv = variant->GetAsISupports(getter_AddRefs(supports));
 
       if (NS_FAILED(rv))
         continue;
 
       nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
 
       if (!file)
         continue;
 
       nsRefPtr<nsDOMFileFile> domFile = new nsDOMFileFile(file);
 
-      if (!mFiles->Append(domFile))
-        return NS_ERROR_FAILURE;
+      if (!mFiles->Append(domFile)) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return nullptr;
+      }
     }
   }
 
-  *aFileList = mFiles;
-  NS_ADDREF(*aFileList);
-  return NS_OK;
+  return mFiles;
 }
 
 NS_IMETHODIMP
-DataTransfer::GetTypes(nsIDOMDOMStringList** aTypes)
+DataTransfer::GetFiles(nsIDOMFileList** aFileList)
 {
-  *aTypes = nullptr;
+  ErrorResult rv;
+  *aFileList = GetFiles(rv);
+  return rv.ErrorCode();
+}
 
+already_AddRefed<nsIDOMDOMStringList>
+DataTransfer::Types()
+{
   nsRefPtr<nsDOMStringList> types = new nsDOMStringList();
-
   if (mItems.Length()) {
     const nsTArray<TransferItem>& item = mItems[0];
     for (uint32_t i = 0; i < item.Length(); i++)
       types->Add(item[i].mFormat);
 
     bool filePresent, filePromisePresent;
     types->Contains(NS_LITERAL_STRING(kFileMime), &filePresent);
     types->Contains(NS_LITERAL_STRING("application/x-moz-file-promise"), &filePromisePresent);
     if (filePresent || filePromisePresent)
       types->Add(NS_LITERAL_STRING("Files"));
   }
 
-  *aTypes = types;
-  NS_ADDREF(*aTypes);
+  return types.forget();
+}
+
+NS_IMETHODIMP
+DataTransfer::GetTypes(nsIDOMDOMStringList** aTypes)
+{
+  *aTypes = Types().get();
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-DataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
+void
+DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
+                      ErrorResult& aRv)
 {
   // 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;
+  if (NS_FAILED(rv)) {
+    if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
+      aRv.Throw(rv);
+    }
+    return;
   }
 
-  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
     nsAutoString lowercaseFormat;
-    rv = nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
-    if (NS_FAILED(rv)) {
-      return rv;
+    aRv = nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
+    if (aRv.Failed()) {
+      return;
     }
 
     if (lowercaseFormat.EqualsLiteral("url")) {
       int32_t lastidx = 0, idx;
       int32_t length = stringdata.Length();
       while (lastidx < length) {
         idx = stringdata.FindChar('\n', lastidx);
         // lines beginning with # are comments
@@ -330,116 +390,169 @@ DataTransfer::GetData(const nsAString& a
             break;
         }
         else {
           if (idx == -1)
             aData.Assign(Substring(stringdata, lastidx));
           else
             aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
           aData = nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData, true);
-          return NS_OK;
+          return;
         }
         lastidx = idx + 1;
       }
     }
     else {
       aData = stringdata;
     }
   }
+}
 
-  return NS_OK;
+NS_IMETHODIMP
+DataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
+{
+  ErrorResult rv;
+  GetData(aFormat, aData, rv);
+  return rv.ErrorCode();
+}
+
+void
+DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
+                      ErrorResult& aRv)
+{
+  nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
+  if (!variant) {
+    aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
+    return;
+  }
+
+  variant->SetAsAString(aData);
+
+  aRv = MozSetDataAt(aFormat, variant, 0);
 }
 
 NS_IMETHODIMP
 DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData)
 {
-  nsCOMPtr<nsIWritableVariant> variant = do_CreateInstance(NS_VARIANT_CONTRACTID);
-  NS_ENSURE_TRUE(variant, NS_ERROR_OUT_OF_MEMORY);
+  ErrorResult rv;
+  SetData(aFormat, aData, rv);
+  return rv.ErrorCode();
+}
 
-  variant->SetAsAString(aData);
+void
+DataTransfer::ClearData(const Optional<nsAString>& aFormat, ErrorResult& aRv)
+{
+  if (mReadOnly) {
+    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
+    return;
+  }
 
-  return MozSetDataAt(aFormat, variant, 0);
+  if (mItems.Length() == 0) {
+    return;
+  }
+
+  if (aFormat.WasPassed()) {
+    MozClearDataAtHelper(aFormat.Value(), 0, aRv);
+  } else {
+    MozClearDataAtHelper(EmptyString(), 0, aRv);
+  }
 }
 
 NS_IMETHODIMP
 DataTransfer::ClearData(const nsAString& aFormat)
 {
-  nsresult rv = MozClearDataAt(aFormat, 0);
-  return (rv == NS_ERROR_DOM_INDEX_SIZE_ERR) ? NS_OK : rv;
+  Optional<nsAString> format;
+  format = &aFormat;
+  ErrorResult rv;
+  ClearData(format, rv);
+  return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 DataTransfer::GetMozItemCount(uint32_t* aCount)
 {
-  *aCount = mItems.Length();
+  *aCount = MozItemCount();
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DataTransfer::GetMozCursor(nsAString& aCursorState)
 {
-  if (mCursorState) {
-    aCursorState.AssignLiteral("default");
-  } else {
-    aCursorState.AssignLiteral("auto");
-  }
+  nsString cursor;
+  GetMozCursor(cursor);
+  aCursorState = cursor;
   return NS_OK;
 }
 
 NS_IMETHODIMP
 DataTransfer::SetMozCursor(const nsAString& aCursorState)
 {
   // Lock the cursor to an arrow during the drag.
   mCursorState = aCursorState.EqualsLiteral("default");
 
   return NS_OK;
 }
 
-NS_IMETHODIMP
-DataTransfer::GetMozSourceNode(nsIDOMNode** aSourceNode)
+already_AddRefed<nsINode>
+DataTransfer::GetMozSourceNode()
 {
-  *aSourceNode = nullptr;
-
   nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
-  if (!dragSession)
-    return NS_OK;
+  if (!dragSession) {
+    return nullptr;
+  }
 
   nsCOMPtr<nsIDOMNode> sourceNode;
   dragSession->GetSourceNode(getter_AddRefs(sourceNode));
-  if (sourceNode && !nsContentUtils::CanCallerAccess(sourceNode))
-    return NS_OK;
+  nsCOMPtr<nsINode> node = do_QueryInterface(sourceNode);
+  if (node && !nsContentUtils::CanCallerAccess(node)) {
+    return nullptr;
+  }
 
-  sourceNode.swap(*aSourceNode);
-  return NS_OK;
+  return node.forget();
 }
 
 NS_IMETHODIMP
-DataTransfer::MozTypesAt(uint32_t aIndex, nsIDOMDOMStringList** aTypes)
+DataTransfer::GetMozSourceNode(nsIDOMNode** aSourceNode)
 {
-  *aTypes = nullptr;
+  nsCOMPtr<nsINode> sourceNode = GetMozSourceNode();
+  if (!sourceNode) {
+    *aSourceNode = nullptr;
+    return NS_OK;
+  }
 
+  return CallQueryInterface(sourceNode, aSourceNode);
+}
+
+already_AddRefed<nsIDOMDOMStringList>
+DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv)
+{
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
       (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) {
-    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return nullptr;
   }
 
   nsRefPtr<nsDOMStringList> types = new nsDOMStringList();
-
   if (aIndex < mItems.Length()) {
     // note that you can retrieve the types regardless of their principal
     nsTArray<TransferItem>& item = mItems[aIndex];
     for (uint32_t i = 0; i < item.Length(); i++)
       types->Add(item[i].mFormat);
   }
 
-  *aTypes = types;
-  NS_ADDREF(*aTypes);
+  return types.forget();
+}
 
-  return NS_OK;
+NS_IMETHODIMP
+DataTransfer::MozTypesAt(uint32_t aIndex, nsIDOMDOMStringList** aTypes)
+{
+  ErrorResult rv;
+  *aTypes = MozTypesAt(aIndex, rv).get();
+  return rv.ErrorCode();
 }
 
 NS_IMETHODIMP
 DataTransfer::MozGetDataAt(const nsAString& aFormat, uint32_t aIndex,
                            nsIVariant** aData)
 {
   *aData = nullptr;
 
@@ -516,27 +629,51 @@ DataTransfer::MozGetDataAt(const nsAStri
       NS_IF_ADDREF(*aData);
       return NS_OK;
     }
   }
 
   return NS_OK;
 }
 
+JS::Value
+DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
+                           uint32_t aIndex, mozilla::ErrorResult& aRv)
+{
+  nsCOMPtr<nsIVariant> data;
+  aRv = MozGetDataAt(aFormat, aIndex, getter_AddRefs(data));
+  if (aRv.Failed()) {
+    return JS::UndefinedValue();
+  }
+
+  if (!data) {
+    return JS::NullValue();
+  }
+
+  JS::Rooted<JS::Value> result(aCx);
+  JS::Rooted<JSObject*> scope(aCx, GetWrapper());
+  if (!VariantToJsval(aCx, scope, data, &result)) {
+    aRv = NS_ERROR_FAILURE;
+    return JS::UndefinedValue();
+  }
+
+  return result;
+}
+
 NS_IMETHODIMP
 DataTransfer::MozSetDataAt(const nsAString& aFormat, nsIVariant* aData,
                            uint32_t aIndex)
 {
-  NS_ENSURE_TRUE(aData, NS_ERROR_NULL_POINTER);
-
-  if (aFormat.IsEmpty())
+  if (aFormat.IsEmpty()) {
     return NS_OK;
+  }
 
-  if (mReadOnly)
+  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;
   }
 
   // Only the first item is valid for clipboard events
@@ -551,116 +688,175 @@ DataTransfer::MozSetDataAt(const nsAStri
        aFormat.EqualsLiteral("application/x-moz-file")) &&
        !nsContentUtils::IsCallerChrome()) {
     return NS_ERROR_DOM_SECURITY_ERR;
   }
 
   nsresult rv = NS_OK;
   nsIPrincipal* principal = GetCurrentPrincipal(&rv);
   NS_ENSURE_SUCCESS(rv, rv);
+
   return SetDataWithPrincipal(aFormat, aData, aIndex, principal);
 }
 
-NS_IMETHODIMP
-DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex)
+void
+DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
+                           JS::Handle<JS::Value> aData,
+                           uint32_t aIndex, ErrorResult& aRv)
 {
-  if (mReadOnly)
-    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  nsCOMPtr<nsIVariant> data;
+  aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
+                                                    getter_AddRefs(data));
+  if (!aRv.Failed()) {
+    aRv = MozSetDataAt(aFormat, data, aIndex);
+  }
+}
+
+void
+DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
+                             ErrorResult& aRv)
+{
+  if (mReadOnly) {
+    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
+    return;
+  }
 
   if (aIndex >= mItems.Length()) {
-    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
   }
 
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
       (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) {
-    return NS_ERROR_DOM_INDEX_SIZE_ERR;
+    aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
+    return;
   }
 
+  MozClearDataAtHelper(aFormat, aIndex, aRv);
+}
+
+void
+DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
+                                   ErrorResult& aRv)
+{
+  MOZ_ASSERT(!mReadOnly);
+  MOZ_ASSERT(aIndex < mItems.Length());
+  MOZ_ASSERT(aIndex == 0 ||
+             (mEventType != NS_CUT && mEventType != NS_COPY &&
+              mEventType != NS_PASTE));
+
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
   nsresult rv = NS_OK;
   nsIPrincipal* principal = GetCurrentPrincipal(&rv);
-  NS_ENSURE_SUCCESS(rv, rv);
+  if (NS_FAILED(rv)) {
+    aRv = rv;
+    return;
+  }
 
   // if the format is empty, clear all formats
   bool 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 (int32_t 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
       bool subsumes;
       if (formatitem.mPrincipal && principal &&
-          (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes))
-        return NS_ERROR_DOM_SECURITY_ERR;
+          (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes)) {
+        aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
+        return;
+      }
 
       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
+DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex)
+{
+  ErrorResult rv;
+  MozClearDataAt(aFormat, aIndex, rv);
+  return rv.ErrorCode();
+}
+
+void
+DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY,
+                           ErrorResult& aRv)
+{
+  if (mReadOnly) {
+    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
+    return;
+  }
+
+  mDragImage = &aImage;
+  mDragImageX = aX;
+  mDragImageY = aY;
 }
 
 NS_IMETHODIMP
 DataTransfer::SetDragImage(nsIDOMElement* aImage, int32_t aX, int32_t aY)
 {
-  if (mReadOnly)
-    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
+  ErrorResult rv;
+  nsCOMPtr<Element> image = do_QueryInterface(aImage);
+  if (image) {
+    SetDragImage(*image, aX, aY, rv);
+  }
+  return rv.ErrorCode();
+}
 
-  if (aImage) {
-    nsCOMPtr<nsIContent> content = do_QueryInterface(aImage);
-    NS_ENSURE_TRUE(content, NS_ERROR_INVALID_ARG);
+void
+DataTransfer::AddElement(Element& aElement, ErrorResult& aRv)
+{
+  if (mReadOnly) {
+    aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
+    return;
   }
-  mDragImage = aImage;
-  mDragImageX = aX;
-  mDragImageY = aY;
-  return NS_OK;
+
+  mDragTarget = &aElement;
 }
 
 NS_IMETHODIMP
 DataTransfer::AddElement(nsIDOMElement* aElement)
 {
   NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
 
-  if (aElement) {
-    nsCOMPtr<nsIContent> content = do_QueryInterface(aElement);
-    NS_ENSURE_TRUE(content, NS_ERROR_INVALID_ARG);
-  }
+  nsCOMPtr<Element> element = do_QueryInterface(aElement);
+  NS_ENSURE_TRUE(element, NS_ERROR_INVALID_ARG);
 
-  if (mReadOnly)
-    return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
-
-  mDragTarget = do_QueryInterface(aElement);
-
-  return NS_OK;
+  ErrorResult rv;
+  AddElement(*element, rv);
+  return rv.ErrorCode();
 }
 
 nsresult
-DataTransfer::Clone(uint32_t aEventType, bool aUserCancelled,
-                    bool aIsCrossDomainSubFrameDrop,
-                    nsIDOMDataTransfer** aNewDataTransfer)
+DataTransfer::Clone(nsISupports* aParent, uint32_t aEventType,
+                    bool aUserCancelled, bool aIsCrossDomainSubFrameDrop,
+                    DataTransfer** aNewDataTransfer)
 {
   DataTransfer* newDataTransfer =
-    new DataTransfer(aEventType, mEffectAllowed, mCursorState, mIsExternal,
-                     aUserCancelled, aIsCrossDomainSubFrameDrop, mClipboardType,
-                     mItems, mDragImage, mDragImageX, mDragImageY);
+    new DataTransfer(aParent, aEventType, mEffectAllowed, mCursorState,
+                     mIsExternal, aUserCancelled, aIsCrossDomainSubFrameDrop,
+                     mClipboardType, mItems, mDragImage, mDragImageX,
+                     mDragImageY);
 
   *aNewDataTransfer = newDataTransfer;
   NS_ADDREF(*aNewDataTransfer);
   return NS_OK;
 }
 
 already_AddRefed<nsISupportsArray>
 DataTransfer::GetTransferables(nsIDOMNode* aDragTarget)
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -7,100 +7,176 @@
 #define mozilla_dom_DataTransfer_h
 
 #include "nsString.h"
 #include "nsTArray.h"
 #include "nsIVariant.h"
 #include "nsIPrincipal.h"
 #include "nsIDOMDataTransfer.h"
 #include "nsIDOMElement.h"
+#include "nsIDragService.h"
 #include "nsCycleCollectionParticipant.h"
 
 #include "nsAutoPtr.h"
 #include "nsDOMFile.h"
 #include "mozilla/Attributes.h"
 
 class nsEventStateManager;
+class nsINode;
 class nsITransferable;
 class nsISupportsArray;
 class nsILoadContext;
 
 namespace mozilla {
 namespace dom {
 
+class Element;
+template<typename T> class Optional;
+
 /**
  * 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 DataTransfer MOZ_FINAL : public nsIDOMDataTransfer
+#define NS_DATATRANSFER_IID \
+{ 0x43ee0327, 0xde5d, 0x463d, \
+  { 0x9b, 0xd0, 0xf1, 0x79, 0x09, 0x69, 0xf2, 0xfb } }
+
+class DataTransfer MOZ_FINAL : public nsIDOMDataTransfer,
+                               public nsWrapperCache
 {
 public:
+  NS_DECLARE_STATIC_IID_ACCESSOR(NS_DATATRANSFER_IID)
+
   NS_DECL_CYCLE_COLLECTING_ISUPPORTS
   NS_DECL_NSIDOMDATATRANSFER
 
-  NS_DECL_CYCLE_COLLECTION_CLASS_AMBIGUOUS(DataTransfer, nsIDOMDataTransfer)
+  NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DataTransfer)
 
   friend class ::nsEventStateManager;
 
 protected:
 
   // hide the default constructor
   DataTransfer();
 
   // this constructor is used only by the Clone method to copy the fields as
   // needed to a new data transfer.
-  DataTransfer(uint32_t aEventType,
+  DataTransfer(nsISupports* aParent,
+               uint32_t aEventType,
                const uint32_t aEffectAllowed,
                bool aCursorState,
                bool aIsExternal,
                bool aUserCancelled,
                bool aIsCrossDomainSubFrameDrop,
                int32_t aClipboardType,
                nsTArray<nsTArray<TransferItem> >& aItems,
-               nsIDOMElement* aDragImage,
+               Element* aDragImage,
                uint32_t aDragImageX,
                uint32_t aDragImageY);
 
-  ~DataTransfer()
-  {
-    if (mFiles) {
-      mFiles->Disconnect();
-    }
-  }
+  ~DataTransfer();
 
   static const char sEffects[8][9];
 
 public:
 
   // Constructor for DataTransfer.
   //
   // aEventType is an event constant (such as NS_DRAGDROP_START)
   //
   // aIsExternal must only be true when used to create a dataTransfer for a
   // paste or a drag that was started without using a data transfer. The
   // latter will occur when an external drag occurs, that is, a drag where the
   // source is another application, or a drag is started by calling the drag
   // service directly. For clipboard operations, aClipboardType indicates
   // which clipboard to use, from nsIClipboard, or -1 for non-clipboard operations,
   // or if access to the system clipboard should not be allowed.
-  DataTransfer(uint32_t aEventType, bool aIsExternal, int32_t aClipboardType);
+  DataTransfer(nsISupports* aParent, uint32_t aEventType, bool aIsExternal,
+               int32_t aClipboardType);
+
+  virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope);
+  nsISupports* GetParentObject()
+  {
+    return mParent;
+  }
+
+  void SetParentObject(nsISupports* aNewParent)
+  {
+    MOZ_ASSERT(aNewParent);
+    // Setting the parent after we've been wrapped is pointless, so
+    // make sure we aren't wrapped yet.
+    MOZ_ASSERT(!GetWrapperPreserveColor());
+    mParent = aNewParent;
+  }
+
+  static already_AddRefed<DataTransfer>
+  Constructor(const GlobalObject& aGlobal, const nsAString& aEventType,
+              bool aIsExternal, ErrorResult& aRv);
 
-  void GetDragTarget(nsIDOMElement** aDragTarget)
+  void GetDropEffect(nsString& aDropEffect)
+  {
+    aDropEffect.AssignASCII(sEffects[mDropEffect]);
+  }
+  void GetEffectAllowed(nsString& aEffectAllowed)
+  {
+    if (mEffectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED) {
+      aEffectAllowed.AssignLiteral("uninitialized");
+    } else {
+      aEffectAllowed.AssignASCII(sEffects[mEffectAllowed]);
+    }
+  }
+  void SetDragImage(Element& aElement, int32_t aX, int32_t aY,
+                    ErrorResult& aRv);
+  already_AddRefed<nsIDOMDOMStringList> Types();
+  void GetData(const nsAString& aFormat, nsAString& aData, ErrorResult& aRv);
+  void SetData(const nsAString& aFormat, const nsAString& aData,
+               ErrorResult& aRv);
+  void ClearData(const mozilla::dom::Optional<nsAString>& aFormat,
+                 mozilla::ErrorResult& aRv);
+  nsDOMFileList* GetFiles(mozilla::ErrorResult& aRv);
+  void AddElement(Element& aElement, mozilla::ErrorResult& aRv);
+  uint32_t MozItemCount()
   {
-    *aDragTarget = mDragTarget;
-    NS_IF_ADDREF(*aDragTarget);
+    return mItems.Length();
+  }
+  void GetMozCursor(nsString& aCursor)
+  {
+    if (mCursorState) {
+      aCursor.AssignLiteral("default");
+    } else {
+      aCursor.AssignLiteral("auto");
+    }
+  }
+  already_AddRefed<nsIDOMDOMStringList> MozTypesAt(uint32_t aIndex,
+                                                   mozilla::ErrorResult& aRv);
+  void MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
+                      mozilla::ErrorResult& aRv);
+  void MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
+                    JS::Handle<JS::Value> aData, uint32_t aIndex,
+                    mozilla::ErrorResult& aRv);
+  JS::Value MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
+                         uint32_t aIndex, mozilla::ErrorResult& aRv);
+  bool MozUserCancelled()
+  {
+    return mUserCancelled;
+  }
+  already_AddRefed<nsINode> GetMozSourceNode();
+
+  mozilla::dom::Element* GetDragTarget()
+  {
+    return mDragTarget;
   }
 
   // a readonly dataTransfer cannot have new data added or existing data removed.
   // Only the dropEffect and effectAllowed may be modified.
   void SetReadOnly() { mReadOnly = true; }
 
   // converts the data into an array of nsITransferable objects to be used for
   // drag and drop or clipboard operations.
@@ -122,26 +198,29 @@ public:
   // Similar to SetData except also specifies the principal to store.
   // aData may be null when called from CacheExternalDragFormats or
   // CacheExternalClipboardFormats.
   nsresult SetDataWithPrincipal(const nsAString& aFormat,
                                 nsIVariant* aData,
                                 uint32_t aIndex,
                                 nsIPrincipal* aPrincipal);
 
-protected:
-
   // returns a weak reference to the drag image
-  nsIDOMElement* GetDragImage(int32_t* aX, int32_t* aY)
+  Element* GetDragImage(int32_t* aX, int32_t* aY)
   {
     *aX = mDragImageX;
     *aY = mDragImageY;
     return mDragImage;
   }
 
+  nsresult Clone(nsISupports* aParent, uint32_t aEventType, bool aUserCancelled,
+                 bool aIsCrossDomainSubFrameDrop, DataTransfer** aResult);
+
+protected:
+
   // returns a weak reference to the current principal
   nsIPrincipal* GetCurrentPrincipal(nsresult* rv);
 
   // 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 text and uri-list data formats that exist in the drag service or
@@ -154,16 +233,21 @@ protected:
 
   // caches the formats that exist in the clipboard
   void CacheExternalClipboardFormats();
 
   // fills in the data field of aItem with the data from the drag service or
   // clipboard for a given index.
   void FillInExternalData(TransferItem& aItem, uint32_t aIndex);
 
+  void MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
+                            mozilla::ErrorResult& aRv);
+
+  nsCOMPtr<nsISupports> mParent;
+
   // the event type this data transfer is for. This will correspond to an
   // event->message value.
   uint32_t mEventType;
 
   // the drop effect and effect allowed
   uint32_t mDropEffect;
   uint32_t mEffectAllowed;
 
@@ -191,22 +275,24 @@ protected:
 
   // array of items, each containing an array of format->data pairs
   nsTArray<nsTArray<TransferItem> > mItems;
 
   // array of files, containing only the files present in the dataTransfer
   nsRefPtr<nsDOMFileList> mFiles;
 
   // the target of the drag. The drag and dragend events will fire at this.
-  nsCOMPtr<nsIDOMElement> mDragTarget;
+  nsCOMPtr<mozilla::dom::Element> 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;
+  nsCOMPtr<mozilla::dom::Element> mDragImage;
   uint32_t mDragImageX;
   uint32_t mDragImageY;
 };
 
+NS_DEFINE_STATIC_IID_ACCESSOR(DataTransfer, NS_DATATRANSFER_IID)
+
 } // namespace dom
 } // namespace mozilla
 
 #endif /* mozilla_dom_DataTransfer_h */
 
--- a/dom/events/nsDOMDragEvent.cpp
+++ b/dom/events/nsDOMDragEvent.cpp
@@ -4,18 +4,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "nsDOMDragEvent.h"
 #include "nsContentUtils.h"
 #include "prtime.h"
 #include "mozilla/MouseEvents.h"
 
 using namespace mozilla;
+using namespace mozilla::dom;
 
-nsDOMDragEvent::nsDOMDragEvent(mozilla::dom::EventTarget* aOwner,
+nsDOMDragEvent::nsDOMDragEvent(EventTarget* aOwner,
                                nsPresContext* aPresContext,
                                WidgetDragEvent* aEvent)
   : nsDOMMouseEvent(aOwner, aPresContext, aEvent ? aEvent :
                     new WidgetDragEvent(false, 0, nullptr))
 {
   if (aEvent) {
     mEventIsInternal = false;
   }
@@ -29,48 +30,76 @@ nsDOMDragEvent::nsDOMDragEvent(mozilla::
 
 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_END_INHERITING(nsDOMMouseEvent)
 
+void
+nsDOMDragEvent::InitDragEvent(const nsAString& aType, bool aCanBubble,
+                              bool aCancelable, nsIDOMWindow* aView,
+                              int32_t aDetail, int32_t aScreenX,
+                              int32_t aScreenY, int32_t aClientX,
+                              int32_t aClientY, bool aCtrlKey, bool aAltKey,
+                              bool aShiftKey, bool aMetaKey, uint16_t aButton,
+                              EventTarget* aRelatedTarget,
+                              DataTransfer* aDataTransfer, ErrorResult& aError)
+{
+  aError =
+    nsDOMMouseEvent::InitMouseEvent(aType, aCanBubble, aCancelable,
+                                    aView, aDetail, aScreenX, aScreenY,
+                                    aClientX, aClientY, aCtrlKey, aAltKey,
+                                    aShiftKey, aMetaKey, aButton,
+                                    aRelatedTarget);
+  if (aError.Failed()) {
+    return;
+  }
+
+  if (mEventIsInternal && mEvent) {
+    mEvent->AsDragEvent()->dataTransfer = aDataTransfer;
+  }
+}
+
 NS_IMETHODIMP
 nsDOMDragEvent::InitDragEvent(const nsAString & aType,
                               bool aCanBubble, bool aCancelable,
                               nsIDOMWindow* aView, int32_t aDetail,
                               int32_t aScreenX, int32_t aScreenY,
                               int32_t aClientX, int32_t aClientY, 
                               bool aCtrlKey, bool aAltKey, bool aShiftKey,
                               bool aMetaKey, uint16_t aButton,
                               nsIDOMEventTarget *aRelatedTarget,
                               nsIDOMDataTransfer* aDataTransfer)
 {
+  nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(aDataTransfer);
+  NS_ENSURE_ARG(dataTransfer);
+
   nsresult rv = nsDOMMouseEvent::InitMouseEvent(aType, aCanBubble, aCancelable,
                   aView, aDetail, aScreenX, aScreenY, aClientX, aClientY,
                   aCtrlKey, aAltKey, aShiftKey, aMetaKey, aButton,
                   aRelatedTarget);
   NS_ENSURE_SUCCESS(rv, rv);
 
   if (mEventIsInternal && mEvent) {
-    mEvent->AsDragEvent()->dataTransfer = aDataTransfer;
+    mEvent->AsDragEvent()->dataTransfer = dataTransfer;
   }
 
   return NS_OK;
 }
 
 NS_IMETHODIMP
 nsDOMDragEvent::GetDataTransfer(nsIDOMDataTransfer** aDataTransfer)
 {
   NS_IF_ADDREF(*aDataTransfer = GetDataTransfer());
   return NS_OK;
 }
 
-nsIDOMDataTransfer*
+DataTransfer*
 nsDOMDragEvent::GetDataTransfer()
 {
   // 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.
   if (!mEvent || mEvent->eventStructType != NS_DRAG_EVENT) {
     NS_WARNING("Tried to get dataTransfer from non-drag event!");
@@ -83,15 +112,15 @@ nsDOMDragEvent::GetDataTransfer()
     nsresult rv = nsContentUtils::SetDataTransferInEvent(dragEvent);
     NS_ENSURE_SUCCESS(rv, nullptr);
   }
 
   return dragEvent->dataTransfer;
 }
 
 nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult,
-                            mozilla::dom::EventTarget* aOwner,
+                            EventTarget* aOwner,
                             nsPresContext* aPresContext,
                             WidgetDragEvent* aEvent) 
 {
   nsDOMDragEvent* event = new nsDOMDragEvent(aOwner, aPresContext, aEvent);
   return CallQueryInterface(event, aInstancePtrResult);
 }
--- a/dom/events/nsDOMDragEvent.h
+++ b/dom/events/nsDOMDragEvent.h
@@ -6,16 +6,22 @@
 #ifndef nsDOMDragEvent_h__
 #define nsDOMDragEvent_h__
 
 #include "nsIDOMDragEvent.h"
 #include "nsDOMMouseEvent.h"
 #include "mozilla/dom/DragEventBinding.h"
 #include "mozilla/EventForwards.h"
 
+namespace mozilla {
+namespace dom {
+class DataTransfer;
+}
+}
+
 class nsDOMDragEvent : public nsDOMMouseEvent,
                        public nsIDOMDragEvent
 {
 public:
   nsDOMDragEvent(mozilla::dom::EventTarget* aOwner,
                  nsPresContext* aPresContext,
                  mozilla::WidgetDragEvent* aEvent);
 
@@ -26,34 +32,28 @@ public:
   NS_FORWARD_TO_NSDOMMOUSEEVENT
 
   virtual JSObject* WrapObject(JSContext* aCx,
                                JS::Handle<JSObject*> aScope) MOZ_OVERRIDE
   {
     return mozilla::dom::DragEventBinding::Wrap(aCx, aScope, this);
   }
 
-  nsIDOMDataTransfer* GetDataTransfer();
+  mozilla::dom::DataTransfer* GetDataTransfer();
 
   void InitDragEvent(const nsAString& aType,
                      bool aCanBubble, bool aCancelable,
                      nsIDOMWindow* aView, int32_t aDetail,
                      int32_t aScreenX, int32_t aScreenY,
-                     int32_t aClientX, int32_t aClientY, 
+                     int32_t aClientX, int32_t aClientY,
                      bool aCtrlKey, bool aAltKey, bool aShiftKey,
                      bool aMetaKey, uint16_t aButton,
                      mozilla::dom::EventTarget* aRelatedTarget,
-                     nsIDOMDataTransfer* aDataTransfer,
-                     mozilla::ErrorResult& aRv)
-  {
-    aRv = InitDragEvent(aType, aCanBubble, aCancelable,
-                        aView, aDetail, aScreenX, aScreenY, aClientX, aClientY,
-                        aCtrlKey, aAltKey, aShiftKey, aMetaKey, aButton,
-                        aRelatedTarget, aDataTransfer);
-  }
+                     mozilla::dom::DataTransfer* aDataTransfer,
+                     mozilla::ErrorResult& aError);
 };
 
 nsresult NS_NewDOMDragEvent(nsIDOMEvent** aInstancePtrResult,
                             mozilla::dom::EventTarget* aOwner,
                             nsPresContext* aPresContext,
                             mozilla::WidgetDragEvent* aEvent);
 
 #endif // nsDOMDragEvent_h__
--- a/dom/events/nsEventStateManager.cpp
+++ b/dom/events/nsEventStateManager.cpp
@@ -2106,35 +2106,42 @@ nsEventStateManager::GenerateDragGesture
     if (DeprecatedAbs(pt.x - mGestureDownPoint.x) > pixelThresholdX ||
         DeprecatedAbs(pt.y - mGestureDownPoint.y) > pixelThresholdY) {
       if (Prefs::ClickHoldContextMenu()) {
         // stop the click-hold before we fire off the drag gesture, in case
         // it takes a long time
         KillClickHoldTimer();
       }
 
+      nsCOMPtr<nsISupports> container = aPresContext->GetContainerWeak();
+      nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(container);
+      if (!window)
+        return;
+
       nsRefPtr<DataTransfer> dataTransfer =
-        new DataTransfer(NS_DRAGDROP_START, false, -1);
-      if (!dataTransfer)
-        return;
+        new DataTransfer(window, NS_DRAGDROP_START, false, -1);
 
       nsCOMPtr<nsISelection> selection;
       nsCOMPtr<nsIContent> eventContent, targetContent;
       mCurrentTarget->GetContentForEvent(aEvent, getter_AddRefs(eventContent));
       if (eventContent)
-        DetermineDragTarget(aPresContext, eventContent, dataTransfer,
+        DetermineDragTarget(window, eventContent, dataTransfer,
                             getter_AddRefs(selection), getter_AddRefs(targetContent));
 
       // Stop tracking the drag gesture now. This should stop us from
       // reentering GenerateDragGesture inside DOM event processing.
       StopTrackingDragGesture();
 
       if (!targetContent)
         return;
 
+      // Use our targetContent, now that we've determined it, as the
+      // parent object of the DataTransfer.
+      dataTransfer->SetParentObject(targetContent);
+
       sLastDragOverFrame = nullptr;
       nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetNearestWidget();
 
       // get the widget from the target frame
       WidgetDragEvent startEvent(aEvent->mFlags.mIsTrusted,
                                  NS_DRAGDROP_START, widget);
       FillInEventFromGestureDown(&startEvent);
 
@@ -2208,38 +2215,33 @@ nsEventStateManager::GenerateDragGesture
 
     // Now flush all pending notifications, for better responsiveness
     // while dragging.
     FlushPendingEvents(aPresContext);
   }
 } // GenerateDragGesture
 
 void
-nsEventStateManager::DetermineDragTarget(nsPresContext* aPresContext,
+nsEventStateManager::DetermineDragTarget(nsPIDOMWindow* aWindow,
                                          nsIContent* aSelectionTarget,
                                          DataTransfer* aDataTransfer,
                                          nsISelection** aSelection,
                                          nsIContent** aTargetNode)
 {
   *aTargetNode = nullptr;
 
-  nsCOMPtr<nsISupports> container = aPresContext->GetContainerWeak();
-  nsCOMPtr<nsPIDOMWindow> window = do_GetInterface(container);
-  if (!window)
-    return;
-
   // 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.
   // mGestureDownContent is the node where the mousedown event for the drag
   // occurred, and aSelectionTarget is the node to use when a selection is used
   bool canDrag;
   nsCOMPtr<nsIContent> dragDataNode;
   bool wasAlt = (mGestureModifiers & MODIFIER_ALT) != 0;
-  nsresult rv = nsContentAreaDragDrop::GetDragData(window, mGestureDownContent,
+  nsresult rv = nsContentAreaDragDrop::GetDragData(aWindow, mGestureDownContent,
                                                    aSelectionTarget, wasAlt,
                                                    aDataTransfer, &canDrag, aSelection,
                                                    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
@@ -2330,49 +2332,46 @@ nsEventStateManager::DoDefaultDragStart(
     aDataTransfer->GetMozItemCount(&count);
   if (!count)
     return false;
 
   // 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);
+  nsCOMPtr<Element> dragTarget = aDataTransfer->GetDragTarget();
   if (!dragTarget) {
     dragTarget = do_QueryInterface(aDragTarget);
     if (!dragTarget)
       return false;
   }
-  nsCOMPtr<nsIContent> content = do_QueryInterface(dragTarget);
 
   // check which drag effect should initially be used. If the effect was not
   // set, just use all actions, otherwise Windows won't allow a drop.
   uint32_t action;
   aDataTransfer->GetEffectAllowedInt(&action);
   if (action == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
     action = nsIDragService::DRAGDROP_ACTION_COPY |
              nsIDragService::DRAGDROP_ACTION_MOVE |
              nsIDragService::DRAGDROP_ACTION_LINK;
 
   // get any custom drag image that was set
   int32_t imageX, imageY;
-  nsIDOMElement* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
-
-  nsCOMPtr<nsISupportsArray> transArray = aDataTransfer->GetTransferables(dragTarget);
+  Element* dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
+
+  nsCOMPtr<nsISupportsArray> transArray =
+    aDataTransfer->GetTransferables(dragTarget->AsDOMNode());
   if (!transArray)
     return false;
 
   // 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), content,
+  NS_NewDOMDragEvent(getter_AddRefs(domEvent), dragTarget,
                      aPresContext, aDragEvent);
 
   nsCOMPtr<nsIDOMDragEvent> domDragEvent = do_QueryInterface(domEvent);
   // if creating a drag event failed, starting a drag session will
   // just fail.
 
   // Use InvokeDragSessionWithSelection if a selection is being dragged,
   // such that the image can be generated from the selected text. However,
@@ -2387,29 +2386,33 @@ nsEventStateManager::DoDefaultDragStart(
     // 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) {
-      if (content->NodeInfo()->Equals(nsGkAtoms::treechildren,
-                                      kNameSpaceID_XUL)) {
-        nsTreeBodyFrame* treeBody = do_QueryFrame(content->GetPrimaryFrame());
+      if (dragTarget->NodeInfo()->Equals(nsGkAtoms::treechildren,
+                                         kNameSpaceID_XUL)) {
+        nsTreeBodyFrame* treeBody =
+          do_QueryFrame(dragTarget->GetPrimaryFrame());
         if (treeBody) {
           treeBody->GetSelectionRegion(getter_AddRefs(region));
         }
       }
     }
 #endif
 
-    dragService->InvokeDragSessionWithImage(dragTarget, transArray,
-                                            region, action, dragImage,
-                                            imageX, imageY, domDragEvent,
+    dragService->InvokeDragSessionWithImage(dragTarget->AsDOMNode(), transArray,
+                                            region, action,
+                                            dragImage ? dragImage->AsDOMNode() :
+                                                        nullptr,
+                                            imageX,
+                                            imageY, domDragEvent,
                                             aDataTransfer);
   }
 
   return true;
 }
 
 nsresult
 nsEventStateManager::GetMarkupDocumentViewer(nsIMarkupDocumentViewer** aMv)
--- a/dom/events/nsEventStateManager.h
+++ b/dom/events/nsEventStateManager.h
@@ -732,17 +732,17 @@ protected:
    * 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
    * aSelection - [out] set to the selection to be dragged
    * aTargetNode - [out] the draggable node, or null if there isn't one
    */
-  void DetermineDragTarget(nsPresContext* aPresContext,
+  void DetermineDragTarget(nsPIDOMWindow* aWindow,
                            nsIContent* aSelectionTarget,
                            mozilla::dom::DataTransfer* aDataTransfer,
                            nsISelection** aSelection,
                            nsIContent** aTargetNode);
 
   /*
    * Perform the default handling for the dragstart/draggesture event and set up a
    * drag for aDataTransfer if it contains any data. Returns true if a drag has
--- a/dom/interfaces/events/nsIDOMDataTransfer.idl
+++ b/dom/interfaces/events/nsIDOMDataTransfer.idl
@@ -3,17 +3,17 @@
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #include "domstubs.idl"
 
 interface nsIVariant;
 interface nsIDOMFileList;
 
-[scriptable, uuid(7D73CFBF-EC30-4F8E-B6A4-BB31EB943580)]
+[scriptable, builtinclass, uuid(4ba241dd-a964-4077-bc30-515a657772e4)]
 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
@@ -224,19 +224,9 @@ interface nsIDOMDataTransfer : nsISuppor
    */
   [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, for the given event type and
-   * user cancelled flag. If isCrossDomainSubFrameDrop is set, then this is a
-   * cross-domain drop from a subframe where access to the data should be
-   * prevented.
-   */
-  [noscript] nsIDOMDataTransfer clone(in uint32_t aEventType,
-                                      in boolean aUserCancelled,
-                                      in boolean isCrossDomainSubFrameDrop);
 };
--- a/dom/webidl/ClipboardEvent.webidl
+++ b/dom/webidl/ClipboardEvent.webidl
@@ -5,18 +5,16 @@
  *
  * For more information on this interface please see
  * http://dev.w3.org/2006/webapi/clipops/#x5-clipboard-event-interfaces
  *
  * Copyright © 2012 W3C® (MIT, ERCIM, Keio), All Rights Reserved. W3C
  * liability, trademark and document use rules apply.
  */
 
-interface DataTransfer;
-
 [Constructor(DOMString type, optional ClipboardEventInit eventInitDict)]
 interface ClipboardEvent : Event
 {
   readonly attribute DataTransfer? clipboardData;
 };
 
 dictionary ClipboardEventInit : EventInit
 {
new file mode 100644
--- /dev/null
+++ b/dom/webidl/DataTransfer.webidl
@@ -0,0 +1,137 @@
+/* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this file,
+ * You can obtain one at http://mozilla.org/MPL/2.0/.
+ *
+ * The origin of this IDL file is:
+ * http://www.whatwg.org/specs/web-apps/current-work/#the-datatransfer-interface
+ */
+
+[ChromeConstructor(DOMString eventType, boolean isExternal)]
+interface DataTransfer {
+           attribute DOMString dropEffect;
+           attribute DOMString effectAllowed;
+
+  //readonly attribute DataTransferItemList items;
+
+  [Throws]
+  void setDragImage(Element image, long x, long y);
+
+  readonly attribute DOMStringList types;
+  [Throws]
+  DOMString getData(DOMString format);
+  [Throws]
+  void setData(DOMString format, DOMString data);
+  [Throws]
+  void clearData(optional DOMString format);
+  [Throws]
+  readonly attribute FileList files;
+};
+
+// Mozilla specific stuff
+partial interface DataTransfer {
+  /*
+   * 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
+   */
+  [Throws]
+  void addElement(Element element);
+
+  /**
+   * The number of items being dragged.
+   */
+  readonly attribute unsigned long mozItemCount;
+
+  /**
+   * Sets the drag cursor state. Primarily used to control the cursor during
+   * tab drags, but could be expanded to other uses. XXX Currently implemented
+   * on Win32 only.
+   *
+   * Possible values:
+   *  auto - use default system behavior.
+   *  default - set the cursor to an arrow during the drag operation.
+   *
+   * Values other than 'default' are indentical to setting mozCursor to
+   * 'auto'.
+   */
+  attribute DOMString mozCursor;
+
+  /**
+   * 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.
+   */
+  [Throws]
+  DOMStringList mozTypesAt(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
+   */
+  [Throws]
+  void mozClearDataAt(DOMString format, 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
+   */
+  [Throws]
+  void mozSetDataAt(DOMString format, any data, 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
+   */
+  [Throws]
+  any mozGetDataAt(DOMString format, unsigned long index);
+
+  /**
+   * Will be true when the user has cancelled the drag (typically by pressing
+   * Escape) and when the drag has been cancelled unexpectedly.  This will be
+   * false otherwise, including when the drop has been rejected by its target.
+   * This property is only relevant for the dragend event.
+   */
+  readonly attribute boolean mozUserCancelled;
+
+  /**
+   * The node that the mouse was pressed over to begin the drag. For external
+   * drags, or if the caller cannot access this node, this will be null.
+   */
+  readonly attribute Node? mozSourceNode;
+};
--- a/dom/webidl/DragEvent.webidl
+++ b/dom/webidl/DragEvent.webidl
@@ -1,16 +1,15 @@
 /* -*- Mode: IDL; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/.
  */
 
 interface WindowProxy;
-interface DataTransfer;
 
 interface DragEvent : MouseEvent
 {
   readonly attribute DataTransfer? dataTransfer;
 
   [Throws]
   void initDragEvent(DOMString type,
                      boolean canBubble,
--- a/dom/webidl/moz.build
+++ b/dom/webidl/moz.build
@@ -61,16 +61,17 @@ WEBIDL_FILES = [
     'CSS.webidl',
     'CSSPrimitiveValue.webidl',
     'CSSStyleDeclaration.webidl',
     'CSSStyleSheet.webidl',
     'CSSValue.webidl',
     'CSSValueList.webidl',
     'DataContainerEvent.webidl',
     'DataStore.webidl',
+    'DataTransfer.webidl',
     'DedicatedWorkerGlobalScope.webidl',
     'DelayNode.webidl',
     'DesktopNotification.webidl',
     'DeviceMotionEvent.webidl',
     'DeviceStorage.webidl',
     'Document.webidl',
     'DocumentFragment.webidl',
     'DocumentType.webidl',
--- a/editor/libeditor/base/nsEditorEventListener.cpp
+++ b/editor/libeditor/base/nsEditorEventListener.cpp
@@ -16,17 +16,17 @@
 #include "nsEventListenerManager.h"     // for nsEventListenerManager
 #include "nsFocusManager.h"             // for nsFocusManager
 #include "nsGkAtoms.h"                  // for nsGkAtoms, nsGkAtoms::input
 #include "nsIClipboard.h"               // for nsIClipboard, etc
 #include "nsIContent.h"                 // for nsIContent
 #include "nsIController.h"              // for nsIController
 #include "nsID.h"
 #include "nsIDOMDOMStringList.h"        // for nsIDOMDOMStringList
-#include "nsIDOMDataTransfer.h"         // for nsIDOMDataTransfer
+#include "mozilla/dom/DataTransfer.h"
 #include "nsIDOMDocument.h"             // for nsIDOMDocument
 #include "nsIDOMDragEvent.h"            // for nsIDOMDragEvent
 #include "nsIDOMElement.h"              // for nsIDOMElement
 #include "nsIDOMEvent.h"                // for nsIDOMEvent
 #include "nsIDOMEventTarget.h"          // for nsIDOMEventTarget
 #include "nsIDOMKeyEvent.h"             // for nsIDOMKeyEvent
 #include "nsIDOMMouseEvent.h"           // for nsIDOMMouseEvent
 #include "nsIDOMNode.h"                 // for nsIDOMNode
--- a/editor/libeditor/html/nsHTMLDataTransfer.cpp
+++ b/editor/libeditor/html/nsHTMLDataTransfer.cpp
@@ -30,17 +30,17 @@
 #include "nsGkAtoms.h"
 #include "nsHTMLEditUtils.h"
 #include "nsHTMLEditor.h"
 #include "nsIClipboard.h"
 #include "nsIContent.h"
 #include "nsIContentFilter.h"
 #include "nsIDOMComment.h"
 #include "nsIDOMDOMStringList.h"
-#include "nsIDOMDataTransfer.h"
+#include "mozilla/dom/DataTransfer.h"
 #include "nsIDOMDocument.h"
 #include "nsIDOMDocumentFragment.h"
 #include "nsIDOMElement.h"
 #include "nsIDOMHTMLAnchorElement.h"
 #include "nsIDOMHTMLEmbedElement.h"
 #include "nsIDOMHTMLFrameElement.h"
 #include "nsIDOMHTMLIFrameElement.h"
 #include "nsIDOMHTMLImageElement.h"
--- a/layout/forms/nsFileControlFrame.cpp
+++ b/layout/forms/nsFileControlFrame.cpp
@@ -5,16 +5,17 @@
 
 #include "nsFileControlFrame.h"
 
 #include "nsGkAtoms.h"
 #include "nsCOMPtr.h"
 #include "nsIDocument.h"
 #include "nsINodeInfo.h"
 #include "mozilla/dom/Element.h"
+#include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/HTMLButtonElement.h"
 #include "mozilla/dom/HTMLInputElement.h"
 #include "nsNodeInfoManager.h"
 #include "nsContentCreatorFunctions.h"
 #include "nsContentUtils.h"
 #include "nsEventStates.h"
 #include "nsIDOMDataTransfer.h"
 #include "nsIDOMDOMStringList.h"
--- a/widget/ContentEvents.h
+++ b/widget/ContentEvents.h
@@ -4,19 +4,19 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 
 #ifndef mozilla_ContentEvents_h__
 #define mozilla_ContentEvents_h__
 
 #include <stdint.h>
 
 #include "mozilla/BasicEvents.h"
+#include "mozilla/dom/DataTransfer.h"
 #include "mozilla/dom/EventTarget.h"
 #include "nsCOMPtr.h"
-#include "nsIDOMDataTransfer.h"
 #include "nsRect.h"
 #include "nsStringGlue.h"
 
 class nsIContent;
 
 namespace mozilla {
 
 /******************************************************************************
@@ -169,17 +169,17 @@ public:
     MOZ_ASSERT(eventStructType == NS_CLIPBOARD_EVENT,
                "Duplicate() must be overridden by sub class");
     InternalClipboardEvent* result = new InternalClipboardEvent(false, message);
     result->AssignClipboardEventData(*this, true);
     result->mFlags = mFlags;
     return result;
   }
 
-  nsCOMPtr<nsIDOMDataTransfer> clipboardData;
+  nsCOMPtr<dom::DataTransfer> clipboardData;
 
   void AssignClipboardEventData(const InternalClipboardEvent& aEvent,
                                 bool aCopyTargets)
   {
     AssignEventData(aEvent, aCopyTargets);
 
     clipboardData = aEvent.clipboardData;
   }
--- a/widget/MouseEvents.h
+++ b/widget/MouseEvents.h
@@ -5,18 +5,18 @@
 
 #ifndef mozilla_MouseEvents_h__
 #define mozilla_MouseEvents_h__
 
 #include <stdint.h>
 
 #include "mozilla/BasicEvents.h"
 #include "mozilla/MathAlgorithms.h"
+#include "mozilla/dom/DataTransfer.h"
 #include "nsCOMPtr.h"
-#include "nsIDOMDataTransfer.h"
 #include "nsIDOMMouseEvent.h"
 #include "nsIDOMWheelEvent.h"
 
 /******************************************************************************
  * nsDragDropEventStatus
  ******************************************************************************/
 
 enum nsDragDropEventStatus
@@ -305,17 +305,17 @@ public:
     // Not copying widget, it is a weak reference.
     WidgetDragEvent* result = new WidgetDragEvent(false, message, nullptr);
     result->AssignDragEventData(*this, true);
     result->mFlags = mFlags;
     return result;
   }
 
   // The dragging data.
-  nsCOMPtr<nsIDOMDataTransfer> dataTransfer;
+  nsCOMPtr<dom::DataTransfer> dataTransfer;
 
   // If this is true, user has cancelled the drag operation.
   bool userCancelled;
   // If this is true, the drag event's preventDefault() is called on content.
   bool mDefaultPreventedOnContent;
 
   // XXX Not tested by test_assign_event_data.html
   void AssignDragEventData(const WidgetDragEvent& aEvent, bool aCopyTargets)