Backed out changeset 342274a86c0d (bug 1162050)
authorCarsten "Tomcat" Book <cbook@mozilla.com>
Mon, 11 Jul 2016 11:18:44 +0200
changeset 346577 0e91dc7e8e439a5c1bb8d5bdddf88e02663216bf
parent 346576 0d73ad04a5366c51a9dd4354d97912ac2a2f9e00
child 346578 03e6db7e26dddd7513b773e9800d1ba33778cc3f
push id1230
push userjlund@mozilla.com
push dateMon, 31 Oct 2016 18:13:35 +0000
treeherdermozilla-release@5e06e3766db2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
bugs1162050
milestone50.0a1
backs out342274a86c0ddbcb0ba6ef462d6f3f78f1a4f3a4
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
Backed out changeset 342274a86c0d (bug 1162050)
dom/base/nsContentUtils.cpp
dom/base/nsGkAtomList.h
dom/events/DataTransfer.cpp
dom/events/EventNameList.h
dom/plugins/base/nsPluginInstanceOwner.cpp
widget/EventMessageList.h
widget/WidgetEventImpl.cpp
--- a/dom/base/nsContentUtils.cpp
+++ b/dom/base/nsContentUtils.cpp
@@ -5393,17 +5393,18 @@ nsContentUtils::SetDataTransferInEvent(W
     initialDataTransfer =
       new DataTransfer(aDragEvent->mTarget, aDragEvent->mMessage, 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->mMessage == eDrop) {
+  if (aDragEvent->mMessage == eDrop ||
+      aDragEvent->mMessage == eLegacyDragDrop) {
     isCrossDomainSubFrameDrop = CheckForSubFrameDrop(dragSession, aDragEvent);
   }
 
   // each event should use a clone of the original dataTransfer.
   initialDataTransfer->Clone(aDragEvent->mTarget, aDragEvent->mMessage,
                              aDragEvent->mUserCancelled,
                              isCrossDomainSubFrameDrop,
                              getter_AddRefs(aDragEvent->mDataTransfer));
@@ -5417,16 +5418,17 @@ nsContentUtils::SetDataTransferInEvent(W
   if (aDragEvent->mMessage == eDragEnter || aDragEvent->mMessage == eDragOver) {
     uint32_t action, effectAllowed;
     dragSession->GetDragAction(&action);
     aDragEvent->mDataTransfer->GetEffectAllowedInt(&effectAllowed);
     aDragEvent->mDataTransfer->SetDropEffectInt(
                                  FilterDropEffect(action, effectAllowed));
   }
   else if (aDragEvent->mMessage == eDrop ||
+           aDragEvent->mMessage == eLegacyDragDrop ||
            aDragEvent->mMessage == eDragEnd) {
     // For the drop and dragend events, set the drop effect based on the
     // last value that the dropEffect had. This will have been set in
     // EventStateManager::PostHandleEvent for the last dragenter or
     // dragover event.
     uint32_t dropEffect;
     initialDataTransfer->GetDropEffectInt(&dropEffect);
     aDragEvent->mDataTransfer->SetDropEffectInt(dropEffect);
--- a/dom/base/nsGkAtomList.h
+++ b/dom/base/nsGkAtomList.h
@@ -323,16 +323,17 @@ GK_ATOM(DOMAttrModified, "DOMAttrModifie
 GK_ATOM(DOMCharacterDataModified, "DOMCharacterDataModified")
 GK_ATOM(DOMNodeInserted, "DOMNodeInserted")
 GK_ATOM(DOMNodeInsertedIntoDocument, "DOMNodeInsertedIntoDocument")
 GK_ATOM(DOMNodeRemoved, "DOMNodeRemoved")
 GK_ATOM(DOMNodeRemovedFromDocument, "DOMNodeRemovedFromDocument")
 GK_ATOM(DOMSubtreeModified, "DOMSubtreeModified")
 GK_ATOM(double_, "double")
 GK_ATOM(drag, "drag")
+GK_ATOM(dragdrop, "dragdrop")
 GK_ATOM(dragend, "dragend")
 GK_ATOM(dragenter, "dragenter")
 GK_ATOM(dragevent, "dragevent")
 GK_ATOM(dragexit, "dragexit")
 GK_ATOM(draggable, "draggable")
 GK_ATOM(dragging, "dragging")
 GK_ATOM(dragleave, "dragleave")
 GK_ATOM(dragover, "dragover")
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -25,48 +25,39 @@
 #include "nsCRT.h"
 #include "nsIScriptObjectPrincipal.h"
 #include "nsIScriptContext.h"
 #include "nsIDocument.h"
 #include "nsIScriptGlobalObject.h"
 #include "nsVariant.h"
 #include "mozilla/dom/ContentChild.h"
 #include "mozilla/dom/DataTransferBinding.h"
+#include "mozilla/dom/DataTransferItemList.h"
 #include "mozilla/dom/Directory.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/FileList.h"
 #include "mozilla/dom/BindingUtils.h"
 #include "mozilla/dom/OSFileSystem.h"
 #include "mozilla/dom/Promise.h"
+#include "nsNetUtil.h"
 
 namespace mozilla {
 namespace dom {
 
-inline void
-ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
-                            TransferItem& aField,
-                            const char* aName,
-                            uint32_t aFlags = 0)
-{
-  ImplCycleCollectionTraverse(aCallback, aField.mData, aName, aFlags);
-}
-
 NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
 
 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
-  NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
   NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
   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(mFileList)
   NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
   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)
@@ -100,16 +91,17 @@ DataTransfer::DataTransfer(nsISupports* 
   , mReadOnly(true)
   , mIsExternal(aIsExternal)
   , mUserCancelled(false)
   , mIsCrossDomainSubFrameDrop(false)
   , mClipboardType(aClipboardType)
   , mDragImageX(0)
   , mDragImageY(0)
 {
+  mItems = new DataTransferItemList(this, aIsExternal, false /* aIsCrossDomainSubFrameDrop */);
   // 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 (aEventMessage == eCut ||
       aEventMessage == eCopy ||
       aEventMessage == eDragStart) {
     mReadOnly = false;
   } else if (mIsExternal) {
@@ -125,36 +117,40 @@ DataTransfer::DataTransfer(nsISupports* 
 DataTransfer::DataTransfer(nsISupports* aParent,
                            EventMessage aEventMessage,
                            const uint32_t aEffectAllowed,
                            bool aCursorState,
                            bool aIsExternal,
                            bool aUserCancelled,
                            bool aIsCrossDomainSubFrameDrop,
                            int32_t aClipboardType,
-                           nsTArray<nsTArray<TransferItem> >& aItems,
+                           DataTransferItemList* aItems,
                            Element* aDragImage,
                            uint32_t aDragImageX,
                            uint32_t aDragImageY)
   : mParent(aParent)
   , mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE)
   , mEffectAllowed(aEffectAllowed)
   , mEventMessage(aEventMessage)
   , mCursorState(aCursorState)
   , mReadOnly(true)
   , mIsExternal(aIsExternal)
   , mUserCancelled(aUserCancelled)
   , mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop)
   , mClipboardType(aClipboardType)
-  , mItems(aItems)
   , mDragImage(aDragImage)
   , mDragImageX(aDragImageX)
   , mDragImageY(aDragImageY)
 {
   MOZ_ASSERT(mParent);
+  MOZ_ASSERT(aItems);
+
+  // We clone the items array after everything else, so that it has a valid
+  // mParent value
+  mItems = aItems->Clone(this);
   // 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
   // dragstart event is the only time when items are
   // modifiable, but those events should have been using the first constructor
   // above.
   NS_ASSERTION(aEventMessage != eDragStart,
                "invalid event type for DataTransfer constructor");
 }
@@ -285,110 +281,90 @@ DataTransfer::GetMozUserCancelled(bool* 
 {
   *aUserCancelled = MozUserCancelled();
   return NS_OK;
 }
 
 FileList*
 DataTransfer::GetFiles(ErrorResult& aRv)
 {
-  return GetFileListInternal(aRv, nsContentUtils::SubjectPrincipal());
-}
-
-FileList*
-DataTransfer::GetFileListInternal(ErrorResult& aRv,
-                                  nsIPrincipal* aSubjectPrincipal)
-{
-  if (mEventMessage != eDrop && mEventMessage != ePaste) {
-    return nullptr;
-  }
-
-  if (!mFileList) {
-    mFileList = new FileList(static_cast<nsIDOMDataTransfer*>(this));
-
-    uint32_t count = mItems.Length();
-
-    for (uint32_t i = 0; i < count; i++) {
-      nsCOMPtr<nsIVariant> variant;
-      aRv = GetDataAtInternal(NS_ConvertUTF8toUTF16(kFileMime), i,
-                              aSubjectPrincipal, getter_AddRefs(variant));
-      if (NS_WARN_IF(aRv.Failed())) {
-        return nullptr;
-      }
-
-      if (!variant) {
-        continue;
-      }
-
-      nsCOMPtr<nsISupports> supports;
-      nsresult rv = variant->GetAsISupports(getter_AddRefs(supports));
-
-      if (NS_WARN_IF(NS_FAILED(rv))) {
-        continue;
-      }
-
-      nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
-
-      RefPtr<File> domFile;
-      if (file) {
-        MOZ_ASSERT(XRE_GetProcessType() == GeckoProcessType_Default,
-                   "nsIFile objects are not expected on the content process");
-
-        bool isDir;
-        aRv = file->IsDirectory(&isDir);
-        if (NS_WARN_IF(aRv.Failed())) {
-          return nullptr;
-        }
-
-        if (isDir) {
-          continue;
-        }
-
-        domFile = File::CreateFromFile(GetParentObject(), file);
-      } else {
-        nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(supports);
-        if (!blobImpl) {
-          continue;
-        }
-
-        MOZ_ASSERT(blobImpl->IsFile());
-
-        domFile = File::Create(GetParentObject(), blobImpl);
-        MOZ_ASSERT(domFile);
-      }
-
-      mFileList->Append(domFile);
-    }
-  }
-
-  return mFileList;
+  return mItems->Files();
 }
 
 NS_IMETHODIMP
 DataTransfer::GetFiles(nsIDOMFileList** aFileList)
 {
+  if (!aFileList) {
+    return NS_ERROR_FAILURE;
+  }
+
   ErrorResult rv;
-  NS_IF_ADDREF(*aFileList =
-    GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal()));
-  return rv.StealNSResult();
+  RefPtr<FileList> files = GetFiles(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
+  files.forget(aFileList);
+  return NS_OK;
 }
 
 already_AddRefed<DOMStringList>
-DataTransfer::Types() const
+DataTransfer::GetTypes(ErrorResult& aRv) const
 {
-  ErrorResult rv;
-  return MozTypesAt(0, rv);
+  RefPtr<DOMStringList> types = new DOMStringList();
+
+  const nsTArray<RefPtr<DataTransferItem>>* items = mItems->MozItemsAt(0);
+  if (!items || items->IsEmpty()) {
+    return types.forget();
+  }
+
+  bool addFile = false;
+  for (uint32_t i = 0; i < items->Length(); i++) {
+    DataTransferItem* item = items->ElementAt(i);
+    MOZ_ASSERT(item);
+
+    if (item->ChromeOnly() && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+      continue;
+    }
+
+    nsAutoString type;
+    item->GetType(type);
+    if (NS_WARN_IF(!types->Add(type))) {
+      aRv.Throw(NS_ERROR_FAILURE);
+      return nullptr;
+    }
+
+    if (!addFile) {
+      addFile = item->Kind() == DataTransferItem::KIND_FILE;
+    }
+  }
+
+  // If we have any files, we need to also add the "Files" type!
+  if (addFile && NS_WARN_IF(!types->Add(NS_LITERAL_STRING("Files")))) {
+    aRv.Throw(NS_ERROR_FAILURE);
+    return nullptr;
+  }
+
+  return types.forget();
 }
 
 NS_IMETHODIMP
 DataTransfer::GetTypes(nsISupports** aTypes)
 {
-  RefPtr<DOMStringList> types = Types();
+  if (NS_WARN_IF(!aTypes)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  ErrorResult rv;
+  RefPtr<DOMStringList> types = GetTypes(rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+
   types.forget(aTypes);
-
   return NS_OK;
 }
 
 void
 DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
                       ErrorResult& aRv)
 {
   // return an empty string if data for the format was not found
@@ -466,17 +442,17 @@ DataTransfer::SetData(const nsAString& a
 void
 DataTransfer::ClearData(const Optional<nsAString>& aFormat, ErrorResult& aRv)
 {
   if (mReadOnly) {
     aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
     return;
   }
 
-  if (mItems.Length() == 0) {
+  if (MozItemCount() == 0) {
     return;
   }
 
   if (aFormat.WasPassed()) {
     MozClearDataAtHelper(aFormat.Value(), 0, aRv);
   } else {
     MozClearDataAtHelper(EmptyString(), 0, aRv);
   }
@@ -555,37 +531,39 @@ DataTransfer::MozTypesAt(uint32_t aIndex
   if (aIndex > 0 &&
       (mEventMessage == eCut || mEventMessage == eCopy ||
        mEventMessage == ePaste)) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return nullptr;
   }
 
   RefPtr<DOMStringList> types = new DOMStringList();
-  if (aIndex < mItems.Length()) {
-    bool addFile = false;
+  if (aIndex < MozItemCount()) {
     // note that you can retrieve the types regardless of their principal
-    const nsTArray<TransferItem>& item = mItems[aIndex];
-    for (uint32_t i = 0; i < item.Length(); i++) {
-      const nsString& format = item[i].mFormat;
-      types->Add(format);
-      if (!addFile) {
-        addFile = format.EqualsASCII(kFileMime);
+    const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(aIndex);
+
+    bool addFile = false;
+    for (uint32_t i = 0; i < items.Length(); i++) {
+      if (items[i]->ChromeOnly() && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
+        continue;
+      }
+
+      nsAutoString type;
+      items[i]->GetType(type);
+      if (NS_WARN_IF(!types->Add(type))) {
+        aRv.Throw(NS_ERROR_FAILURE);
+        return nullptr;
+      }
+
+      if (items[i]->Kind() == DataTransferItem::KIND_FILE) {
+        addFile = true;
       }
     }
 
     if (addFile) {
-      // If this is a content caller, and a file is in the data transfer, remove
-      // the non-file types. This prevents alternate text forms of the file
-      // from being returned.
-      if (!nsContentUtils::LegacyIsCallerChromeOrNativeCode()) {
-        types->Clear();
-        types->Add(NS_LITERAL_STRING(kFileMime));
-      }
-
       types->Add(NS_LITERAL_STRING("Files"));
     }
   }
 
   return types.forget();
 }
 
 NS_IMETHODIMP
@@ -612,89 +590,84 @@ DataTransfer::GetDataAtInternal(const ns
                                 nsIVariant** aData)
 {
   *aData = nullptr;
 
   if (aFormat.IsEmpty()) {
     return NS_OK;
   }
 
-  if (aIndex >= mItems.Length()) {
+  if (aIndex >= MozItemCount()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
       (mEventMessage == eCut || mEventMessage == eCopy ||
        mEventMessage == ePaste)) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
-  nsTArray<TransferItem>& item = mItems[aIndex];
-
-  // If this is a content caller, and a file is in the data transfer, only
-  // return the file type.
-  if (!format.EqualsLiteral(kFileMime) &&
-      !nsContentUtils::IsSystemPrincipal(aSubjectPrincipal)) {
-    uint32_t count = item.Length();
-    for (uint32_t i = 0; i < count; i++) {
-      if (item[i].mFormat.EqualsLiteral(kFileMime)) {
-        return NS_OK;
-      }
-    }
-  }
-
   // Check if the caller is allowed to access the drag data. Callers with
   // chrome privileges can always read the data. During the
   // drop event, allow retrieving the data except in the case where the
   // source of the drag is in a child frame of the caller. In that case,
   // we only allow access to data of the same principal. During other events,
   // only allow access to the data with the same principal.
   bool checkFormatItemPrincipal = mIsCrossDomainSubFrameDrop ||
-      (mEventMessage != eDrop && mEventMessage != ePaste);
+      (mEventMessage != eDrop && mEventMessage != eLegacyDragDrop &&
+       mEventMessage != ePaste);
+  MOZ_ASSERT(aSubjectPrincipal);
 
-  uint32_t count = item.Length();
-  for (uint32_t i = 0; i < count; i++) {
-    TransferItem& formatitem = item[i];
-    if (formatitem.mFormat.Equals(format)) {
-      if (formatitem.mPrincipal && checkFormatItemPrincipal &&
-          !aSubjectPrincipal->Subsumes(formatitem.mPrincipal)) {
-        return NS_ERROR_DOM_SECURITY_ERR;
-      }
+  RefPtr<DataTransferItem> item = mItems->MozItemByTypeAt(format, aIndex);
+  if (!item) {
+    // The index exists but there's no data for the specified format, in this
+    // case we just return undefined
+    return NS_OK;
+  }
+
+  // If we have chrome only content, and we aren't chrome, don't allow access
+  if (!nsContentUtils::IsSystemPrincipal(aSubjectPrincipal) && item->ChromeOnly()) {
+    return NS_OK;
+  }
+
+  if (item->Principal() && checkFormatItemPrincipal &&
+      !aSubjectPrincipal->Subsumes(item->Principal())) {
+    return NS_ERROR_DOM_SECURITY_ERR;
+  }
 
-      if (!formatitem.mData) {
-        FillInExternalData(formatitem, aIndex);
-      } else {
-        nsCOMPtr<nsISupports> data;
-        formatitem.mData->GetAsISupports(getter_AddRefs(data));
-        // Make sure the code that is calling us is same-origin with the data.
-        nsCOMPtr<EventTarget> pt = do_QueryInterface(data);
-        if (pt) {
-          nsresult rv = NS_OK;
-          nsIScriptContext* c = pt->GetContextForEventHandlers(&rv);
-          NS_ENSURE_TRUE(c && NS_SUCCEEDED(rv), NS_ERROR_DOM_SECURITY_ERR);
-          nsIGlobalObject* go = c->GetGlobalObject();
-          NS_ENSURE_TRUE(go, NS_ERROR_DOM_SECURITY_ERR);
-          nsCOMPtr<nsIScriptObjectPrincipal> sp = do_QueryInterface(go);
-          MOZ_ASSERT(sp, "This cannot fail on the main thread.");
-          nsIPrincipal* dataPrincipal = sp->GetPrincipal();
-          NS_ENSURE_TRUE(dataPrincipal, NS_ERROR_DOM_SECURITY_ERR);
-          NS_ENSURE_TRUE(aSubjectPrincipal->Subsumes(dataPrincipal),
-                                                     NS_ERROR_DOM_SECURITY_ERR);
-        }
-      }
-      *aData = formatitem.mData;
-      NS_IF_ADDREF(*aData);
-      return NS_OK;
+  nsCOMPtr<nsIVariant> data = item->Data();
+  if (!data) {
+    return NS_OK;
+  }
+
+  nsCOMPtr<nsISupports> isupportsData;
+  nsresult rv = data->GetAsISupports(getter_AddRefs(isupportsData));
+
+  if (NS_SUCCEEDED(rv) && isupportsData) {
+    // Make sure the code that is calling us is same-origin with the data.
+    nsCOMPtr<EventTarget> pt = do_QueryInterface(isupportsData);
+    if (pt) {
+      nsresult rv = NS_OK;
+      nsIScriptContext* c = pt->GetContextForEventHandlers(&rv);
+      NS_ENSURE_TRUE(c && NS_SUCCEEDED(rv), NS_ERROR_DOM_SECURITY_ERR);
+      nsIGlobalObject* go = c->GetGlobalObject();
+      NS_ENSURE_TRUE(go, NS_ERROR_DOM_SECURITY_ERR);
+      nsCOMPtr<nsIScriptObjectPrincipal> sp = do_QueryInterface(go);
+      MOZ_ASSERT(sp, "This cannot fail on the main thread.");
+      nsIPrincipal* dataPrincipal = sp->GetPrincipal();
+      NS_ENSURE_TRUE(dataPrincipal, NS_ERROR_DOM_SECURITY_ERR);
+      NS_ENSURE_TRUE(aSubjectPrincipal->Subsumes(dataPrincipal), NS_ERROR_DOM_SECURITY_ERR);
     }
   }
 
+  data.forget(aData);
   return NS_OK;
 }
 
 void
 DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
                            uint32_t aIndex,
                            JS::MutableHandle<JS::Value> aRetval,
                            mozilla::ErrorResult& aRv)
@@ -728,17 +701,17 @@ DataTransfer::SetDataAtInternal(const ns
   }
 
   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()) {
+  if (aIndex > MozItemCount()) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
   }
 
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
       (mEventMessage == eCut || mEventMessage == eCopy ||
        mEventMessage == ePaste)) {
     return NS_ERROR_DOM_INDEX_SIZE_ERR;
@@ -786,79 +759,57 @@ 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()) {
+  if (aIndex >= MozItemCount()) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   // Only the first item is valid for clipboard events
   if (aIndex > 0 &&
       (mEventMessage == eCut || mEventMessage == eCopy ||
        mEventMessage == ePaste)) {
     aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
     return;
   }
 
   MozClearDataAtHelper(aFormat, aIndex, aRv);
+
+  // If we just cleared the 0-th index, and there are still more than 1 indexes
+  // remaining, MozClearDataAt should cause the 1st index to become the 0th
+  // index. This should _only_ happen when the MozClearDataAt function is
+  // explicitly called by script, as this behavior is inconsistent with spec.
+  // (however, so is the MozClearDataAt API)
+
+  if (aIndex == 0 && mItems->MozItemCount() > 1 &&
+      mItems->MozItemsAt(0)->Length() == 0) {
+    mItems->PopIndexZero();
+  }
 }
 
 void
 DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
                                    ErrorResult& aRv)
 {
   MOZ_ASSERT(!mReadOnly);
-  MOZ_ASSERT(aIndex < mItems.Length());
+  MOZ_ASSERT(aIndex < MozItemCount());
   MOZ_ASSERT(aIndex == 0 ||
              (mEventMessage != eCut && mEventMessage != eCopy &&
               mEventMessage != ePaste));
 
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
-  nsIPrincipal* principal = nsContentUtils::SubjectPrincipal();
-
-  // 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)) {
-        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);
-  }
+  mItems->MozRemoveByTypeAt(format, aIndex, aRv);
 }
 
 NS_IMETHODIMP
 DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex)
 {
   ErrorResult rv;
   MozClearDataAt(aFormat, aIndex, rv);
   return rv.StealNSResult();
@@ -901,29 +852,27 @@ DataTransfer::GetFilesAndDirectories(Err
   nsCOMPtr<nsIGlobalObject> global = parentNode->OwnerDoc()->GetScopeObject();
   MOZ_ASSERT(global);
   if (!global) {
     aRv.Throw(NS_ERROR_FAILURE);
     return nullptr;
   }
 
   RefPtr<Promise> p = Promise::Create(global, aRv);
-  if (aRv.Failed()) {
+  if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
-  if (!mFileList) {
-    GetFiles(aRv);
-    if (NS_WARN_IF(aRv.Failed())) {
-      return nullptr;
-    }
+  RefPtr<FileList> files = mItems->Files();
+  if (NS_WARN_IF(!files)) {
+    return nullptr;
   }
 
   Sequence<RefPtr<File>> filesSeq;
-  mFileList->ToSequence(filesSeq, aRv);
+  files->ToSequence(filesSeq, aRv);
   if (NS_WARN_IF(aRv.Failed())) {
     return nullptr;
   }
 
   p->MaybeResolve(filesSeq);
 
   return p.forget();
 }
@@ -959,24 +908,23 @@ DataTransfer::AddElement(nsIDOMElement* 
   return rv.StealNSResult();
 }
 
 nsresult
 DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
                     bool aUserCancelled, bool aIsCrossDomainSubFrameDrop,
                     DataTransfer** aNewDataTransfer)
 {
-  DataTransfer* newDataTransfer =
+  RefPtr<DataTransfer> newDataTransfer =
     new DataTransfer(aParent, aEventMessage, mEffectAllowed, mCursorState,
                      mIsExternal, aUserCancelled, aIsCrossDomainSubFrameDrop,
                      mClipboardType, mItems, mDragImage, mDragImageX,
                      mDragImageY);
 
-  *aNewDataTransfer = newDataTransfer;
-  NS_ADDREF(*aNewDataTransfer);
+  newDataTransfer.forget(aNewDataTransfer);
   return NS_OK;
 }
 
 already_AddRefed<nsISupportsArray>
 DataTransfer::GetTransferables(nsIDOMNode* aDragTarget)
 {
   MOZ_ASSERT(aDragTarget);
 
@@ -998,35 +946,35 @@ DataTransfer::GetTransferables(nsILoadCo
 {
 
   nsCOMPtr<nsISupportsArray> transArray =
     do_CreateInstance("@mozilla.org/supports-array;1");
   if (!transArray) {
     return nullptr;
   }
 
-  uint32_t count = mItems.Length();
+  uint32_t count = MozItemCount();
   for (uint32_t i = 0; i < count; i++) {
     nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
     if (transferable) {
       transArray->AppendElement(transferable);
     }
   }
 
   return transArray.forget();
 }
 
 already_AddRefed<nsITransferable>
 DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
 {
-  if (aIndex >= mItems.Length()) {
+  if (aIndex >= MozItemCount()) {
     return nullptr;
   }
 
-  nsTArray<TransferItem>& item = mItems[aIndex];
+  const nsTArray<RefPtr<DataTransferItem>>& item = *mItems->MozItemsAt(aIndex);
   uint32_t count = item.Length();
   if (!count) {
     return nullptr;
   }
 
   nsCOMPtr<nsITransferable> transferable =
     do_CreateInstance("@mozilla.org/widget/transferable;1");
   if (!transferable) {
@@ -1068,35 +1016,38 @@ DataTransfer::GetTransferable(uint32_t a
    *   <wide string> format
    *   <32-bit> length of data
    *   <wide string> data
    * A type of eCustomClipboardTypeId_None ends the list, without any following
    * data.
    */
   do {
     for (uint32_t f = 0; f < count; f++) {
-      const TransferItem& formatitem = item[f];
-      if (!formatitem.mData) { // skip empty items
+      RefPtr<DataTransferItem> formatitem = item[f];
+      if (!formatitem->Data()) { // skip empty items
         continue;
       }
 
+      nsAutoString type;
+      formatitem->GetType(type);
+
       // If the data is of one of the well-known formats, use it directly.
       bool isCustomFormat = true;
       for (uint32_t f = 0; f < ArrayLength(knownFormats); f++) {
-        if (formatitem.mFormat.EqualsASCII(knownFormats[f])) {
+        if (type.EqualsASCII(knownFormats[f])) {
           isCustomFormat = false;
           break;
         }
       }
 
       uint32_t lengthInBytes;
       nsCOMPtr<nsISupports> convertedData;
 
       if (handlingCustomFormats) {
-        if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData),
+        if (!ConvertFromVariant(formatitem->Data(), getter_AddRefs(convertedData),
                                 &lengthInBytes)) {
           continue;
         }
 
         // When handling custom types, add the data to the stream if this is a
         // custom type.
         if (isCustomFormat) {
           // If it isn't a string, just ignore it. The dataTransfer is cached in
@@ -1113,22 +1064,21 @@ DataTransfer::GetTransferable(uint32_t a
 
               nsCOMPtr<nsIOutputStream> outputStream;
               storageStream->GetOutputStream(0, getter_AddRefs(outputStream));
 
               stream = do_CreateInstance("@mozilla.org/binaryoutputstream;1");
               stream->SetOutputStream(outputStream);
             }
 
-            int32_t formatLength =
-              formatitem.mFormat.Length() * sizeof(nsString::char_type);
+            int32_t formatLength = type.Length() * sizeof(nsString::char_type);
 
             stream->Write32(eCustomClipboardTypeId_String);
             stream->Write32(formatLength);
-            stream->WriteBytes((const char *)formatitem.mFormat.get(),
+            stream->WriteBytes((const char *)type.get(),
                                formatLength);
             stream->Write32(lengthInBytes);
             stream->WriteBytes((const char *)data.get(), lengthInBytes);
 
             // The total size of the stream is the format length, the data
             // length, two integers to hold the lengths and one integer for the
             // string flag.
             totalCustomLength +=
@@ -1172,25 +1122,25 @@ DataTransfer::GetTransferable(uint32_t a
 
         added = true;
 
         // Clear the stream so it doesn't get used again.
         stream = nullptr;
       } else {
         // This is the second pass of the loop and a known type is encountered.
         // Add it as is.
-        if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData),
+        if (!ConvertFromVariant(formatitem->Data(), getter_AddRefs(convertedData),
                                 &lengthInBytes)) {
           continue;
         }
 
         // The underlying drag code uses text/unicode, so use that instead of
         // text/plain
         const char* format;
-        NS_ConvertUTF16toUTF8 utf8format(formatitem.mFormat);
+        NS_ConvertUTF16toUTF8 utf8format(type);
         if (utf8format.EqualsLiteral(kTextMime)) {
           format = kUnicodeMime;
         } else {
           format = utf8format.get();
         }
 
         // If a converter is set for a format, set the converter for the
         // transferable and don't add the item
@@ -1232,17 +1182,17 @@ DataTransfer::ConvertFromVariant(nsIVari
   *aLength = 0;
 
   uint16_t type;
   aVariant->GetDataType(&type);
   if (type == nsIDataType::VTYPE_INTERFACE ||
       type == nsIDataType::VTYPE_INTERFACE_IS) {
     nsCOMPtr<nsISupports> data;
     if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data)))) {
-     return false;
+      return false;
     }
 
     nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data);
     if (fdp) {
       // for flavour data providers, use kFlavorHasDataProvider (which has the
       // value 0) as the length.
       fdp.forget(aSupports);
       *aLength = nsITransferable::kFlavorHasDataProvider;
@@ -1288,83 +1238,61 @@ DataTransfer::ConvertFromVariant(nsIVari
   *aLength = str.Length() << 1;
 
   return true;
 }
 
 void
 DataTransfer::ClearAll()
 {
-  mItems.Clear();
+  mItems->ClearAllItems();
+}
+
+uint32_t
+DataTransfer::MozItemCount() const
+{
+  return mItems->MozItemCount();
 }
 
 nsresult
 DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
                                    nsIVariant* aData,
                                    uint32_t aIndex,
                                    nsIPrincipal* aPrincipal)
 {
   nsAutoString format;
   GetRealFormat(aFormat, format);
 
-  // check if the item for the format already exists. In that case,
-  // just replace it.
-  TransferItem* formatitem;
-  if (aIndex < mItems.Length()) {
-    nsTArray<TransferItem>& item = mItems[aIndex];
-    uint32_t count = item.Length();
-    for (uint32_t i = 0; i < count; i++) {
-      TransferItem& itemformat = item[i];
-      if (itemformat.mFormat.Equals(format)) {
-        // don't allow replacing data that has a stronger principal
-        bool subsumes;
-        if (itemformat.mPrincipal && aPrincipal &&
-            (NS_FAILED(aPrincipal->Subsumes(itemformat.mPrincipal,
-                                            &subsumes)) || !subsumes)) {
-          return NS_ERROR_DOM_SECURITY_ERR;
-        }
-
-        itemformat.mPrincipal = aPrincipal;
-        itemformat.mData = aData;
-        return NS_OK;
-      }
-    }
-
-    // add a new format
-    formatitem = item.AppendElement();
-  }
-  else {
-    NS_ASSERTION(aIndex == mItems.Length(), "Index out of range");
-
-    // add a new index
-    nsTArray<TransferItem>* item = mItems.AppendElement();
-    NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
-
-    formatitem = item->AppendElement();
-  }
-
-  NS_ENSURE_TRUE(formatitem, NS_ERROR_OUT_OF_MEMORY);
-
-  formatitem->mFormat = format;
-  formatitem->mPrincipal = aPrincipal;
-  formatitem->mData = aData;
-
-  return NS_OK;
+  ErrorResult rv;
+  RefPtr<DataTransferItem> item =
+    mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
+                                 /* aInsertOnly = */ false,
+                                 /* aHidden= */ false,
+                                 rv);
+  return rv.StealNSResult();
 }
 
 void
 DataTransfer::SetDataWithPrincipalFromOtherProcess(const nsAString& aFormat,
                                                    nsIVariant* aData,
                                                    uint32_t aIndex,
-                                                   nsIPrincipal* aPrincipal)
+                                                   nsIPrincipal* aPrincipal,
+                                                   bool aHidden)
 {
   if (aFormat.EqualsLiteral(kCustomTypesMime)) {
     FillInExternalCustomTypes(aData, aIndex, aPrincipal);
   } else {
-    SetDataWithPrincipal(aFormat, aData, aIndex, aPrincipal);
+    nsAutoString format;
+    GetRealFormat(aFormat, format);
+
+    ErrorResult rv;
+    RefPtr<DataTransferItem> item =
+      mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
+                                   /* aInsertOnly = */ false, aHidden, rv);
+    NS_WARN_IF(rv.Failed());
   }
 }
 
 void
 DataTransfer::GetRealFormat(const nsAString& aInFormat,
                             nsAString& aOutFormat) const
 {
   // treat text/unicode as equivalent to text/plain
@@ -1379,36 +1307,58 @@ DataTransfer::GetRealFormat(const nsAStr
   if (lowercaseFormat.EqualsLiteral("url")) {
     aOutFormat.AssignLiteral("text/uri-list");
     return;
   }
 
   aOutFormat.Assign(lowercaseFormat);
 }
 
-void
+nsresult
 DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex,
-                                nsIPrincipal* aPrincipal)
+                                nsIPrincipal* aPrincipal, bool aHidden)
 {
+  ErrorResult rv;
+  RefPtr<DataTransferItem> item;
+
   if (strcmp(aFormat, kUnicodeMime) == 0) {
-    SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nullptr, aIndex,
-                         aPrincipal);
-    return;
+    item = mItems->SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nullptr,
+                                        aIndex, aPrincipal, false, aHidden, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return rv.StealNSResult();
+    }
+    return NS_OK;
   }
 
   if (strcmp(aFormat, kURLDataMime) == 0) {
-    SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nullptr, aIndex,
-                         aPrincipal);
-    return;
+    item = mItems->SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nullptr,
+                                        aIndex, aPrincipal, false, aHidden, rv);
+    if (NS_WARN_IF(rv.Failed())) {
+      return rv.StealNSResult();
+    }
+    return NS_OK;
   }
 
-  SetDataWithPrincipal(NS_ConvertUTF8toUTF16(aFormat), nullptr, aIndex,
-                       aPrincipal);
+  nsAutoString format;
+  GetRealFormat(NS_ConvertUTF8toUTF16(aFormat), format);
+  item = mItems->SetDataWithPrincipal(format, nullptr, aIndex,
+                                      aPrincipal, false, aHidden, rv);
+  if (NS_WARN_IF(rv.Failed())) {
+    return rv.StealNSResult();
+  }
+  return NS_OK;
 }
 
+// there isn't a way to get a list of the formats that might be available on
+// all platforms, so just check for the types that can actually be imported
+// XXXndeakin there are some other formats but those are platform specific.
+const char* kFormats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime,
+                           kUnicodeMime, kPNGImageMime, kJPEGImageMime,
+                           kGIFImageMime };
+
 void
 DataTransfer::CacheExternalDragFormats()
 {
   // Called during the constructor to cache the formats available from an
   // external drag. The data associated with each format will be set to null.
   // This data will instead only be retrieved in FillInExternalDragData when
   // asked for, as it may be time consuming for the source application to
   // generate it.
@@ -1427,34 +1377,37 @@ DataTransfer::CacheExternalDragFormats()
   // all platforms, so just check for the types that can actually be imported
   // XXXndeakin there are some other formats but those are platform specific.
   const char* formats[] = { kFileMime, kHTMLMime, kRTFMime,
                             kURLMime, kURLDataMime, kUnicodeMime };
 
   uint32_t count;
   dragSession->GetNumDropItems(&count);
   for (uint32_t c = 0; c < count; c++) {
+    bool hasFileData = false;
+    dragSession->IsDataFlavorSupported(kFileMime, &hasFileData);
+
     // First, check for the special format that holds custom types.
     bool supported;
     dragSession->IsDataFlavorSupported(kCustomTypesMime, &supported);
     if (supported) {
       FillInExternalCustomTypes(c, sysPrincipal);
     }
 
     for (uint32_t f = 0; f < ArrayLength(formats); f++) {
       // IsDataFlavorSupported doesn't take an index as an argument and just
       // checks if any of the items support a particular flavor, even though
       // the GetData method does take an index. Here, we just assume that
       // every item being dragged has the same set of flavors.
       bool supported;
-      dragSession->IsDataFlavorSupported(formats[f], &supported);
+      dragSession->IsDataFlavorSupported(kFormats[f], &supported);
       // if the format is supported, add an item to the array with null as
       // the data. When retrieved, GetRealData will read the data.
       if (supported) {
-        CacheExternalData(formats[f], c, sysPrincipal);
+        CacheExternalData(kFormats[f], c, sysPrincipal, /* hidden = */ f && hasFileData);
       }
     }
   }
 }
 
 void
 DataTransfer::CacheExternalClipboardFormats()
 {
@@ -1470,151 +1423,75 @@ DataTransfer::CacheExternalClipboardForm
   if (!clipboard || mClipboardType < 0) {
     return;
   }
 
   nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
   nsCOMPtr<nsIPrincipal> sysPrincipal;
   ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
 
+  // Check if the clipboard has any files
+  bool hasFileData = false;
+  const char *fileMime[] = { kFileMime };
+  clipboard->HasDataMatchingFlavors(fileMime, 1, mClipboardType, &hasFileData);
+
   // there isn't a way to get a list of the formats that might be available on
   // all platforms, so just check for the types that can actually be imported.
   // Note that the loop below assumes that kCustomTypesMime will be first.
   const char* formats[] = { kCustomTypesMime, kFileMime, kHTMLMime, kRTFMime,
-                            kURLMime, kURLDataMime, kUnicodeMime };
+                            kURLMime, kURLDataMime, kUnicodeMime, kPNGImageMime,
+                            kJPEGImageMime, kGIFImageMime };
 
   for (uint32_t f = 0; f < mozilla::ArrayLength(formats); ++f) {
     // check each format one at a time
     bool supported;
     clipboard->HasDataMatchingFlavors(&(formats[f]), 1, mClipboardType,
                                       &supported);
     // if the format is supported, add an item to the array with null as
     // the data. When retrieved, GetRealData will read the data.
     if (supported) {
       if (f == 0) {
         FillInExternalCustomTypes(0, sysPrincipal);
       } else {
-        CacheExternalData(formats[f], 0, sysPrincipal);
+        // If we aren't the file data, and we have file data, we want to be hidden
+        CacheExternalData(formats[f], 0, sysPrincipal, /* hidden = */ f != 1 && hasFileData);
       }
     }
   }
 }
 
 void
-DataTransfer::FillInExternalData(TransferItem& aItem, uint32_t aIndex)
-{
-  NS_PRECONDITION(mIsExternal, "Not an external data transfer");
-
-  if (aItem.mData) {
-    return;
-  }
-
-  // only drag and paste events should be calling FillInExternalData
-  NS_ASSERTION(mEventMessage != eCut && mEventMessage != eCopy,
-               "clipboard event with empty data");
-
-  NS_ConvertUTF16toUTF8 utf8format(aItem.mFormat);
-  const char* format = utf8format.get();
-  if (strcmp(format, "text/plain") == 0) {
-    format = kUnicodeMime;
-  } else if (strcmp(format, "text/uri-list") == 0) {
-    format = kURLDataMime;
-  }
-
-  nsCOMPtr<nsITransferable> trans =
-    do_CreateInstance("@mozilla.org/widget/transferable;1");
-  if (!trans) {
-    return;
-  }
-
-  trans->Init(nullptr);
-  trans->AddDataFlavor(format);
-
-  if (mEventMessage == ePaste) {
-    MOZ_ASSERT(aIndex == 0, "index in clipboard must be 0");
-
-    nsCOMPtr<nsIClipboard> clipboard =
-      do_GetService("@mozilla.org/widget/clipboard;1");
-    if (!clipboard || mClipboardType < 0) {
-      return;
-    }
-
-    clipboard->GetData(trans, mClipboardType);
-  } else {
-    nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
-    if (!dragSession) {
-      return;
-    }
-
-#ifdef DEBUG
-    // Since this is an external drag, the source document will always be null.
-    nsCOMPtr<nsIDOMDocument> domDoc;
-    dragSession->GetSourceDocument(getter_AddRefs(domDoc));
-    MOZ_ASSERT(!domDoc);
-#endif
-
-    dragSession->GetData(trans, aIndex);
-  }
-
-  uint32_t length = 0;
-  nsCOMPtr<nsISupports> data;
-  trans->GetTransferData(format, getter_AddRefs(data), &length);
-  if (!data)
-    return;
-
-  RefPtr<nsVariantCC> variant = new nsVariantCC();
-
-  nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
-  if (supportsstr) {
-    nsAutoString str;
-    supportsstr->GetData(str);
-    variant->SetAsAString(str);
-  }
-  else {
-    nsCOMPtr<nsISupportsCString> supportscstr = do_QueryInterface(data);
-    if (supportscstr) {
-      nsAutoCString str;
-      supportscstr->GetData(str);
-      variant->SetAsACString(str);
-    } else {
-      variant->SetAsISupports(data);
-    }
-  }
-
-  aItem.mData = variant;
-}
-
-void
 DataTransfer::FillAllExternalData()
 {
   if (mIsExternal) {
-    for (uint32_t i = 0; i < mItems.Length(); ++i) {
-      nsTArray<TransferItem>& itemArray = mItems[i];
-      for (uint32_t j = 0; j < itemArray.Length(); ++j) {
-        if (!itemArray[j].mData) {
-          FillInExternalData(itemArray[j], i);
-        }
+    for (uint32_t i = 0; i < MozItemCount(); ++i) {
+      const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(i);
+      for (uint32_t j = 0; j < items.Length(); ++j) {
+        MOZ_ASSERT(items[j]->Index() == i);
+
+        items[j]->FillInExternalData();
       }
     }
   }
 }
 
 void
 DataTransfer::FillInExternalCustomTypes(uint32_t aIndex,
                                         nsIPrincipal* aPrincipal)
 {
-  TransferItem item;
-  item.mFormat.AssignLiteral(kCustomTypesMime);
+  RefPtr<DataTransferItem> item = new DataTransferItem(mItems,
+                                                       NS_LITERAL_STRING(kCustomTypesMime));
+  item->SetKind(DataTransferItem::KIND_STRING);
+  item->SetIndex(aIndex);
 
-  FillInExternalData(item, aIndex);
-  if (!item.mData) {
+  if (!item->Data()) {
     return;
   }
 
-  FillInExternalCustomTypes(item.mData, aIndex, aPrincipal);
+  FillInExternalCustomTypes(item->Data(), aIndex, aPrincipal);
 }
 
 void
 DataTransfer::FillInExternalCustomTypes(nsIVariant* aData, uint32_t aIndex,
                                         nsIPrincipal* aPrincipal)
 {
   char* chrs;
   uint32_t len = 0;
--- a/dom/events/EventNameList.h
+++ b/dom/events/EventNameList.h
@@ -769,16 +769,20 @@ NON_IDL_EVENT(broadcast,
 NON_IDL_EVENT(commandupdate,
               eXULCommandUpdate,
               EventNameType_XUL,
               eBasicEventClass)
 NON_IDL_EVENT(dragexit,
               eDragExit,
               EventNameType_XUL,
               eDragEventClass)
+NON_IDL_EVENT(dragdrop,
+              eLegacyDragDrop,
+              EventNameType_XUL,
+              eDragEventClass)
 NON_IDL_EVENT(overflow,
               eScrollPortOverflow,
               EventNameType_XUL,
               eBasicEventClass)
 NON_IDL_EVENT(underflow,
               eScrollPortUnderflow,
               EventNameType_XUL,
               eBasicEventClass)
--- a/dom/plugins/base/nsPluginInstanceOwner.cpp
+++ b/dom/plugins/base/nsPluginInstanceOwner.cpp
@@ -2901,16 +2901,17 @@ nsPluginInstanceOwner::Destroy()
   content->RemoveEventListener(NS_LITERAL_STRING("click"), this, false);
   content->RemoveEventListener(NS_LITERAL_STRING("dblclick"), this, false);
   content->RemoveEventListener(NS_LITERAL_STRING("mouseover"), this, false);
   content->RemoveEventListener(NS_LITERAL_STRING("mouseout"), this, false);
   content->RemoveEventListener(NS_LITERAL_STRING("keypress"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("keydown"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("keyup"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("drop"), this, true);
+  content->RemoveEventListener(NS_LITERAL_STRING("dragdrop"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("drag"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragenter"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragover"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragleave"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragexit"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragstart"), this, true);
   content->RemoveEventListener(NS_LITERAL_STRING("dragend"), this, true);
   content->RemoveSystemEventListener(NS_LITERAL_STRING("compositionstart"),
@@ -3299,16 +3300,17 @@ nsresult nsPluginInstanceOwner::Init(nsI
   aContent->AddEventListener(NS_LITERAL_STRING("mouseover"), this, false,
                              false);
   aContent->AddEventListener(NS_LITERAL_STRING("mouseout"), this, false,
                              false);
   aContent->AddEventListener(NS_LITERAL_STRING("keypress"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("keydown"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("keyup"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("drop"), this, true);
+  aContent->AddEventListener(NS_LITERAL_STRING("dragdrop"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("drag"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragenter"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragover"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragleave"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragexit"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragstart"), this, true);
   aContent->AddEventListener(NS_LITERAL_STRING("dragend"), this, true);
   aContent->AddSystemEventListener(NS_LITERAL_STRING("compositionstart"),
--- a/widget/EventMessageList.h
+++ b/widget/EventMessageList.h
@@ -126,16 +126,17 @@ NS_EVENT_MESSAGE(eFormInvalid)
 
 //Need separate focus/blur notifications for non-native widgets
 NS_EVENT_MESSAGE(eFocus)
 NS_EVENT_MESSAGE(eBlur)
 
 NS_EVENT_MESSAGE(eDragEnter)
 NS_EVENT_MESSAGE(eDragOver)
 NS_EVENT_MESSAGE(eDragExit)
+NS_EVENT_MESSAGE(eLegacyDragDrop)
 NS_EVENT_MESSAGE(eDrag)
 NS_EVENT_MESSAGE(eDragEnd)
 NS_EVENT_MESSAGE(eDragStart)
 NS_EVENT_MESSAGE(eDrop)
 NS_EVENT_MESSAGE(eDragLeave)
 NS_EVENT_MESSAGE_FIRST_LAST(eDragDropEvent, eDragEnter, eDragLeave)
 
 // XUL specific events
--- a/widget/WidgetEventImpl.cpp
+++ b/widget/WidgetEventImpl.cpp
@@ -208,16 +208,17 @@ WidgetEvent::HasMouseEventMessage() cons
 
 bool
 WidgetEvent::HasDragEventMessage() const
 {
   switch (mMessage) {
     case eDragEnter:
     case eDragOver:
     case eDragExit:
+    case eLegacyDragDrop:
     case eDrag:
     case eDragEnd:
     case eDragStart:
     case eDrop:
     case eDragLeave:
       return true;
     default:
       return false;