Backed out changeset 5c836acf3197 (bug 906420) on developer request by baku

--HG--
extra : rebase_source : 9f109cdb706feef6d2bcd9a2e71f8c4d3c17ebdf
This commit is contained in:
Carsten "Tomcat" Book 2016-06-09 15:30:53 +02:00
parent cec713e0c7
commit afff301b32
23 changed files with 470 additions and 1716 deletions

View File

@ -691,8 +691,8 @@ nsCopySupport::FireClipboardEvent(EventMessage aEventMessage,
RefPtr<DataTransfer> clipboardData;
if (chromeShell || Preferences::GetBool("dom.event.clipboardevents.enabled", true)) {
clipboardData =
new DataTransfer(doc->GetScopeObject(), aEventMessage,
aEventMessage == ePaste, aClipboardType);
new DataTransfer(piWindow, aEventMessage, aEventMessage == ePaste,
aClipboardType);
nsEventStatus status = nsEventStatus_eIgnore;
InternalClipboardEvent evt(true, aEventMessage);

View File

@ -30,22 +30,30 @@
#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)
@ -53,6 +61,7 @@ NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
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)
@ -96,7 +105,6 @@ DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
, 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.
@ -123,7 +131,7 @@ DataTransfer::DataTransfer(nsISupports* aParent,
bool aUserCancelled,
bool aIsCrossDomainSubFrameDrop,
int32_t aClipboardType,
DataTransferItemList* aItems,
nsTArray<nsTArray<TransferItem> >& aItems,
Element* aDragImage,
uint32_t aDragImageX,
uint32_t aDragImageY)
@ -137,16 +145,12 @@ DataTransfer::DataTransfer(nsISupports* aParent,
, 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
// draggesture and dragstart events are the only times when items are
@ -288,76 +292,102 @@ DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
FileList*
DataTransfer::GetFiles(ErrorResult& aRv)
{
return mItems->Files();
return GetFileListInternal(aRv, nsContentUtils::SubjectPrincipal());
}
FileList*
DataTransfer::GetFileListInternal(ErrorResult& aRv,
nsIPrincipal* aSubjectPrincipal)
{
if (mEventMessage != eDrop &&
mEventMessage != eLegacyDragDrop &&
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;
}
NS_IMETHODIMP
DataTransfer::GetFiles(nsIDOMFileList** aFileList)
{
if (!aFileList) {
return NS_ERROR_FAILURE;
}
ErrorResult rv;
RefPtr<FileList> files = GetFiles(rv);
if (NS_WARN_IF(rv.Failed())) {
return rv.StealNSResult();
}
files.forget(aFileList);
return NS_OK;
NS_IF_ADDREF(*aFileList =
GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal()));
return rv.StealNSResult();
}
already_AddRefed<DOMStringList>
DataTransfer::GetTypes(ErrorResult& aRv) const
DataTransfer::Types() const
{
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);
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();
ErrorResult rv;
return MozTypesAt(0, rv);
}
NS_IMETHODIMP
DataTransfer::GetTypes(nsISupports** aTypes)
{
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();
}
RefPtr<DOMStringList> types = Types();
types.forget(aTypes);
return NS_OK;
}
@ -445,7 +475,7 @@ DataTransfer::ClearData(const Optional<nsAString>& aFormat, ErrorResult& aRv)
return;
}
if (MozItemCount() == 0) {
if (mItems.Length() == 0) {
return;
}
@ -534,18 +564,29 @@ DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv) const
}
RefPtr<DOMStringList> types = new DOMStringList();
if (aIndex < MozItemCount()) {
if (aIndex < mItems.Length()) {
bool addFile = false;
// note that you can retrieve the types regardless of their principal
const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(aIndex);
for (uint32_t i = 0; i < items.Length(); i++) {
nsAutoString type;
items[i]->GetType(type);
if (NS_WARN_IF(!types->Add(type))) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
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);
}
}
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();
@ -580,7 +621,7 @@ DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
return NS_OK;
}
if (aIndex >= MozItemCount()) {
if (aIndex >= mItems.Length()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
@ -594,11 +635,15 @@ DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
nsAutoString format;
GetRealFormat(aFormat, format);
const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(aIndex);
if (!aFormat.EqualsLiteral(kFileMime) &&
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)) {
for (uint32_t i = 0; i < items.Length(); ++i) {
if (items[i]->IsFile()) {
uint32_t count = item.Length();
for (uint32_t i = 0; i < count; i++) {
if (item[i].mFormat.EqualsLiteral(kFileMime)) {
return NS_OK;
}
}
@ -613,44 +658,43 @@ DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
bool checkFormatItemPrincipal = mIsCrossDomainSubFrameDrop ||
(mEventMessage != eDrop && mEventMessage != eLegacyDragDrop &&
mEventMessage != ePaste);
MOZ_ASSERT(aSubjectPrincipal);
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;
}
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;
}
if (item->Principal() && checkFormatItemPrincipal &&
!aSubjectPrincipal->Subsumes(item->Principal())) {
return NS_ERROR_DOM_SECURITY_ERR;
}
nsCOMPtr<nsIVariant> data = item->Data();
MOZ_ASSERT(data);
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);
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;
}
}
data.forget(aData);
return NS_OK;
}
@ -694,7 +738,7 @@ DataTransfer::SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
// 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 > MozItemCount()) {
if (aIndex > mItems.Length()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
@ -752,7 +796,7 @@ DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
return;
}
if (aIndex >= MozItemCount()) {
if (aIndex >= mItems.Length()) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
@ -766,17 +810,6 @@ DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
}
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
@ -784,7 +817,7 @@ DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
ErrorResult& aRv)
{
MOZ_ASSERT(!mReadOnly);
MOZ_ASSERT(aIndex < MozItemCount());
MOZ_ASSERT(aIndex < mItems.Length());
MOZ_ASSERT(aIndex == 0 ||
(mEventMessage != eCut && mEventMessage != eCopy &&
mEventMessage != ePaste));
@ -792,7 +825,40 @@ DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
nsAutoString format;
GetRealFormat(aFormat, format);
mItems->MozRemoveByTypeAt(format, aIndex, aRv);
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);
}
}
NS_IMETHODIMP
@ -845,17 +911,19 @@ DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
}
RefPtr<Promise> p = Promise::Create(global, aRv);
if (NS_WARN_IF(aRv.Failed())) {
if (aRv.Failed()) {
return nullptr;
}
RefPtr<FileList> files = mItems->Files();
if (NS_WARN_IF(!files)) {
return nullptr;
if (!mFileList) {
GetFiles(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
}
Sequence<RefPtr<File>> filesSeq;
files->ToSequence(filesSeq, aRv);
mFileList->ToSequence(filesSeq, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
@ -894,13 +962,14 @@ DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
bool aUserCancelled, bool aIsCrossDomainSubFrameDrop,
DataTransfer** aNewDataTransfer)
{
RefPtr<DataTransfer> newDataTransfer =
DataTransfer* newDataTransfer =
new DataTransfer(aParent, aEventMessage, mEffectAllowed, mCursorState,
mIsExternal, aUserCancelled, aIsCrossDomainSubFrameDrop,
mClipboardType, mItems, mDragImage, mDragImageX,
mDragImageY);
newDataTransfer.forget(aNewDataTransfer);
*aNewDataTransfer = newDataTransfer;
NS_ADDREF(*aNewDataTransfer);
return NS_OK;
}
@ -932,7 +1001,7 @@ DataTransfer::GetTransferables(nsILoadContext* aLoadContext)
return nullptr;
}
uint32_t count = MozItemCount();
uint32_t count = mItems.Length();
for (uint32_t i = 0; i < count; i++) {
nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
if (transferable) {
@ -946,11 +1015,11 @@ DataTransfer::GetTransferables(nsILoadContext* aLoadContext)
already_AddRefed<nsITransferable>
DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
{
if (aIndex >= MozItemCount()) {
if (aIndex >= mItems.Length()) {
return nullptr;
}
const nsTArray<RefPtr<DataTransferItem>>& item = *mItems->MozItemsAt(aIndex);
nsTArray<TransferItem>& item = mItems[aIndex];
uint32_t count = item.Length();
if (!count) {
return nullptr;
@ -1002,18 +1071,15 @@ DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
*/
do {
for (uint32_t f = 0; f < count; f++) {
RefPtr<DataTransferItem> formatitem = item[f];
if (!formatitem->Data()) { // skip empty items
const TransferItem& formatitem = item[f];
if (!formatitem.mData) { // 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 (type.EqualsASCII(knownFormats[f])) {
if (formatitem.mFormat.EqualsASCII(knownFormats[f])) {
isCustomFormat = false;
break;
}
@ -1023,7 +1089,7 @@ DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
nsCOMPtr<nsISupports> convertedData;
if (handlingCustomFormats) {
if (!ConvertFromVariant(formatitem->Data(), getter_AddRefs(convertedData),
if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData),
&lengthInBytes)) {
continue;
}
@ -1050,11 +1116,12 @@ DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
stream->SetOutputStream(outputStream);
}
int32_t formatLength = type.Length() * sizeof(nsString::char_type);
int32_t formatLength =
formatitem.mFormat.Length() * sizeof(nsString::char_type);
stream->Write32(eCustomClipboardTypeId_String);
stream->Write32(formatLength);
stream->WriteBytes((const char *)type.get(),
stream->WriteBytes((const char *)formatitem.mFormat.get(),
formatLength);
stream->Write32(lengthInBytes);
stream->WriteBytes((const char *)data.get(), lengthInBytes);
@ -1108,7 +1175,7 @@ DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
} else {
// This is the second pass of the loop and a known type is encountered.
// Add it as is.
if (!ConvertFromVariant(formatitem->Data(), getter_AddRefs(convertedData),
if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData),
&lengthInBytes)) {
continue;
}
@ -1116,7 +1183,7 @@ DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
// The underlying drag code uses text/unicode, so use that instead of
// text/plain
const char* format;
NS_ConvertUTF16toUTF8 utf8format(type);
NS_ConvertUTF16toUTF8 utf8format(formatitem.mFormat);
if (utf8format.EqualsLiteral(kTextMime)) {
format = kUnicodeMime;
} else {
@ -1168,7 +1235,7 @@ DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
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);
@ -1224,13 +1291,7 @@ DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
void
DataTransfer::ClearAll()
{
mItems->ClearAllItems();
}
uint32_t
DataTransfer::MozItemCount() const
{
return mItems->MozItemCount();
mItems.Clear();
}
nsresult
@ -1242,10 +1303,49 @@ DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
nsAutoString format;
GetRealFormat(aFormat, format);
ErrorResult rv;
RefPtr<DataTransferItem> item =
mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal, false, rv);
return rv.StealNSResult();
// 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;
}
void
@ -1302,12 +1402,6 @@ DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex,
aPrincipal);
}
// 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 };
void
DataTransfer::CacheExternalDragFormats()
{
@ -1349,11 +1443,11 @@ DataTransfer::CacheExternalDragFormats()
// 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(kFormats[f], &supported);
dragSession->IsDataFlavorSupported(formats[f], &supported);
// if the format is supported, add an item to the array with null as
// the data. When retrieved, GetRealData will read the data.
if (supported) {
CacheExternalData(kFormats[f], c, sysPrincipal);
CacheExternalData(formats[f], c, sysPrincipal);
}
}
}
@ -1402,16 +1496,100 @@ DataTransfer::CacheExternalClipboardFormats()
}
}
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 < 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();
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);
}
}
}
}
@ -1421,16 +1599,15 @@ void
DataTransfer::FillInExternalCustomTypes(uint32_t aIndex,
nsIPrincipal* aPrincipal)
{
RefPtr<DataTransferItem> item = new DataTransferItem(mItems,
NS_LITERAL_STRING(kCustomTypesMime));
item->SetKind(DataTransferItem::KIND_STRING);
item->SetIndex(aIndex);
TransferItem item;
item.mFormat.AssignLiteral(kCustomTypesMime);
if (!item->Data()) {
FillInExternalData(item, aIndex);
if (!item.mData) {
return;
}
FillInExternalCustomTypes(item->Data(), aIndex, aPrincipal);
FillInExternalCustomTypes(item.mData, aIndex, aPrincipal);
}
void

View File

@ -32,17 +32,28 @@ class EventStateManager;
namespace dom {
class DataTransferItem;
class DataTransferItemList;
class DOMStringList;
class Element;
class FileList;
class Promise;
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;
};
#define NS_DATATRANSFER_IID \
{ 0x6c5f90d1, 0xa886, 0x42c8, \
{ 0x85, 0x06, 0x10, 0xbe, 0x5c, 0x0d, 0xc6, 0x77 } }
{ 0x43ee0327, 0xde5d, 0x463d, \
{ 0x9b, 0xd0, 0xf1, 0x79, 0x09, 0x69, 0xf2, 0xfb } }
class DataTransfer final : public nsIDOMDataTransfer,
public nsWrapperCache
@ -77,7 +88,7 @@ protected:
bool aUserCancelled,
bool aIsCrossDomainSubFrameDrop,
int32_t aClipboardType,
DataTransferItemList* aItems,
nsTArray<nsTArray<TransferItem> >& aItems,
Element* aDragImage,
uint32_t aDragImageX,
uint32_t aDragImageY);
@ -87,6 +98,7 @@ protected:
static const char sEffects[8][9];
public:
// Constructor for DataTransfer.
//
// aIsExternal must only be true when used to create a dataTransfer for a
@ -137,7 +149,7 @@ public:
void SetDragImage(Element& aElement, int32_t aX, int32_t aY,
ErrorResult& aRv);
already_AddRefed<DOMStringList> GetTypes(ErrorResult& rv) const;
already_AddRefed<DOMStringList> Types() const;
void GetData(const nsAString& aFormat, nsAString& aData, ErrorResult& aRv);
@ -153,7 +165,10 @@ public:
void AddElement(Element& aElement, mozilla::ErrorResult& aRv);
uint32_t MozItemCount() const;
uint32_t MozItemCount() const
{
return mItems.Length();
}
void GetMozCursor(nsString& aCursor)
{
@ -195,14 +210,8 @@ public:
// a readonly dataTransfer cannot have new data added or existing data
// removed. Only the dropEffect and effectAllowed may be modified.
DataTransferItemList* Items() const { return mItems; }
bool IsReadOnly() const { return mReadOnly; }
void SetReadOnly() { mReadOnly = true; }
int32_t ClipboardType() const { return mClipboardType; }
EventMessage GetEventMessage() const { return mEventMessage; }
// converts the data into an array of nsITransferable objects to be used for
// drag and drop or clipboard operations.
already_AddRefed<nsISupportsArray> GetTransferables(nsIDOMNode* aDragTarget);
@ -251,12 +260,12 @@ public:
bool aUserCancelled, bool aIsCrossDomainSubFrameDrop,
DataTransfer** aResult);
protected:
// 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) const;
protected:
// caches text and uri-list data formats that exist in the drag service or
// clipboard for retrieval later.
void CacheExternalData(const char* aFormat, uint32_t aIndex,
@ -269,7 +278,14 @@ protected:
// caches the formats that exist in the clipboard
void CacheExternalClipboardFormats();
FileList* GetFilesInternal(ErrorResult& aRv, nsIPrincipal* aSubjectPrincipal);
// 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);
FileList* GetFileListInternal(ErrorResult& aRv,
nsIPrincipal* aSubjectPrincipal);
nsresult GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
nsIPrincipal* aSubjectPrincipal,
nsIVariant** aData);
@ -321,8 +337,12 @@ protected:
// drag and drop.
int32_t mClipboardType;
// The items contained with the DataTransfer
RefPtr<DataTransferItemList> mItems;
// array of items, each containing an array of format->data pairs
nsTArray<nsTArray<TransferItem> > mItems;
// array of files and directories, containing only the files present in the
// dataTransfer
RefPtr<FileList> mFileList;
// the target of the drag. The drag and dragend events will fire at this.
nsCOMPtr<mozilla::dom::Element> mDragTarget;

View File

@ -1,365 +0,0 @@
/* -*- Mode: C++; 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/. */
#include "DataTransferItem.h"
#include "DataTransferItemList.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EventForwards.h"
#include "mozilla/dom/DataTransferItemBinding.h"
#include "nsIClipboard.h"
#include "nsISupportsPrimitives.h"
#include "nsNetUtil.h"
#include "nsQueryObject.h"
#include "nsContentUtils.h"
#include "nsVariant.h"
namespace {
struct FileMimeNameData
{
const char* mMimeName;
const char* mFileName;
};
FileMimeNameData kFileMimeNameMap[] = {
{ kFileMime, "GenericFileName" },
};
already_AddRefed<mozilla::dom::File>
FileFromISupports(nsISupports* aSupports)
{
MOZ_ASSERT(aSupports);
nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(aSupports);
if (domBlob) {
// Get out the blob - this is OK, because nsIDOMBlob is a builtinclass
// and the only implementer is Blob.
mozilla::dom::Blob* blob = static_cast<mozilla::dom::Blob*>(domBlob.get());
return blob->ToFile();
}
return nullptr;
}
} // anonymous namespace
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DataTransferItem, mData,
mPrincipal, mParent)
NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransferItem)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransferItem)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransferItem)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
DataTransferItem::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return DataTransferItemBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<DataTransferItem>
DataTransferItem::Clone(DataTransferItemList* aParent) const
{
MOZ_ASSERT(aParent);
RefPtr<DataTransferItem> it = new DataTransferItem(aParent, mType);
// Copy over all of the fields
it->mKind = mKind;
it->mIndex = mIndex;
it->mData = mData;
it->mPrincipal = mPrincipal;
return it.forget();
}
void
DataTransferItem::SetType(const nsAString& aType)
{
mType = aType;
}
void
DataTransferItem::SetData(nsIVariant* aData)
{
if (!aData) {
// We are holding a temporary null which will later be filled.
// These are provided by the system, and have guaranteed properties about
// their kind based on their type.
MOZ_ASSERT(!mType.IsEmpty());
mKind = KIND_STRING;
for (uint32_t i = 0; i < ArrayLength(kFileMimeNameMap); ++i) {
if (mType.EqualsASCII(kFileMimeNameMap[i].mMimeName)) {
mKind = KIND_FILE;
break;
}
}
mData = nullptr;
return;
}
mKind = KIND_OTHER;
mData = aData;
nsCOMPtr<nsISupports> supports;
nsresult rv = aData->GetAsISupports(getter_AddRefs(supports));
if (NS_SUCCEEDED(rv) && supports) {
RefPtr<File> file = FileFromISupports(supports);
if (file) {
mKind = KIND_FILE;
return;
}
}
nsAutoString string;
// If we can't get the data type as a string, that means that the object
// should be considered to be of the "other" type. This is impossible
// through the APIs defined by the spec, but we provide extra Moz* APIs,
// which allow setting of non-string data. We determine whether we can
// consider it a string, by calling GetAsAString, and checking for success.
rv = aData->GetAsAString(string);
if (NS_SUCCEEDED(rv)) {
mKind = KIND_STRING;
}
}
void
DataTransferItem::FillInExternalData()
{
if (mData) {
return;
}
NS_ConvertUTF16toUTF8 utf8format(mType);
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 (NS_WARN_IF(!trans)) {
return;
}
trans->Init(nullptr);
trans->AddDataFlavor(format);
if (mParent->GetEventMessage() == ePaste) {
MOZ_ASSERT(mIndex == 0, "index in clipboard must be 0");
nsCOMPtr<nsIClipboard> clipboard =
do_GetService("@mozilla.org/widget/clipboard;1");
if (!clipboard || mParent->ClipboardType() < 0) {
return;
}
nsresult rv = clipboard->GetData(trans, mParent->ClipboardType());
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
} else {
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
if (!dragSession) {
return;
}
nsresult rv = dragSession->GetData(trans, mIndex);
if (NS_WARN_IF(NS_FAILED(rv))) {
return;
}
}
uint32_t length = 0;
nsCOMPtr<nsISupports> data;
nsresult rv = trans->GetTransferData(format, getter_AddRefs(data), &length);
if (NS_WARN_IF(NS_FAILED(rv) || !data)) {
return;
}
if (Kind() == KIND_FILE) {
// Because this is an external piece of data, mType is kFileMime. We want to
// convert whatever type happens to actually be stored into a dom::File.
RefPtr<File> file = FileFromISupports(data);
if (!file) {
if (nsCOMPtr<nsIFile> ifile = do_QueryInterface(data)) {
file = File::CreateFromFile(GetParentObject(), ifile);
} else if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(data)) {
// This consumes the stream object
ErrorResult rv;
file = CreateFileFromInputStream(GetParentObject(), stream, rv);
if (NS_WARN_IF(rv.Failed())) {
rv.SuppressException();
}
} else if (nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(data)) {
MOZ_ASSERT(blobImpl->IsFile());
file = File::Create(GetParentObject(), blobImpl);
}
}
MOZ_ASSERT(file, "Invalid format for Kind() == KIND_FILE");
data = do_QueryObject(file);
}
RefPtr<nsVariantCC> variant = new nsVariantCC();
nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
if (supportsstr) {
MOZ_ASSERT(Kind() == KIND_STRING);
nsAutoString str;
supportsstr->GetData(str);
variant->SetAsAString(str);
} else {
nsCOMPtr<nsISupportsCString> supportscstr = do_QueryInterface(data);
if (supportscstr) {
MOZ_ASSERT(Kind() == KIND_STRING);
nsAutoCString str;
supportscstr->GetData(str);
variant->SetAsACString(str);
} else {
MOZ_ASSERT(Kind() == KIND_FILE);
variant->SetAsISupports(data);
}
}
SetData(variant);
}
already_AddRefed<File>
DataTransferItem::GetAsFile(ErrorResult& aRv)
{
if (mKind != KIND_FILE) {
return nullptr;
}
nsIVariant* data = Data();
if (NS_WARN_IF(!data)) {
return nullptr;
}
nsCOMPtr<nsISupports> supports;
aRv = data->GetAsISupports(getter_AddRefs(supports));
MOZ_ASSERT(!aRv.Failed() && supports,
"Files should be stored with type dom::File!");
if (aRv.Failed() || !supports) {
return nullptr;
}
RefPtr<File> file = FileFromISupports(supports);
MOZ_ASSERT(file, "Files should be stored with type dom::File!");
if (!file) {
return nullptr;
}
// The File object should have been stored as a File in the nsIVariant. If it
// was stored as a Blob, with a file BlobImpl, we could still get to this
// point, except that file is a new File object, with a different identity
// then the original blob ibject. This should never happen so we assert
// against it.
MOZ_ASSERT(SameCOMIdentity(supports, NS_ISUPPORTS_CAST(nsIDOMBlob*, file)),
"Got back a new File object from FileFromISupports in GetAsFile!");
return file.forget();
}
already_AddRefed<File>
DataTransferItem::CreateFileFromInputStream(nsISupports* aParent,
nsIInputStream* aStream,
ErrorResult& aRv)
{
const char* key = nullptr;
for (uint32_t i = 0; i < ArrayLength(kFileMimeNameMap); ++i) {
if (mType.EqualsASCII(kFileMimeNameMap[i].mMimeName)) {
key = kFileMimeNameMap[i].mFileName;
break;
}
}
if (!key) {
MOZ_ASSERT_UNREACHABLE("Unsupported mime type");
key = "GenericFileName";
}
nsXPIDLString fileName;
aRv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
key, fileName);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
uint64_t available;
aRv = aStream->Available(&available);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
void* data = nullptr;
aRv = NS_ReadInputStreamToBuffer(aStream, &data, available);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
return File::CreateMemoryFile(aParent, data, available, fileName,
mType, PR_Now());
}
void
DataTransferItem::GetAsString(const RefPtr<FunctionStringCallback>& aCallback,
ErrorResult& aRv)
{
if (!aCallback || mKind != KIND_STRING) {
return;
}
nsIVariant* data = Data();
if (NS_WARN_IF(!data)) {
return;
}
nsAutoString stringData;
data->GetAsAString(stringData);
// Dispatch the callback to the main thread
class GASRunnable final : public Runnable
{
public:
GASRunnable(const RefPtr<FunctionStringCallback>& aCallback,
const nsAString& aStringData)
: mCallback(aCallback), mStringData(aStringData)
{}
NS_IMETHOD Run() override
{
ErrorResult rv;
mCallback->Call(mStringData, rv);
NS_WARN_IF(rv.Failed());
return rv.StealNSResult();
}
private:
RefPtr<FunctionStringCallback> mCallback;
nsString mStringData;
};
RefPtr<GASRunnable> runnable = new GASRunnable(aCallback, stringData);
nsresult rv = NS_DispatchToMainThread(runnable);
if (NS_FAILED(rv)) {
NS_WARNING("NS_DispatchToMainThread Failed in "
"DataTransferItem::GetAsString!");
}
}
} // namespace dom
} // namespace mozilla

View File

@ -1,130 +0,0 @@
/* -*- Mode: C++; 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/. */
#ifndef mozilla_dom_DataTransferItem_h
#define mozilla_dom_DataTransferItem_h
#include "mozilla/ErrorResult.h"
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DOMString.h"
#include "mozilla/dom/File.h"
namespace mozilla {
namespace dom {
class FunctionStringCallback;
class DataTransferItem final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DataTransferItem);
public:
// The spec only talks about the "file" and "string" kinds. Due to the Moz*
// APIs, it is possible to attach any type to a DataTransferItem, meaning that
// we can have other kinds then just FILE and STRING. These others are simply
// marked as "other" and can only be produced throug the Moz* APIs.
enum eKind {
KIND_FILE,
KIND_STRING,
KIND_OTHER,
};
DataTransferItem(DataTransferItemList* aParent, const nsAString& aType)
: mIndex(0), mKind(KIND_OTHER), mType(aType), mParent(aParent)
{}
already_AddRefed<DataTransferItem> Clone(DataTransferItemList* aParent) const;
virtual JSObject* WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto) override;
void GetAsString(const RefPtr<FunctionStringCallback>& aCallback,
ErrorResult& aRv);
void GetKind(nsAString& aKind) const
{
switch (mKind) {
case KIND_FILE:
aKind = NS_LITERAL_STRING("file");
return;
case KIND_STRING:
aKind = NS_LITERAL_STRING("string");
return;
default:
aKind = NS_LITERAL_STRING("other");
return;
}
}
void GetType(nsAString& aType) const
{
aType = mType;
}
void SetType(const nsAString& aType);
eKind Kind() const
{
return mKind;
}
void SetKind(eKind aKind)
{
mKind = aKind;
}
already_AddRefed<File> GetAsFile(ErrorResult& aRv);
DataTransferItemList* GetParentObject() const
{
return mParent;
}
nsIPrincipal* Principal() const
{
return mPrincipal;
}
void SetPrincipal(nsIPrincipal* aPrincipal)
{
mPrincipal = aPrincipal;
}
nsIVariant* Data()
{
if (!mData) {
FillInExternalData();
}
return mData;
}
void SetData(nsIVariant* aData);
uint32_t Index() const
{
return mIndex;
}
void SetIndex(uint32_t aIndex)
{
mIndex = aIndex;
}
void FillInExternalData();
private:
~DataTransferItem() {}
already_AddRefed<File> CreateFileFromInputStream(nsISupports* aParent,
nsIInputStream* aStream,
ErrorResult& aRv);
// The index in the 2d mIndexedItems array
uint32_t mIndex;
eKind mKind;
nsString mType;
nsCOMPtr<nsIVariant> mData;
nsCOMPtr<nsIPrincipal> mPrincipal;
RefPtr<DataTransferItemList> mParent;
};
} // namespace dom
} // namespace mozilla
#endif /* mozilla_dom_DataTransferItem_h */

View File

@ -1,585 +0,0 @@
/* -*- Mode: C++; 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/. */
#include "DataTransferItemList.h"
#include "nsContentUtils.h"
#include "nsIGlobalObject.h"
#include "nsIClipboard.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIScriptGlobalObject.h"
#include "nsIScriptContext.h"
#include "nsISupportsPrimitives.h"
#include "nsQueryObject.h"
#include "nsVariant.h"
#include "mozilla/ContentEvents.h"
#include "mozilla/EventForwards.h"
#include "mozilla/storage/Variant.h"
#include "mozilla/dom/DataTransferItemListBinding.h"
namespace mozilla {
namespace dom {
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DataTransferItemList, mParent, mItems,
mIndexedItems, mFiles)
NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransferItemList)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransferItemList)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransferItemList)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END
JSObject*
DataTransferItemList::WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto)
{
return DataTransferItemListBinding::Wrap(aCx, this, aGivenProto);
}
already_AddRefed<DataTransferItemList>
DataTransferItemList::Clone(DataTransfer* aParent) const
{
RefPtr<DataTransferItemList> list =
new DataTransferItemList(aParent, mIsExternal, mIsCrossDomainSubFrameDrop);
// We need to clone the mItems and mIndexedItems lists while keeping the same
// correspondences between the mIndexedItems and mItems lists (namely, if an
// item is in mIndexedItems, and mItems it must have the same new identity)
// First, we copy over indexedItems, and clone every entry. Then, we go over
// mItems. For every entry, we use its mIndex property to locate it in
// mIndexedItems on the original DataTransferItemList, and then copy over the
// reference from the same index pair on the new DataTransferItemList
list->mIndexedItems.SetLength(mIndexedItems.Length());
list->mItems.SetLength(mItems.Length());
// Copy over mIndexedItems, cloning every entry
for (uint32_t i = 0; i < mIndexedItems.Length(); i++) {
const nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[i];
nsTArray<RefPtr<DataTransferItem>>& newItems = list->mIndexedItems[i];
newItems.SetLength(items.Length());
for (uint32_t j = 0; j < items.Length(); j++) {
newItems[j] = items[j]->Clone(list);
}
}
// Copy over mItems, getting the actual entries from mIndexedItems
for (uint32_t i = 0; i < mItems.Length(); i++) {
uint32_t index = mItems[i]->Index();
MOZ_ASSERT(index < mIndexedItems.Length());
uint32_t subIndex = mIndexedItems[index].IndexOf(mItems[i]);
// Copy over the reference
list->mItems[i] = list->mIndexedItems[index][subIndex];
}
return list.forget();
}
void
DataTransferItemList::Remove(uint32_t aIndex, ErrorResult& aRv)
{
if (IsReadOnly()) {
aRv.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
return;
}
if (aIndex >= Length()) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
ClearDataHelper(mItems[aIndex], aIndex, -1, aRv);
}
DataTransferItem*
DataTransferItemList::IndexedGetter(uint32_t aIndex, bool& aFound, ErrorResult& aRv) const
{
if (aIndex >= mItems.Length()) {
aFound = false;
return nullptr;
}
RefPtr<DataTransferItem> item = mItems[aIndex];
// 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.
nsIPrincipal* principal = nullptr;
if (mIsCrossDomainSubFrameDrop) {
principal = nsContentUtils::SubjectPrincipal();
}
if (item->Principal() && principal &&
!principal->Subsumes(item->Principal())) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
aFound = false;
return nullptr;
}
nsCOMPtr<nsIVariant> variantData = item->Data();
nsCOMPtr<nsISupports> data;
if (variantData &&
NS_SUCCEEDED(variantData->GetAsISupports(getter_AddRefs(data))) &&
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);
if (NS_WARN_IF(NS_FAILED(rv) || !c)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsIGlobalObject* go = c->GetGlobalObject();
if (NS_WARN_IF(!go)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
nsCOMPtr<nsIScriptObjectPrincipal> sp = do_QueryInterface(go);
MOZ_ASSERT(sp, "This cannot fail on the main thread.");
nsIPrincipal* dataPrincipal = sp->GetPrincipal();
if (!principal) {
principal = nsContentUtils::SubjectPrincipal();
}
if (NS_WARN_IF(!dataPrincipal || !principal->Equals(dataPrincipal))) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
}
}
aFound = true;
return item;
}
uint32_t
DataTransferItemList::MozItemCount() const
{
uint32_t length = mIndexedItems.Length();
// XXX: Compat hack - Index 0 always exists due to changes in internals, but
// if it is empty, scripts using the moz* APIs should see it as not existing.
if (length == 1 && mIndexedItems[0].IsEmpty()) {
return 0;
}
return length;
}
void
DataTransferItemList::Clear(ErrorResult& aRv)
{
if (NS_WARN_IF(IsReadOnly())) {
return;
}
uint32_t count = Length();
for (uint32_t i = 0; i < count; i++) {
// We always remove the last item first, to avoid moving items around in
// memory as much
Remove(Length() - 1, aRv);
ENSURE_SUCCESS_VOID(aRv);
}
MOZ_ASSERT(Length() == 0);
}
DataTransferItem*
DataTransferItemList::Add(const nsAString& aData,
const nsAString& aType,
ErrorResult& aRv)
{
if (NS_WARN_IF(IsReadOnly())) {
return nullptr;
}
nsCOMPtr<nsIVariant> data(new storage::TextVariant(aData));
nsAutoString format;
mParent->GetRealFormat(aType, format);
// We add the textual data to index 0. We set aInsertOnly to true, as we don't
// want to update an existing entry if it is already present, as per the spec.
RefPtr<DataTransferItem> item =
SetDataWithPrincipal(format, data, 0,
nsContentUtils::SubjectPrincipal(),
true, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
MOZ_ASSERT(item->Kind() != DataTransferItem::KIND_FILE);
return item;
}
DataTransferItem*
DataTransferItemList::Add(File& aData, ErrorResult& aRv)
{
if (IsReadOnly()) {
return nullptr;
}
nsCOMPtr<nsISupports> supports = do_QueryObject(&aData);
nsCOMPtr<nsIWritableVariant> data = new nsVariant();
data->SetAsISupports(supports);
nsAutoString type;
aData.GetType(type);
// We need to add this as a new item, as multiple files can't exist in the
// same item in the Moz DataTransfer layout. It will be appended at the end of
// the internal specced layout.
uint32_t index = mIndexedItems.Length();
RefPtr<DataTransferItem> item =
SetDataWithPrincipal(type, data, index,
nsContentUtils::SubjectPrincipal(),
true, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
MOZ_ASSERT(item->Kind() == DataTransferItem::KIND_FILE);
return item;
}
FileList*
DataTransferItemList::Files()
{
if (!mFiles) {
mFiles = new FileList(static_cast<nsIDOMDataTransfer*>(mParent));
RegenerateFiles();
}
return mFiles;
}
void
DataTransferItemList::MozRemoveByTypeAt(const nsAString& aType,
uint32_t aIndex,
ErrorResult& aRv)
{
if (NS_WARN_IF(IsReadOnly() ||
aIndex >= mIndexedItems.Length())) {
return;
}
bool removeAll = aType.IsEmpty();
nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aIndex];
uint32_t count = items.Length();
// We remove the last item of the list repeatedly - that way we don't
// have to worry about modifying the loop iterator
if (removeAll) {
for (uint32_t i = 0; i < count; ++i) {
uint32_t index = items.Length() - 1;
MOZ_ASSERT(index == count - i - 1);
ClearDataHelper(items[index], -1, index, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return;
}
}
// items is no longer a valid reference, as removing the last element from
// it via ClearDataHelper invalidated it. so we can't MOZ_ASSERT that the
// length is now 0.
return;
}
for (uint32_t i = 0; i < count; ++i) {
nsAutoString type;
items[i]->GetType(type);
if (type == aType) {
ClearDataHelper(items[i], -1, i, aRv);
return;
}
}
}
DataTransferItem*
DataTransferItemList::MozItemByTypeAt(const nsAString& aType, uint32_t aIndex)
{
if (NS_WARN_IF(aIndex >= mIndexedItems.Length())) {
return nullptr;
}
uint32_t count = mIndexedItems[aIndex].Length();
for (uint32_t i = 0; i < count; i++) {
RefPtr<DataTransferItem> item = mIndexedItems[aIndex][i];
nsString type;
item->GetType(type);
if (type.Equals(aType)) {
return item;
}
}
return nullptr;
}
already_AddRefed<DataTransferItem>
DataTransferItemList::SetDataWithPrincipal(const nsAString& aType,
nsIVariant* aData,
uint32_t aIndex,
nsIPrincipal* aPrincipal,
bool aInsertOnly,
ErrorResult& aRv)
{
if (aIndex < mIndexedItems.Length()) {
nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aIndex];
uint32_t count = items.Length();
for (uint32_t i = 0; i < count; i++) {
RefPtr<DataTransferItem> item = items[i];
nsString type;
item->GetType(type);
if (type.Equals(aType)) {
if (NS_WARN_IF(aInsertOnly)) {
aRv.Throw(NS_ERROR_DOM_NOT_SUPPORTED_ERR);
return nullptr;
}
// don't allow replacing data that has a stronger principal
bool subsumes;
if (NS_WARN_IF(item->Principal() && aPrincipal &&
(NS_FAILED(aPrincipal->Subsumes(item->Principal(),
&subsumes))
|| !subsumes))) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return nullptr;
}
item->SetPrincipal(aPrincipal);
DataTransferItem::eKind oldKind = item->Kind();
item->SetData(aData);
if (aIndex != 0) {
// If the item changes from being a file to not a file or vice-versa,
// its presence in the mItems array may need to change.
if (item->Kind() == DataTransferItem::KIND_FILE &&
oldKind != DataTransferItem::KIND_FILE) {
// not file => file
mItems.AppendElement(item);
} else if (item->Kind() != DataTransferItem::KIND_FILE &&
oldKind == DataTransferItem::KIND_FILE) {
// file => not file
mItems.RemoveElement(item);
}
}
// Regenerate the Files array if we have modified a file's status
if (item->Kind() == DataTransferItem::KIND_FILE ||
oldKind == DataTransferItem::KIND_FILE) {
RegenerateFiles();
}
return item.forget();
}
}
} else {
// Make sure that we aren't adding past the end of the mIndexedItems array.
// XXX Should this be a MOZ_ASSERT instead?
aIndex = mIndexedItems.Length();
}
// Add the new item
RefPtr<DataTransferItem> item = AppendNewItem(aIndex, aType, aData, aPrincipal);
if (item->Kind() == DataTransferItem::KIND_FILE) {
RegenerateFiles();
}
return item.forget();
}
DataTransferItem*
DataTransferItemList::AppendNewItem(uint32_t aIndex,
const nsAString& aType,
nsIVariant* aData,
nsIPrincipal* aPrincipal)
{
if (mIndexedItems.Length() <= aIndex) {
MOZ_ASSERT(mIndexedItems.Length() == aIndex);
mIndexedItems.AppendElement();
}
RefPtr<DataTransferItem> item = new DataTransferItem(this, aType);
item->SetIndex(aIndex);
item->SetPrincipal(aPrincipal);
item->SetData(aData);
mIndexedItems[aIndex].AppendElement(item);
// We only want to add the item to the main mItems list if the index we are
// adding to is 0, or the item we are adding is a file. If we add an item
// which is not a file to a non-zero index, invariants could be broken.
// (namely the invariant that there are not 2 non-file entries in the items
// array with the same type)
if (item->Kind() == DataTransferItem::KIND_FILE || aIndex == 0) {
mItems.AppendElement(item);
}
return item;
}
const nsTArray<RefPtr<DataTransferItem>>*
DataTransferItemList::MozItemsAt(uint32_t aIndex) // -- INDEXED
{
if (aIndex >= mIndexedItems.Length()) {
return nullptr;
}
return &mIndexedItems[aIndex];
}
bool
DataTransferItemList::IsReadOnly() const
{
MOZ_ASSERT(mParent);
return mParent->IsReadOnly();
}
int32_t
DataTransferItemList::ClipboardType() const
{
MOZ_ASSERT(mParent);
return mParent->ClipboardType();
}
EventMessage
DataTransferItemList::GetEventMessage() const
{
MOZ_ASSERT(mParent);
return mParent->GetEventMessage();
}
void
DataTransferItemList::PopIndexZero()
{
MOZ_ASSERT(mIndexedItems.Length() > 1);
MOZ_ASSERT(mIndexedItems[0].IsEmpty());
mIndexedItems.RemoveElementAt(0);
// Update the index of every element which has now been shifted
for (uint32_t i = 0; i < mIndexedItems.Length(); i++) {
nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[i];
for (uint32_t j = 0; j < items.Length(); j++) {
items[j]->SetIndex(i);
}
}
}
void
DataTransferItemList::ClearAllItems()
{
// We always need to have index 0, so don't delete that one
mItems.Clear();
mIndexedItems.Clear();
mIndexedItems.SetLength(1);
// Re-generate files (into an empty list)
RegenerateFiles();
}
void
DataTransferItemList::ClearDataHelper(DataTransferItem* aItem,
uint32_t aIndexHint,
uint32_t aMozOffsetHint,
ErrorResult& aRv)
{
MOZ_ASSERT(aItem);
if (NS_WARN_IF(IsReadOnly())) {
return;
}
nsIPrincipal* principal = nsContentUtils::SubjectPrincipal();
if (aItem->Principal() && principal &&
!principal->Subsumes(aItem->Principal())) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
// Check if the aIndexHint is actually the index, and then remove the item
// from aItems
ErrorResult rv;
bool found;
if (IndexedGetter(aIndexHint, found, rv) == aItem) {
mItems.RemoveElementAt(aIndexHint);
} else {
mItems.RemoveElement(aItem);
}
rv.SuppressException();
// Check if the aMozIndexHint and aMozOffsetHint are actually the index and
// offset, and then remove them from mIndexedItems
MOZ_ASSERT(aItem->Index() < mIndexedItems.Length());
nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[aItem->Index()];
if (aMozOffsetHint < items.Length() && aItem == items[aMozOffsetHint]) {
items.RemoveElementAt(aMozOffsetHint);
} else {
items.RemoveElement(aItem);
}
// Check if we should remove the index. We never remove index 0.
if (items.Length() == 0 && aItem->Index() != 0) {
mIndexedItems.RemoveElementAt(aItem->Index());
// Update the index of every element which has now been shifted
for (uint32_t i = aItem->Index(); i < mIndexedItems.Length(); i++) {
nsTArray<RefPtr<DataTransferItem>>& items = mIndexedItems[i];
for (uint32_t j = 0; j < items.Length(); j++) {
items[j]->SetIndex(i);
}
}
}
// Give the removed item the invalid index
aItem->SetIndex(-1);
if (aItem->Kind() == DataTransferItem::KIND_FILE) {
RegenerateFiles();
}
}
void
DataTransferItemList::RegenerateFiles()
{
// We don't want to regenerate the files list unless we already have a files
// list. That way we can avoid the unnecessary work if the user never touches
// the files list.
if (mFiles) {
// We clear the list rather than performing smaller updates, because it
// simplifies the logic greatly on this code path, which should be very
// infrequently used.
mFiles->Clear();
uint32_t count = Length();
for (uint32_t i = 0; i < count; i++) {
ErrorResult rv;
bool found;
RefPtr<DataTransferItem> item = IndexedGetter(i, found, rv);
if (NS_WARN_IF(!found || rv.Failed())) {
continue;
}
if (item->Kind() == DataTransferItem::KIND_FILE) {
RefPtr<File> file = item->GetAsFile(rv);
if (NS_WARN_IF(rv.Failed() || !file)) {
continue;
}
mFiles->Append(file);
}
}
}
}
} // namespace mozilla
} // namespace dom

View File

@ -1,116 +0,0 @@
/* -*- Mode: C++; 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/. */
#ifndef mozilla_dom_DataTransferItemList_h
#define mozilla_dom_DataTransferItemList_h
#include "mozilla/dom/DataTransfer.h"
#include "mozilla/dom/DataTransferItem.h"
#include "mozilla/dom/FileList.h"
namespace mozilla {
namespace dom {
class DataTransferItem;
class DataTransferItemList final : public nsISupports
, public nsWrapperCache
{
public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(DataTransferItemList);
DataTransferItemList(DataTransfer* aParent, bool aIsExternal,
bool aIsCrossDomainSubFrameDrop)
: mParent(aParent)
, mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop)
, mIsExternal(aIsExternal)
{
// We always allocate an index 0 in our DataTransferItemList. This is done
// in order to maintain the invariants according to the spec. Mainly, within
// the spec's list, there is intended to be a single copy of each mime type,
// for string typed items. File typed items are allowed to have duplicates.
// In the old moz* system, this was modeled by having multiple indexes, each
// of which was independent. Files were fetched from all indexes, but
// strings were only fetched from the first index. In order to maintain this
// correlation and avoid breaking code with the new changes, index 0 is now
// always present and used to store strings, and all file items are given
// their own index starting at index 1.
mIndexedItems.SetLength(1);
}
already_AddRefed<DataTransferItemList> Clone(DataTransfer* aParent) const;
virtual JSObject* WrapObject(JSContext* aCx,
JS::Handle<JSObject*> aGivenProto) override;
uint32_t Length() const
{
return mItems.Length();
};
DataTransferItem* Add(const nsAString& aData, const nsAString& aType,
ErrorResult& rv);
DataTransferItem* Add(File& aData, ErrorResult& aRv);
void Remove(uint32_t aIndex, ErrorResult& aRv);
DataTransferItem* IndexedGetter(uint32_t aIndex, bool& aFound,
ErrorResult& aRv) const;
void Clear(ErrorResult& aRv);
DataTransfer* GetParentObject() const
{
return mParent;
}
// Accessors for data from ParentObject
bool IsReadOnly() const;
int32_t ClipboardType() const;
EventMessage GetEventMessage() const;
already_AddRefed<DataTransferItem>
SetDataWithPrincipal(const nsAString& aType, nsIVariant* aData,
uint32_t aIndex, nsIPrincipal* aPrincipal,
bool aInsertOnly, ErrorResult& aRv);
FileList* Files();
// Moz-style helper methods for interacting with the stored data
void MozRemoveByTypeAt(const nsAString& aType, uint32_t aIndex,
ErrorResult& aRv);
DataTransferItem* MozItemByTypeAt(const nsAString& aType, uint32_t aIndex);
const nsTArray<RefPtr<DataTransferItem>>* MozItemsAt(uint32_t aIndex);
uint32_t MozItemCount() const;
// Causes everything in indexes above 0 to shift down one index.
void PopIndexZero();
// Delete every item in the DataTransferItemList, without checking for
// permissions or read-only status (for internal use only).
void ClearAllItems();
private:
void ClearDataHelper(DataTransferItem* aItem, uint32_t aIndexHint,
uint32_t aMozOffsetHint, ErrorResult& aRv);
DataTransferItem* AppendNewItem(uint32_t aIndex, const nsAString& aType,
nsIVariant* aData, nsIPrincipal* aPrincipal);
void RegenerateFiles();
~DataTransferItemList() {}
RefPtr<DataTransfer> mParent;
bool mIsCrossDomainSubFrameDrop;
bool mIsExternal;
RefPtr<FileList> mFiles;
nsTArray<RefPtr<DataTransferItem>> mItems;
nsTArray<nsTArray<RefPtr<DataTransferItem>>> mIndexedItems;
};
} // namespace dom
} // namespace mozilla
#endif // mozilla_dom_DataTransferItemList_h

View File

@ -1812,7 +1812,7 @@ EventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
}
// now that the dataTransfer has been updated in the dragstart and
// draggesture events, make it readonly so that the data doesn't
// draggesture events, make it read only so that the data doesn't
// change during the drag.
dataTransfer->SetReadOnly();

View File

@ -44,8 +44,6 @@ EXPORTS.mozilla.dom += [
'CustomEvent.h',
'DataContainerEvent.h',
'DataTransfer.h',
'DataTransferItem.h',
'DataTransferItemList.h',
'DeviceMotionEvent.h',
'DragEvent.h',
'Event.h',
@ -124,8 +122,6 @@ UNIFIED_SOURCES += [
# nsEventStateManager.cpp should be built separately because of Mac OS X headers.
SOURCES += [
'DataTransferItem.cpp',
'DataTransferItemList.cpp',
'EventStateManager.cpp',
]

View File

@ -25,4 +25,3 @@ support-files =
[test_bug1128787-2.html]
[test_bug1128787-3.html]
[test_eventctors.xul]
[test_DataTransferItemList.html]

View File

@ -1,227 +0,0 @@
<html>
<head>
<title>Tests for the DatTransferItemList object</title>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/EventUtils.js"></script>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/SpawnTask.js"></script>
<script type="text/javascript" src="chrome://mochikit/content/tests/SimpleTest/ChromeUtils.js"></script>
<link rel="stylesheet" type="text/css" href="chrome://mochikit/content/tests/SimpleTest/test.css">
</head>
<body style="height: 300px; overflow: auto;">
<p id="display"> </p>
<img id="image" draggable="true" src="data:image/png,%89PNG%0D%0A%1A%0A%00%00%00%0DIHDR%00%00%00%18%00%00%00%18%02%03%00%00%00%9D%19%D5k%00%00%00%04gAMA%00%00%B1%8F%0B%FCa%05%00%00%00%0CPLTE%FF%FF%FF%FF%FF%FF%F7%DC%13%00%00%00%03%80%01X%00%00%00%01tRNS%08N%3DPT%00%00%00%01bKGD%00%88%05%1DH%00%00%00%09pHYs%00%00%0B%11%00%00%0B%11%01%7Fd_%91%00%00%00%07tIME%07%D2%05%0C%14%0C%0D%D8%3F%1FQ%00%00%00%5CIDATx%9C%7D%8E%CB%09%C0%20%10D%07r%B7%20%2F%E9wV0%15h%EA%D9%12D4%BB%C1x%CC%5C%1E%0C%CC%07%C0%9C0%9Dd7()%C0A%D3%8D%E0%B8%10%1DiCHM%D0%AC%D2d%C3M%F1%B4%E7%FF%10%0BY%AC%25%93%CD%CBF%B5%B2%C0%3Alh%CD%AE%13%DF%A5%F7%E0%03byW%09A%B4%F3%E2%00%00%00%00IEND%AEB%60%82">
<div id="over" "style="width: 100px; height: 100px; border: 2px black dashed;">
drag over here
</div>
<script>
function spin() {
// Defer to the event loop twice to wait for any events to be flushed out.
return new Promise(function(a) {
SimpleTest.executeSoon(function() {
SimpleTest.executeSoon(a)
});
});
}
add_task(function* () {
yield spin();
var draggable = document.getElementById('image');
var over = document.getElementById('over');
var dragstartFired = 0;
draggable.addEventListener('dragstart', onDragStart);
function onDragStart(e) {
draggable.removeEventListener('dragstart', onDragStart);
var dt = e.dataTransfer;
dragstartFired++;
ok(true, "dragStart event fired");
var dtList = e.dataTransfer.items;
ok(dtList instanceof DataTransferItemList,
"DataTransfer.items returns a DataTransferItemList");
for (var i = 0; i < dtList.length; i++) {
var item = dtList[i];
ok(item instanceof DataTransferItem,
"operator[] returns DataTransferItem objects");
if (item.kind == "file") {
var file = item.getAsFile();
ok(file instanceof File, "getAsFile() returns File objects");
}
}
dtList.clear();
is(dtList.length, 0, "after .clear() DataTransferItemList should be empty");
dtList.add("this is some text", "text/plain");
dtList.add("<a href='www.mozilla.org'>this is a link</a>", "text/html");
dtList.add("http://www.mozilla.org", "text/uri-list");
dtList.add("this is custom-data", "custom-data");
var file = new File(['<a id="a"><b id="b">hey!</b></a>'], "myfile.html",
{type: "text/html"});
dtList.add(file);
checkTypes(["text/plain", "text/html", "text/uri-list", "custom-data", "text/html"],
dtList, "DataTransferItemList.add test");
var files = e.dataTransfer.files;
is(files.length, 1, "DataTransfer.files should contain the one file we added earlier");
is(files[0], file, "It should be the same file as the file we originally created");
is(file, e.dataTransfer.mozGetDataAt("text/html", 1),
"It should be stored in index 1 for mozGetDataAt");
var file2 = new File(['<a id="c"><b id="d">yo!</b></a>'], "myotherfile.html",
{type: "text/html"});
dtList.add(file2);
is(files.length, 2, "The files property should have been updated in place");
is(files[1], file2, "It should be the same file as the file we originally created");
is(file2, e.dataTransfer.mozGetDataAt("text/html", 2),
"It should be stored in index 2 for mozGetDataAt");
var oldLength = dtList.length;
var randomString = "foo!";
e.dataTransfer.mozSetDataAt("random/string", randomString, 3);
is(oldLength, dtList.length,
"Adding a non-file entry to a non-zero index should not add an item to the items list");
var file3 = new File(['<a id="e"><b id="f">heya!</b></a>'], "yetanotherfile.html",
{type: "text/html"});
e.dataTransfer.mozSetDataAt("random/string", file3, 3);
is(oldLength + 1, dtList.length,
"Replacing the entry with a file should add it to the list!");
is(dtList[oldLength].getAsFile(), file3, "It should be stored in the last index as a file");
is(dtList[oldLength].type, "random/string", "It should have the correct type");
is(dtList[oldLength].kind, "file", "It should have the correct kind");
is(files[files.length - 1], file3, "It should also be in the files list");
oldLength = dtList.length;
var nonstring = {};
e.dataTransfer.mozSetDataAt("jsobject", nonstring, 0);
is(oldLength + 1, dtList.length,
"Adding a non-string object using the mozAPIs to index 0 should add an item to the dataTransfer");
is(dtList[oldLength].type, "jsobject", "It should have the correct type");
is(dtList[oldLength].kind, "other", "It should have the correct kind");
// Clear the event's data and get it set up so we can read it later!
dtList.clear();
dtList.add(file);
dtList.add("this is some text", "text/plain");
is(e.dataTransfer.mozGetDataAt("text/html", 1), file);
}
var getAsStringCalled = 0;
var dragenterFired = 0;
over.addEventListener('dragenter', onDragEnter);
function onDragEnter(e) {
over.removeEventListener('dragenter', onDragEnter);
var dt = e.dataTransfer;
dragenterFired++;
readOnly(e);
}
var dropFired = 0;
over.addEventListener('drop', onDrop);
function onDrop(e) {
over.removeEventListener('drop', onDrop);
var dt = e.dataTransfer;
dropFired++;
e.preventDefault();
readOnly(e);
}
function readOnly(e) {
var dtList = e.dataTransfer.items;
var num = dtList.length;
// .clear() should have no effect
dtList.clear();
is(dtList.length, num,
".clear() should have no effect on the object during a readOnly event");
// .remove(i) should throw InvalidStateError
for (var i = 0; i < dtList.length; i++) {
expectError(function() { dtList.remove(i); },
"InvalidStateError", ".remove(" + i + ") during a readOnly event");
}
// .add() should return null and have no effect
var data = [["This is a plain string", "text/plain"],
["This is <em>HTML!</em>", "text/html"],
["http://www.mozilla.org/", "text/uri-list"],
["this is some custom data", "custom-data"]];
for (var i = 0; i < data.length; i++) {
is(dtList.add(data[i][0], data[i][1]), null,
".add() should return null during a readOnly event");
is(dtList.length, num, ".add() should have no effect during a readOnly event");
}
// .add() with a file should return null and have no effect
var file = new File(['<a id="a"><b id="b">hey!</b></a>'], "myfile.html",
{type: "text/html"});
is(dtList.add(file), null, ".add() with a file should return null during a readOnly event");
is(dtList.length, num, ".add() should have no effect during a readOnly event");
// We should be able to access the files
is(e.dataTransfer.files.length, 1, "Should be able to access files");
ok(e.dataTransfer.files[0], "File should be the same file!");
is(e.dataTransfer.items.length, 2, "Should be able to see there are 2 items");
is(e.dataTransfer.items[0].kind, "file", "First item should be a file");
is(e.dataTransfer.items[1].kind, "string", "Second item should be a string");
is(e.dataTransfer.items[0].type, "text/html", "first item should be text/html");
is(e.dataTransfer.items[1].type, "text/plain", "second item should be text/plain");
ok(e.dataTransfer.items[0].getAsFile(), "Should be able to get file");
e.dataTransfer.items[1].getAsString(function(s) {
getAsStringCalled++;
is(s, "this is some text", "Should provide the correct string");
});
}
synthesizeDrop(draggable, over, null, null);
// Wait for the getAsString callbacks to complete
yield spin();
is(getAsStringCalled, 2, "getAsString should be called twice");
// Sanity-check to make sure that the events were actually run
is(dragstartFired, 1, "dragstart fired");
is(dragenterFired, 1, "dragenter fired");
is(dropFired, 1, "drop fired");
});
function expectError(fn, eid, testid) {
var error = "";
try {
fn();
} catch (ex) {
error = ex.name;
}
is(error, eid, testid + " causes exception " + eid);
}
function checkTypes(aExpectedList, aDtList, aTestid) {
is(aDtList.length, aExpectedList.length, aTestid + " length test");
for (var i = 0; i < aExpectedList.length; i++) {
is(aDtList[i].type, aExpectedList[i], aTestid + " type " + i);
}
}
</script>
</body>
</html>

View File

@ -358,19 +358,14 @@ function test_DataTransfer(dt)
expectError(() => dt.mozClearDataAt("text/plain", 3),
"IndexSizeError", "clearData index too high with two items");
// ensure that clearData() removes all data associated with the first item, but doesn't
// shift the second item down into the first item's slot.
// ensure that clearData() removes all data associated with the first item
dt.clearData();
is(dt.mozItemCount, 2, "clearData no argument with multiple items itemCount");
checkOneDataItem(dt, [], [], 0,
"clearData no argument with multiple items item at index 0");
is(dt.mozItemCount, 1, "clearData no argument with multiple items itemCount");
checkOneDataItem(dt, ["text/unknown"],
["Unknown type"], 1, "clearData no argument with multiple items item at index 1");
["Unknown type"], 0, "clearData no argument with multiple items item at index 1");
// remove tha remaining data in index 1. As index 0 is empty at this point, this will actually
// drop mozItemCount to 0. (XXX: This is because of spec-compliance reasons related
// to the more-recent dt.item API. It's an unfortunate, but hopefully rare edge case)
dt.mozClearDataAt("", 1);
// remove tha remaining data
dt.mozClearDataAt("", 0);
is(dt.mozItemCount, 0, "all data cleared");
// now check the effectAllowed and dropEffect properties

View File

@ -232,10 +232,3 @@ FileLastModifiedDateWarning=File.lastModifiedDate is deprecated. Use File.lastMo
ChromeScriptedDOMParserWithoutPrincipal=Creating DOMParser without a principal is deprecated.
IIRFilterChannelCountChangeWarning=IIRFilterNode channel count changes may produce audio glitches.
BiquadFilterChannelCountChangeWarning=BiquadFilterNode channel count changes may produce audio glitches.
# LOCALIZATION NOTE: Do not translate ".jpeg"
GenericImageNameJPEG=image.jpeg
# LOCALIZATION NOTE: Do not translate ".gif"
GenericImageNameGIF=image.gif
# LOCALIZATION NOTE: Do not translate ".png"
GenericImageNamePNG=image.png
GenericFileName=file

View File

@ -664,7 +664,7 @@ function checkCachedDataTransfer(cd, eventtype)
var newtext = (eventtype == "paste") ? cd.getData("text/plain") :
cd.mozGetDataAt("text/plain", 0);
is(newtext, (eventtype == "paste") ? oldtext : "Test Cache Data",
is(newtext, (eventtype == "paste") ? "" : "Test Cache Data",
" clipboardData not changed using " + testprefix);
is(getClipboardText(), "Some Clipboard Text", "clipboard not changed using " + testprefix);

View File

@ -385,10 +385,6 @@ var interfaceNamesInGlobalScope =
{name: "DataErrorEvent", b2g: true},
// IMPORTANT: Do not change this list without review from a DOM peer!
"DataTransfer",
// IMPORTANT: Do not change this list without review from a DOM peer!
"DataTransferItem",
// IMPORTANT: Do not change this list without review from a DOM peer!
"DataTransferItemList",
// IMPORTANT: Do not change this list without review from a DOM peer!
"DelayNode",
// IMPORTANT: Do not change this list without review from a DOM peer!

View File

@ -12,12 +12,11 @@ interface DataTransfer {
attribute DOMString dropEffect;
attribute DOMString effectAllowed;
readonly attribute DataTransferItemList items;
//readonly attribute DataTransferItemList items;
[Throws]
void setDragImage(Element image, long x, long y);
[Throws]
readonly attribute DOMStringList types;
[Throws]
DOMString getData(DOMString format);

View File

@ -1,19 +0,0 @@
/* -*- 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:
* https://html.spec.whatwg.org/multipage/interaction.html#the-datatransferitem-interface
*/
interface DataTransferItem {
readonly attribute DOMString kind;
readonly attribute DOMString type;
[Throws]
void getAsString(FunctionStringCallback? _callback);
[Throws]
File? getAsFile();
};
callback FunctionStringCallback = void (DOMString data);

View File

@ -1,22 +0,0 @@
/* -*- 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:
* https://html.spec.whatwg.org/multipage/interaction.html#the-datatransferitemlist-interface
*/
interface DataTransferItemList {
readonly attribute unsigned long length;
[Throws]
getter DataTransferItem (unsigned long index);
[Throws]
DataTransferItem? add(DOMString data, DOMString type);
[Throws]
DataTransferItem? add(File data);
[Throws]
void remove(unsigned long index);
[Throws]
void clear();
};

View File

@ -110,8 +110,6 @@ WEBIDL_FILES = [
'CSSValueList.webidl',
'DataContainerEvent.webidl',
'DataTransfer.webidl',
'DataTransferItem.webidl',
'DataTransferItemList.webidl',
'DecoderDoctorNotification.webidl',
'DedicatedWorkerGlobalScope.webidl',
'DelayNode.webidl',

View File

@ -939,11 +939,7 @@ nsEditorEventListener::CanDrop(nsIDOMDragEvent* aEvent)
nsCOMPtr<DataTransfer> dataTransfer = do_QueryInterface(domDataTransfer);
NS_ENSURE_TRUE(dataTransfer, false);
ErrorResult err;
RefPtr<DOMStringList> types = dataTransfer->GetTypes(err);
if (NS_WARN_IF(err.Failed())) {
return false;
}
RefPtr<DOMStringList> types = dataTransfer->Types();
// Plaintext editors only support dropping text. Otherwise, HTML and files
// can be dropped as well.

View File

@ -294,12 +294,7 @@ nsFileControlFrame::DnDListener::IsValidDropData(nsIDOMDataTransfer* aDOMDataTra
NS_ENSURE_TRUE(dataTransfer, false);
// We only support dropping files onto a file upload control
ErrorResult rv;
RefPtr<DOMStringList> types = dataTransfer->GetTypes(rv);
if (NS_WARN_IF(rv.Failed())) {
return false;
}
RefPtr<DOMStringList> types = dataTransfer->Types();
return types->Contains(NS_LITERAL_STRING("Files"));
}

View File

@ -1705,6 +1705,60 @@
[DataTransfer interface object length]
expected: FAIL
[DataTransfer interface: attribute items]
expected: FAIL
[DataTransferItemList interface: existence and properties of interface object]
expected: FAIL
[DataTransferItemList interface object length]
expected: FAIL
[DataTransferItemList interface: existence and properties of interface prototype object]
expected: FAIL
[DataTransferItemList interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL
[DataTransferItemList interface: attribute length]
expected: FAIL
[DataTransferItemList interface: operation add(DOMString,DOMString)]
expected: FAIL
[DataTransferItemList interface: operation add(File)]
expected: FAIL
[DataTransferItemList interface: operation remove(unsigned long)]
expected: FAIL
[DataTransferItemList interface: operation clear()]
expected: FAIL
[DataTransferItem interface: existence and properties of interface object]
expected: FAIL
[DataTransferItem interface object length]
expected: FAIL
[DataTransferItem interface: existence and properties of interface prototype object]
expected: FAIL
[DataTransferItem interface: existence and properties of interface prototype object's "constructor" property]
expected: FAIL
[DataTransferItem interface: attribute kind]
expected: FAIL
[DataTransferItem interface: attribute type]
expected: FAIL
[DataTransferItem interface: operation getAsString(FunctionStringCallback)]
expected: FAIL
[DataTransferItem interface: operation getAsFile()]
expected: FAIL
[Window interface: operation showModalDialog(DOMString,any)]
disabled:
if e10s: https://bugzilla.mozilla.org/show_bug.cgi?id=981796
@ -2482,6 +2536,12 @@
[DrawingStyle interface object name]
expected: FAIL
[DataTransferItemList interface object name]
expected: FAIL
[DataTransferItem interface object name]
expected: FAIL
[ApplicationCache interface object name]
expected: FAIL

View File

@ -272,18 +272,12 @@ nsClipboard::GetData(nsITransferable *aTransferable,
nsCOMPtr<imgITools> imgTool = do_GetService(NS_IMGTOOLS_CID);
nsCOMPtr<nsIInputStream> byteStream;
nsresult rv = imgTool->EncodeImage(imageContainer,
flavorStr,
EmptyString(),
getter_AddRefs(byteStream));
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}
imgTool->EncodeImage(imageContainer, flavorStr, EmptyString(), getter_AddRefs(byteStream));
// Set transferable.
rv = aTransferable->SetTransferData(flavorStr,
byteStream,
sizeof(nsIInputStream*));
nsresult rv = aTransferable->SetTransferData(flavorStr,
byteStream,
sizeof(nsIInputStream*));
if (NS_WARN_IF(NS_FAILED(rv))) {
continue;
}