Merge mozilla-central to mozilla-inbound

This commit is contained in:
Carsten "Tomcat" Book 2016-06-09 15:41:40 +02:00
commit 801ccd962d
35 changed files with 661 additions and 2006 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,80 +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);
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();
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;
}
@ -449,7 +475,7 @@ DataTransfer::ClearData(const Optional<nsAString>& aFormat, ErrorResult& aRv)
return;
}
if (MozItemCount() == 0) {
if (mItems.Length() == 0) {
return;
}
@ -538,29 +564,27 @@ DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv) const
}
RefPtr<DOMStringList> types = new DOMStringList();
if (aIndex < MozItemCount()) {
// note that you can retrieve the types regardless of their principal
const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(aIndex);
if (aIndex < mItems.Length()) {
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;
// 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);
}
}
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"));
}
}
@ -597,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;
}
@ -611,6 +635,20 @@ DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
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
@ -620,49 +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 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;
}
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;
}
@ -706,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;
}
@ -764,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;
}
@ -778,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
@ -796,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));
@ -804,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
@ -857,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;
}
@ -906,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;
}
@ -944,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) {
@ -958,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;
@ -1014,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;
}
@ -1035,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;
}
@ -1062,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);
@ -1120,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;
}
@ -1128,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 {
@ -1180,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);
@ -1236,13 +1291,7 @@ DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
void
DataTransfer::ClearAll()
{
mItems->ClearAllItems();
}
uint32_t
DataTransfer::MozItemCount() const
{
return mItems->MozItemCount();
mItems.Clear();
}
nsresult
@ -1254,13 +1303,49 @@ DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
nsAutoString format;
GetRealFormat(aFormat, format);
ErrorResult rv;
RefPtr<DataTransferItem> item =
mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
/* aInsertOnly = */ false,
/* aHidden= */ 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
@ -1297,48 +1382,26 @@ DataTransfer::GetRealFormat(const nsAString& aInFormat,
aOutFormat.Assign(lowercaseFormat);
}
nsresult
void
DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex,
nsIPrincipal* aPrincipal, bool aHidden)
nsIPrincipal* aPrincipal)
{
ErrorResult rv;
RefPtr<DataTransferItem> item;
if (strcmp(aFormat, kUnicodeMime) == 0) {
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;
SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nullptr, aIndex,
aPrincipal);
return;
}
if (strcmp(aFormat, kURLDataMime) == 0) {
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_LITERAL_STRING("text/uri-list"), nullptr, aIndex,
aPrincipal);
return;
}
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;
SetDataWithPrincipal(NS_ConvertUTF8toUTF16(aFormat), nullptr, 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, kPNGImageMime, kJPEGImageMime,
kGIFImageMime };
void
DataTransfer::CacheExternalDragFormats()
{
@ -1367,9 +1430,6 @@ DataTransfer::CacheExternalDragFormats()
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);
@ -1383,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, /* hidden = */ f && hasFileData);
CacheExternalData(formats[f], c, sysPrincipal);
}
}
}
@ -1413,17 +1473,11 @@ DataTransfer::CacheExternalClipboardFormats()
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, kPNGImageMime,
kJPEGImageMime, kGIFImageMime };
kURLMime, kURLDataMime, kUnicodeMime };
for (uint32_t f = 0; f < mozilla::ArrayLength(formats); ++f) {
// check each format one at a time
@ -1436,23 +1490,106 @@ DataTransfer::CacheExternalClipboardFormats()
if (f == 0) {
FillInExternalCustomTypes(0, sysPrincipal);
} else {
// 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);
CacheExternalData(formats[f], 0, sysPrincipal);
}
}
}
}
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);
}
}
}
}
@ -1462,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,16 +260,16 @@ 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.
nsresult CacheExternalData(const char* aFormat, uint32_t aIndex,
nsIPrincipal* aPrincipal, bool aHidden);
void CacheExternalData(const char* aFormat, uint32_t aIndex,
nsIPrincipal* aPrincipal);
// caches the formats that exist in the drag service that were added by an
// external drag
@ -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,368 +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" },
{ kJPEGImageMime, "GenericImageNameJPEG" },
{ kGIFImageMime, "GenericImageNameGIF" },
{ kPNGImageMime, "GenericImageNamePNG" },
};
} // 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 one of kFileMime,
// kPNGImageMime, kJPEGImageMime, or kGIFImageMime. We want to convert
// whatever type happens to actually be stored into a dom::File.
RefPtr<File> file = FileFromISupports(data);
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::FileFromISupports(nsISupports* aSupports)
{
MOZ_ASSERT(aSupports);
RefPtr<File> file;
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.
Blob* blob = static_cast<Blob*>(domBlob.get());
file = blob->ToFile();
} else if (nsCOMPtr<nsIFile> ifile = do_QueryInterface(aSupports)) {
printf("Creating a File from a nsIFile!\n");
file = File::CreateFromFile(GetParentObject(), ifile);
} else if (nsCOMPtr<nsIInputStream> stream = do_QueryInterface(aSupports)) {
// 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(aSupports)) {
MOZ_ASSERT(blobImpl->IsFile());
file = File::Create(GetParentObject(), blobImpl);
}
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(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(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,140 +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), mChromeOnly(false), 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(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();
bool ChromeOnly() const
{
return mChromeOnly;
}
void SetChromeOnly(bool aChromeOnly)
{
mChromeOnly = aChromeOnly;
}
private:
~DataTransferItem() {}
already_AddRefed<File> FileFromISupports(nsISupports* aParent);
already_AddRefed<File> CreateFileFromInputStream(nsISupports* aParent,
nsIInputStream* aStream,
ErrorResult& aRv);
// The index in the 2d mIndexedItems array
uint32_t mIndex;
bool mChromeOnly;
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,590 +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(),
/* aInsertOnly = */ true,
/* aHidden = */ false,
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, false, 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,
bool aHidden,
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, aHidden);
if (item->Kind() == DataTransferItem::KIND_FILE) {
RegenerateFiles();
}
return item.forget();
}
DataTransferItem*
DataTransferItemList::AppendNewItem(uint32_t aIndex,
const nsAString& aType,
nsIVariant* aData,
nsIPrincipal* aPrincipal,
bool aHidden)
{
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);
item->SetChromeOnly(aHidden);
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 (!aHidden && (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,117 +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, bool aHidden, 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,
bool aHidden);
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

@ -200,4 +200,3 @@ skip-if = buildapp == 'b2g' # no wheel events on b2g
[test_bug1264380.html]
run-if = (e10s && os != "win") # Bug 1270043, crash at windows platforms; Bug1264380 comment 20, nsDragService::InvokeDragSessionImpl behaves differently among platform implementations in non-e10s mode which prevents us to check the validity of nsIDragService::getCurrentSession() consistently via synthesize mouse clicks in non-e10s mode.
[test_passive_listeners.html]
[test_paste_image.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

@ -1,196 +0,0 @@
<html><head>
<title>Test for bug 891247</title>
<link rel="stylesheet" href="/tests/SimpleTest/test.css">
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<script type="text/javascript" src="/tests/SimpleTest/EventUtils.js"></script>
<script class="testbody" type="application/javascript">
function ImageTester() {
var counter = 0;
var images = [];
var that = this;
this.add = function(aFile) {
images.push(aFile);
};
this.test = function() {
for (var i = 0; i < images.length; i++) {
testImageSize(images[i]);
}
};
this.returned = function() {
counter++;
info("returned=" + counter + " images.length=" + images.length);
if (counter == images.length) {
info("test finish");
SimpleTest.finish();
}
};
function testImageSize(aFile) {
var source = window.URL.createObjectURL(aFile);
var image = new Image();
image.src = source;
var imageTester = that;
image.onload = function() {
is(this.width, 62, "Check generated image width");
is(this.height, 71, "Check generated image height");
if (aFile.type == "image/gif") {
// this test fails for image/jpeg and image/png because the images
// generated are slightly different
testImageCanvas(image);
}
imageTester.returned();
}
document.body.appendChild(image);
};
function testImageCanvas(aImage) {
var canvas = drawToCanvas(aImage);
var refImage = document.getElementById('image');
var refCanvas = drawToCanvas(refImage);
is(canvas.toDataURL(), refCanvas.toDataURL(), "Image should map pixel-by-pixel");
}
function drawToCanvas(aImage) {
var canvas = document.createElement("CANVAS");
document.body.appendChild(canvas);
canvas.width = aImage.width;
canvas.height = aImage.height;
canvas.getContext('2d').drawImage(aImage, 0, 0);
return canvas;
}
}
function copyImage(aImageId) {
// selection of the node
var node = document.getElementById(aImageId);
var webnav = SpecialPowers.wrap(window)
.QueryInterface(SpecialPowers.Ci.nsIInterfaceRequestor)
.getInterface(SpecialPowers.Ci.nsIWebNavigation)
var docShell = webnav.QueryInterface(SpecialPowers.Ci.nsIDocShell);
// let's copy the node
var documentViewer = docShell.contentViewer
.QueryInterface(SpecialPowers.Ci.nsIContentViewerEdit);
documentViewer.setCommandNode(node);
documentViewer.copyImage(documentViewer.COPY_IMAGE_ALL);
}
function doTest() {
SimpleTest.waitForExplicitFinish();
copyImage('image');
//--------- now check the content of the clipboard
var clipboard = SpecialPowers.Cc["@mozilla.org/widget/clipboard;1"]
.getService(SpecialPowers.Ci.nsIClipboard);
// does the clipboard contain text/unicode data ?
ok(clipboard.hasDataMatchingFlavors(["text/unicode"], 1, clipboard.kGlobalClipboard),
"clipboard contains unicode text");
// does the clipboard contain text/html data ?
ok(clipboard.hasDataMatchingFlavors(["text/html"], 1, clipboard.kGlobalClipboard),
"clipboard contains html text");
// does the clipboard contain image data ?
ok(clipboard.hasDataMatchingFlavors(["image/png"], 1, clipboard.kGlobalClipboard),
"clipboard contains image");
window.addEventListener("paste", onPaste);
var textarea = SpecialPowers.wrap(document.getElementById('textarea'));
textarea.focus();
textarea.editor.paste(clipboard.kGlobalClipboard);
}
function onPaste(e) {
var imageTester = new ImageTester;
testFiles(e, imageTester);
testItems(e, imageTester);
imageTester.test();
}
function testItems(e, imageTester) {
var items = e.clipboardData.items;
is(items, e.clipboardData.items,
"Getting @items twice should return the same object");
var haveFiles = false;
ok(items instanceof DataTransferItemList, "@items implements DataTransferItemList");
ok(items.length > 0, "@items is not empty");
for (var i = 0; i < items.length; i++) {
var item = items[i];
ok(item instanceof DataTransferItem, "each element of @items must implement DataTransferItem");
if (item.kind == "file") {
var file = item.getAsFile();
ok(file instanceof File, ".getAsFile() returns a File object");
ok(file.size > 0, "Files shouldn't have size 0");
imageTester.add(file);
}
}
}
function testFiles(e, imageTester) {
var files = e.clipboardData.files;
is(files, e.clipboardData.files,
"Getting the files array twice should return the same array");
ok(files.length > 0, "There should be at least one file in the clipboard");
for (var i = 0; i < files.length; i++) {
var file = files[i];
ok(file instanceof File, ".files should contain only File objects");
ok(file.size > 0, "This file shouldn't have size 0");
if (file.name == "image.png") {
is(file.type, "image/png", "This file should be a image/png");
} else if (file.name == "image.jpeg") {
is(file.type, "image/jpeg", "This file should be a image/jpeg");
} else if (file.name == "image.gif") {
is(file.type, "image/gif", "This file should be a image/gif");
} else {
info("file.name=" + file.name);
ok(false, "Unexpected file name");
}
testSlice(file);
imageTester.add(file);
// Adding the same image again so we can test concurrency
imageTester.add(file);
}
}
function testSlice(aFile) {
var blob = aFile.slice();
ok(blob instanceof Blob, ".slice returns a blob");
is(blob.size, aFile.size, "the blob has the same size");
blob = aFile.slice(123123);
is(blob.size, 0, ".slice overflow check");
blob = aFile.slice(123, 123141);
is(blob.size, aFile.size - 123, ".slice @size check");
blob = aFile.slice(123, 12);
is(blob.size, 0, ".slice @size check 2");
blob = aFile.slice(124, 134, "image/png");
is(blob.size, 10, ".slice @size check 3");
is(blob.type, "image/png", ".slice @type check");
}
</script>
<body onload="doTest();">
<img id="image" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAD4AAABHCA
IAAADQjmMaAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAB3RJTUUH3goUAwAgSAORBwAAABl0RVh0Q29
tbWVudABDcmVhdGVkIHdpdGggR0lNUFeBDhcAAABPSURBVGje7c4BDQAACAOga//OmuMbJGAurTbq
6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6urq6s31B0IqAY2/t
QVCAAAAAElFTkSuQmCC" />
<form>
<textarea id="textarea"></textarea>
</form>
</body>
</html>

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

@ -0,0 +1,117 @@
#! /bin/bash -vex
set -x -e
echo "running as" $(id)
####
# Taskcluster friendly wrapper for performing fx desktop l10n repacks via mozharness.
# Based on ./build-linux.sh
####
# Inputs, with defaults
: MOZHARNESS_SCRIPT ${MOZHARNESS_SCRIPT}
: MOZHARNESS_CONFIG ${MOZHARNESS_CONFIG}
: MOZHARNESS_ACTIONS ${MOZHARNESS_ACTIONS}
: MOZHARNESS_OPTIONS ${MOZHARNESS_OPTIONS}
: TOOLTOOL_CACHE ${TOOLTOOL_CACHE:=/home/worker/tooltool-cache}
: NEED_XVFB ${NEED_XVFB:=false}
: WORKSPACE ${WORKSPACE:=/home/worker/workspace}
set -v
fail() {
echo # make sure error message is on a new line
echo "[build-l10n.sh:error]" "${@}"
exit 1
}
export MOZ_CRASHREPORTER_NO_REPORT=1
export MOZ_OBJDIR=obj-firefox
export TINDERBOX_OUTPUT=1
# Ensure that in tree libraries can be found
export LIBRARY_PATH=$LIBRARY_PATH:$WORKSPACE/src/obj-firefox:$WORKSPACE/src/gcc/lib64
# test required parameters are supplied
if [[ -z ${MOZHARNESS_SCRIPT} ]]; then fail "MOZHARNESS_SCRIPT is not set"; fi
if [[ -z ${MOZHARNESS_CONFIG} ]]; then fail "MOZHARNESS_CONFIG is not set"; fi
cleanup() {
local rv=$?
if [ -n "$xvfb_pid" ]; then
kill $xvfb_pid || true
fi
exit $rv
}
trap cleanup EXIT INT
# run mozharness in XVfb, if necessary; this is an array to maintain the quoting in the -s argument
if $NEED_XVFB; then
# Some mozharness scripts set DISPLAY=:2
Xvfb :2 -screen 0 1024x768x24 &
export DISPLAY=:2
xvfb_pid=$!
# Only error code 255 matters, because it signifies that no
# display could be opened. As long as we can open the display
# tests should work. We'll retry a few times with a sleep before
# failing.
retry_count=0
max_retries=2
xvfb_test=0
until [ $retry_count -gt $max_retries ]; do
xvinfo || xvfb_test=$?
if [ $xvfb_test != 255 ]; then
retry_count=$(($max_retries + 1))
else
retry_count=$(($retry_count + 1))
echo "Failed to start Xvfb, retry: $retry_count"
sleep 2
fi
done
if [ $xvfb_test == 255 ]; then fail "xvfb did not start properly"; fi
fi
# set up mozharness configuration, via command line, env, etc.
# $TOOLTOOL_CACHE bypasses mozharness completely and is read by tooltool_wrapper.sh to set the
# cache. However, only some mozharness scripts use tooltool_wrapper.sh, so this may not be
# entirely effective.
export TOOLTOOL_CACHE
# support multiple, space delimited, config files
config_cmds=""
for cfg in $MOZHARNESS_CONFIG; do
config_cmds="${config_cmds} --config ${cfg}"
done
# if MOZHARNESS_ACTIONS is given, only run those actions (completely overriding default_actions
# in the mozharness configuration)
if [ -n "$MOZHARNESS_ACTIONS" ]; then
actions=""
for action in $MOZHARNESS_ACTIONS; do
actions="$actions --$action"
done
fi
# if MOZHARNESS_OPTIONS is given, append them to mozharness command line run
# e.g. enable-pgo
if [ -n "$MOZHARNESS_OPTIONS" ]; then
options=""
for option in $MOZHARNESS_OPTIONS; do
options="$options --$option"
done
fi
python2.7 $WORKSPACE/build/src/testing/${MOZHARNESS_SCRIPT} \
--disable-mock \
--revision ${GECKO_HEAD_REV} \
$actions \
$options \
${config_cmds} \
--log-level=debug \
--work-dir=$WORKSPACE/build \

View File

@ -33,6 +33,7 @@ config = {
'default_actions': [
"clobber",
"pull",
"clone-locales",
"list-locales",
"setup",
"repack",

View File

@ -31,6 +31,7 @@ config = {
'default_actions': [
"clobber",
"pull",
"clone-locales",
"list-locales",
"setup",
"repack",

View File

@ -31,6 +31,7 @@ config = {
'default_actions': [
"clobber",
"pull",
"clone-locales",
"list-locales",
"setup",
"repack",

View File

@ -31,6 +31,7 @@ config = {
'default_actions': [
"clobber",
"pull",
"clone-locales",
"list-locales",
"setup",
"repack",

View File

@ -31,6 +31,7 @@ config = {
'default_actions': [
"clobber",
"pull",
"clone-locales",
"list-locales",
"setup",
"repack",

View File

@ -0,0 +1,24 @@
import os
config = {
"locales_file": "src/browser/locales/all-locales",
"tools_repo": "https://hg.mozilla.org/build/tools",
"mozconfig": "src/browser/config/mozconfigs/linux64/l10n-mozconfig",
"bootstrap_env": {
"NO_MERCURIAL_SETUP_CHECK": "1",
"MOZ_OBJDIR": "obj-l10n",
"EN_US_BINARY_URL": "%(en_us_binary_url)s",
"LOCALE_MERGEDIR": "%(abs_merge_dir)s/",
"MOZ_UPDATE_CHANNEL": "%(update_channel)s",
"DIST": "%(abs_objdir)s",
"LOCALE_MERGEDIR": "%(abs_merge_dir)s/",
"L10NBASEDIR": "../../l10n",
"MOZ_MAKE_COMPLETE_MAR": "1",
'TOOLTOOL_CACHE': os.environ.get('TOOLTOOL_CACHE'),
},
"upload_env": {
'UPLOAD_HOST': 'localhost',
'UPLOAD_PATH': '/home/worker/workspace/build/upload/',
},
"mozilla_dir": "src/",
}

View File

@ -141,20 +141,21 @@ class LocalesMixin(ChunkingMixin):
def run_compare_locales(self, locale, halt_on_failure=False):
dirs = self.query_abs_dirs()
compare_locales_script = os.path.join(dirs['abs_compare_locales_dir'],
'scripts', 'compare-locales')
env = self.query_env(partial_env={'PYTHONPATH':
os.path.join(dirs['abs_compare_locales_dir'],
'lib')})
env = self.query_l10n_env()
python = self.query_exe('python2.7')
compare_locales_error_list = list(PythonErrorList)
self.rmtree(dirs['abs_merge_dir'])
self.mkdir_p(dirs['abs_merge_dir'])
command = "python %s -m %s l10n.ini %s %s" % (compare_locales_script,
dirs['abs_merge_dir'], dirs['abs_l10n_dir'], locale)
command = [python, 'mach', 'compare-locales',
'--merge-dir', dirs['abs_merge_dir'],
'--l10n-ini', os.path.join(dirs['abs_locales_src_dir'], 'l10n.ini'),
'--l10n-base', dirs['abs_l10n_dir'], locale]
self.info("*** BEGIN compare-locales %s" % locale)
status = self.run_command(command, error_list=compare_locales_error_list,
cwd=dirs['abs_locales_src_dir'], env=env,
halt_on_failure=halt_on_failure)
status = self.run_command(command,
halt_on_failure=halt_on_failure,
env=env,
cwd=dirs['abs_mozilla_dir'],
error_list=compare_locales_error_list)
self.info("*** END compare-locales %s" % locale)
return status
@ -175,8 +176,15 @@ class LocalesMixin(ChunkingMixin):
c['mozilla_dir'])
dirs['abs_locales_src_dir'] = os.path.join(dirs['abs_mozilla_dir'],
c['locales_dir'])
dirs['abs_l10n_dir'] = os.path.join(dirs['abs_work_dir'],
c['l10n_dir'])
dirs['abs_compare_locales_dir'] = os.path.join(dirs['abs_mozilla_dir'],
'python', 'compare-locales',
'compare_locales')
else:
# Use old-compare-locales if no mozilla_dir set, needed
# for clobberer, and existing mozharness tests.
dirs['abs_compare_locales_dir'] = os.path.join(dirs['abs_work_dir'],
'compare-locales')
if 'objdir' in c:
if os.path.isabs(c['objdir']):
dirs['abs_objdir'] = c['objdir']
@ -187,8 +195,7 @@ class LocalesMixin(ChunkingMixin):
'merged')
dirs['abs_locales_dir'] = os.path.join(dirs['abs_objdir'],
c['locales_dir'])
dirs['abs_compare_locales_dir'] = os.path.join(dirs['abs_work_dir'],
'compare-locales')
for key in dirs.keys():
if key not in abs_dirs:
abs_dirs[key] = dirs[key]

View File

@ -106,6 +106,9 @@ class MultiLocaleBuild(LocalesMixin, MercurialScript):
'upload-multi', 'summary'],
require_config_file=require_config_file)
def query_l10n_env(self):
return self.query_env()
def clobber(self):
c = self.config
if c['work_dir'] != '.':

View File

@ -173,6 +173,7 @@ class DesktopSingleLocale(LocalesMixin, ReleaseMixin, MockMixin, BuildbotMixin,
'all_actions': [
"clobber",
"pull",
"clone-locales",
"list-locales",
"setup",
"repack",
@ -465,7 +466,7 @@ class DesktopSingleLocale(LocalesMixin, ReleaseMixin, MockMixin, BuildbotMixin,
def _query_revision(self):
""" Get the gecko revision in this order of precedence
* cached value
* command line arg --revision (development)
* command line arg --revision (development, taskcluster)
* buildbot properties (try with buildbot forced build)
* buildbot change (try with buildbot scheduler)
* from the en-US build (m-c & m-a)
@ -602,8 +603,7 @@ class DesktopSingleLocale(LocalesMixin, ReleaseMixin, MockMixin, BuildbotMixin,
def clobber(self):
"""clobber"""
dirs = self.query_abs_dirs()
clobber_dirs = (dirs['abs_objdir'], dirs['abs_compare_locales_dir'],
dirs['abs_upload_dir'])
clobber_dirs = (dirs['abs_objdir'], dirs['abs_upload_dir'])
PurgeMixin.clobber(self, always_clobber_dirs=clobber_dirs)
def pull(self):
@ -637,6 +637,8 @@ class DesktopSingleLocale(LocalesMixin, ReleaseMixin, MockMixin, BuildbotMixin,
self.info("repositories: %s" % repos)
self.vcs_checkout_repos(repos, parent_dir=dirs['abs_work_dir'],
tag_override=config.get('tag_override'))
def clone_locales(self):
self.pull_locale_source()
def setup(self):

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;
}