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 188086 a40bcf02bb6025f64ee57cf702c7926edc2f3782
parent 188085 6dacf81bb5255357276102eb305f57b2f3bfdd02
child 188087 023aed557989b2cf12825c82b701814ef1bf4b09
push id3503
push userraliiev@mozilla.com
push dateMon, 28 Apr 2014 18:51:11 +0000
treeherdermozilla-beta@c95ac01e332e [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmaug
bugs923054
milestone30.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
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)