2015-08-28 22:21:08 +03:00
|
|
|
/* -*- 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"
|
2016-07-29 14:42:33 +03:00
|
|
|
#include "mozilla/dom/Directory.h"
|
|
|
|
#include "mozilla/dom/Event.h"
|
2016-08-18 10:17:02 +03:00
|
|
|
#include "mozilla/dom/FileSystem.h"
|
2016-08-18 10:18:05 +03:00
|
|
|
#include "mozilla/dom/FileSystemDirectoryEntry.h"
|
2016-08-18 10:17:48 +03:00
|
|
|
#include "mozilla/dom/FileSystemFileEntry.h"
|
2015-08-28 22:21:08 +03:00
|
|
|
#include "nsIClipboard.h"
|
|
|
|
#include "nsISupportsPrimitives.h"
|
2016-08-18 01:39:15 +03:00
|
|
|
#include "nsIScriptObjectPrincipal.h"
|
2015-08-28 22:21:08 +03:00
|
|
|
#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" },
|
2016-05-05 17:07:24 +03:00
|
|
|
{ kPNGImageMime, "GenericImageNamePNG" },
|
2015-08-28 22:21:08 +03:00
|
|
|
};
|
|
|
|
|
|
|
|
} // anonymous namespace
|
|
|
|
|
|
|
|
namespace mozilla {
|
|
|
|
namespace dom {
|
|
|
|
|
|
|
|
NS_IMPL_CYCLE_COLLECTION_WRAPPERCACHE(DataTransferItem, mData,
|
2016-08-18 01:39:15 +03:00
|
|
|
mPrincipal, mDataTransfer, mCachedFile)
|
2015-08-28 22:21:08 +03:00
|
|
|
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>
|
2016-08-18 01:39:15 +03:00
|
|
|
DataTransferItem::Clone(DataTransfer* aDataTransfer) const
|
2015-08-28 22:21:08 +03:00
|
|
|
{
|
2016-08-18 01:39:15 +03:00
|
|
|
MOZ_ASSERT(aDataTransfer);
|
2015-08-28 22:21:08 +03:00
|
|
|
|
2016-08-18 01:39:15 +03:00
|
|
|
RefPtr<DataTransferItem> it = new DataTransferItem(aDataTransfer, mType);
|
2015-08-28 22:21:08 +03:00
|
|
|
|
|
|
|
// Copy over all of the fields
|
|
|
|
it->mKind = mKind;
|
|
|
|
it->mIndex = mIndex;
|
|
|
|
it->mData = mData;
|
|
|
|
it->mPrincipal = mPrincipal;
|
2016-06-21 20:07:17 +03:00
|
|
|
it->mChromeOnly = mChromeOnly;
|
2015-08-28 22:21:08 +03:00
|
|
|
|
|
|
|
return it.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
DataTransferItem::SetData(nsIVariant* aData)
|
|
|
|
{
|
2016-06-21 20:07:17 +03:00
|
|
|
// Invalidate our file cache, we will regenerate it with the new data
|
|
|
|
mCachedFile = nullptr;
|
|
|
|
|
2015-08-28 22:21:08 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
|
|
|
mData = aData;
|
2016-08-23 18:29:48 +03:00
|
|
|
mKind = KindFromData(mData);
|
|
|
|
}
|
2015-08-28 22:21:08 +03:00
|
|
|
|
2016-08-23 18:29:48 +03:00
|
|
|
/* static */ DataTransferItem::eKind
|
|
|
|
DataTransferItem::KindFromData(nsIVariant* aData)
|
|
|
|
{
|
2015-08-28 22:21:08 +03:00
|
|
|
nsCOMPtr<nsISupports> supports;
|
|
|
|
nsresult rv = aData->GetAsISupports(getter_AddRefs(supports));
|
|
|
|
if (NS_SUCCEEDED(rv) && supports) {
|
2016-06-21 20:07:17 +03:00
|
|
|
// Check if we have one of the supported file data formats
|
|
|
|
if (nsCOMPtr<nsIDOMBlob>(do_QueryInterface(supports)) ||
|
|
|
|
nsCOMPtr<BlobImpl>(do_QueryInterface(supports)) ||
|
|
|
|
nsCOMPtr<nsIFile>(do_QueryInterface(supports))) {
|
2016-08-23 18:29:48 +03:00
|
|
|
return KIND_FILE;
|
2015-08-28 22:21:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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)) {
|
2016-08-23 18:29:48 +03:00
|
|
|
return KIND_STRING;
|
2015-08-28 22:21:08 +03:00
|
|
|
}
|
2016-08-23 18:29:48 +03:00
|
|
|
|
|
|
|
return KIND_OTHER;
|
2015-08-28 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
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);
|
|
|
|
|
2016-08-18 01:39:15 +03:00
|
|
|
if (mDataTransfer->GetEventMessage() == ePaste) {
|
2015-08-28 22:21:08 +03:00
|
|
|
MOZ_ASSERT(mIndex == 0, "index in clipboard must be 0");
|
|
|
|
|
|
|
|
nsCOMPtr<nsIClipboard> clipboard =
|
|
|
|
do_GetService("@mozilla.org/widget/clipboard;1");
|
2016-08-18 01:39:15 +03:00
|
|
|
if (!clipboard || mDataTransfer->ClipboardType() < 0) {
|
2015-08-28 22:21:08 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-08-18 01:39:15 +03:00
|
|
|
nsresult rv = clipboard->GetData(trans, mDataTransfer->ClipboardType());
|
2015-08-28 22:21:08 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2016-06-21 20:07:17 +03:00
|
|
|
// Fill the variant
|
2015-08-28 22:21:08 +03:00
|
|
|
RefPtr<nsVariantCC> variant = new nsVariantCC();
|
|
|
|
|
2016-06-21 20:07:17 +03:00
|
|
|
eKind oldKind = Kind();
|
|
|
|
if (oldKind == KIND_FILE) {
|
|
|
|
// Because this is an external piece of data, mType is one of kFileMime,
|
|
|
|
// kPNGImageMime, kJPEGImageMime, or kGIFImageMime. Some of these types
|
|
|
|
// are passed in as a nsIInputStream which must be converted to a
|
|
|
|
// dom::File before storing.
|
|
|
|
if (nsCOMPtr<nsIInputStream> istream = do_QueryInterface(data)) {
|
|
|
|
RefPtr<File> file = CreateFileFromInputStream(istream);
|
|
|
|
if (NS_WARN_IF(!file)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
data = do_QueryObject(file);
|
|
|
|
}
|
|
|
|
|
|
|
|
variant->SetAsISupports(data);
|
2015-08-28 22:21:08 +03:00
|
|
|
} else {
|
2016-06-21 20:07:17 +03:00
|
|
|
// We have an external piece of string data. Extract it and store it in the variant
|
|
|
|
MOZ_ASSERT(oldKind == KIND_STRING);
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupportsString> supportsstr = do_QueryInterface(data);
|
|
|
|
if (supportsstr) {
|
|
|
|
nsAutoString str;
|
|
|
|
supportsstr->GetData(str);
|
|
|
|
variant->SetAsAString(str);
|
2015-08-28 22:21:08 +03:00
|
|
|
} else {
|
2016-06-21 20:07:17 +03:00
|
|
|
nsCOMPtr<nsISupportsCString> supportscstr = do_QueryInterface(data);
|
|
|
|
if (supportscstr) {
|
|
|
|
nsAutoCString str;
|
|
|
|
supportscstr->GetData(str);
|
|
|
|
variant->SetAsACString(str);
|
|
|
|
}
|
2015-08-28 22:21:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
SetData(variant);
|
2016-06-21 20:07:17 +03:00
|
|
|
|
|
|
|
if (oldKind != Kind()) {
|
|
|
|
NS_WARNING("Clipboard data provided by the OS does not match predicted kind");
|
2016-10-11 04:07:47 +03:00
|
|
|
mDataTransfer->TypesListMayHaveChanged();
|
2016-06-21 20:07:17 +03:00
|
|
|
}
|
2015-08-28 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
2017-02-25 00:06:35 +03:00
|
|
|
void
|
|
|
|
DataTransferItem::GetType(nsAString& aType)
|
|
|
|
{
|
|
|
|
// If we don't have a File, we can just put whatever our recorded internal
|
|
|
|
// type is.
|
|
|
|
if (Kind() != KIND_FILE) {
|
|
|
|
aType = mType;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we do have a File, then we need to look at our File object to discover
|
|
|
|
// what its mime type is. We can use the System Principal here, as this
|
|
|
|
// information should be avaliable even if the data is currently inaccessible
|
|
|
|
// (for example during a dragover).
|
|
|
|
//
|
|
|
|
// XXX: This seems inefficient, as it seems like we should be able to get this
|
|
|
|
// data without getting the entire File object, which may require talking to
|
|
|
|
// the OS.
|
|
|
|
ErrorResult rv;
|
|
|
|
RefPtr<File> file = GetAsFile(*nsContentUtils::GetSystemPrincipal(), rv);
|
|
|
|
MOZ_ASSERT(!rv.Failed(), "Failed to get file data with system principal");
|
|
|
|
|
|
|
|
// If we don't actually have a file, fall back to returning the internal type.
|
|
|
|
if (NS_WARN_IF(!file)) {
|
|
|
|
aType = mType;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
file->GetType(aType);
|
|
|
|
}
|
|
|
|
|
2015-08-28 22:21:08 +03:00
|
|
|
already_AddRefed<File>
|
2016-10-11 04:07:48 +03:00
|
|
|
DataTransferItem::GetAsFile(nsIPrincipal& aSubjectPrincipal,
|
2016-09-29 09:55:20 +03:00
|
|
|
ErrorResult& aRv)
|
2016-08-23 18:29:48 +03:00
|
|
|
{
|
|
|
|
// This is done even if we have an mCachedFile, as it performs the necessary
|
|
|
|
// permissions checks to ensure that we are allowed to access this type.
|
2016-10-11 04:07:48 +03:00
|
|
|
nsCOMPtr<nsIVariant> data = Data(&aSubjectPrincipal, aRv);
|
2016-08-18 01:39:15 +03:00
|
|
|
if (NS_WARN_IF(!data || aRv.Failed())) {
|
2015-08-28 22:21:08 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-09-23 21:44:47 +03:00
|
|
|
// We have to check our kind after getting the data, because if we have
|
|
|
|
// external data and the OS lied to us (which unfortunately does happen
|
|
|
|
// sometimes), then we might not have the same type of data as we did coming
|
|
|
|
// into this function.
|
|
|
|
if (NS_WARN_IF(mKind != KIND_FILE)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-06-21 20:07:17 +03:00
|
|
|
// Generate the dom::File from the stored data, caching it so that the
|
|
|
|
// same object is returned in the future.
|
|
|
|
if (!mCachedFile) {
|
2016-08-23 18:29:48 +03:00
|
|
|
nsCOMPtr<nsISupports> supports;
|
|
|
|
aRv = data->GetAsISupports(getter_AddRefs(supports));
|
|
|
|
MOZ_ASSERT(!aRv.Failed() && supports,
|
|
|
|
"File objects should be stored as nsISupports variants");
|
|
|
|
if (aRv.Failed() || !supports) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-06-21 20:07:17 +03:00
|
|
|
if (nsCOMPtr<nsIDOMBlob> domBlob = do_QueryInterface(supports)) {
|
|
|
|
Blob* blob = static_cast<Blob*>(domBlob.get());
|
|
|
|
mCachedFile = blob->ToFile();
|
|
|
|
} else if (nsCOMPtr<BlobImpl> blobImpl = do_QueryInterface(supports)) {
|
|
|
|
MOZ_ASSERT(blobImpl->IsFile());
|
2016-08-18 01:39:15 +03:00
|
|
|
mCachedFile = File::Create(mDataTransfer, blobImpl);
|
2016-06-21 20:07:17 +03:00
|
|
|
} else if (nsCOMPtr<nsIFile> ifile = do_QueryInterface(supports)) {
|
2016-08-18 01:39:15 +03:00
|
|
|
mCachedFile = File::CreateFromFile(mDataTransfer, ifile);
|
2016-06-21 20:07:17 +03:00
|
|
|
} else {
|
|
|
|
MOZ_ASSERT(false, "One of the above code paths should be taken");
|
2016-09-23 21:44:47 +03:00
|
|
|
return nullptr;
|
2016-05-31 22:03:44 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-06-21 20:07:17 +03:00
|
|
|
RefPtr<File> file = mCachedFile;
|
2016-05-31 22:03:44 +03:00
|
|
|
return file.forget();
|
|
|
|
}
|
|
|
|
|
2016-08-18 10:17:25 +03:00
|
|
|
already_AddRefed<FileSystemEntry>
|
2016-10-11 04:07:48 +03:00
|
|
|
DataTransferItem::GetAsEntry(nsIPrincipal& aSubjectPrincipal,
|
2016-09-29 09:55:20 +03:00
|
|
|
ErrorResult& aRv)
|
2016-07-29 14:42:33 +03:00
|
|
|
{
|
2016-09-29 09:55:20 +03:00
|
|
|
RefPtr<File> file = GetAsFile(aSubjectPrincipal, aRv);
|
2016-07-29 14:42:33 +03:00
|
|
|
if (NS_WARN_IF(aRv.Failed()) || !file) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIGlobalObject> global;
|
|
|
|
// This is annoying, but DataTransfer may have various things as parent.
|
|
|
|
nsCOMPtr<EventTarget> target =
|
2016-08-18 01:39:15 +03:00
|
|
|
do_QueryInterface(mDataTransfer->GetParentObject());
|
2016-07-29 14:42:33 +03:00
|
|
|
if (target) {
|
|
|
|
global = target->GetOwnerGlobal();
|
|
|
|
} else {
|
|
|
|
nsCOMPtr<nsIDOMEvent> event =
|
2016-08-18 01:39:15 +03:00
|
|
|
do_QueryInterface(mDataTransfer->GetParentObject());
|
2016-07-29 14:42:33 +03:00
|
|
|
if (event) {
|
|
|
|
global = event->InternalDOMEvent()->GetParentObject();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!global) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-08-18 10:17:02 +03:00
|
|
|
RefPtr<FileSystem> fs = FileSystem::Create(global);
|
2016-08-18 10:17:25 +03:00
|
|
|
RefPtr<FileSystemEntry> entry;
|
2016-07-29 14:42:33 +03:00
|
|
|
BlobImpl* impl = file->Impl();
|
|
|
|
MOZ_ASSERT(impl);
|
|
|
|
|
|
|
|
if (impl->IsDirectory()) {
|
|
|
|
nsAutoString fullpath;
|
|
|
|
impl->GetMozFullPathInternal(fullpath, aRv);
|
|
|
|
if (aRv.Failed()) {
|
|
|
|
aRv.SuppressException();
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIFile> directoryFile;
|
2017-01-18 06:48:29 +03:00
|
|
|
// fullPath is already in unicode, we don't have to use
|
|
|
|
// NS_NewNativeLocalFile.
|
|
|
|
nsresult rv = NS_NewLocalFile(fullpath, true,
|
|
|
|
getter_AddRefs(directoryFile));
|
2016-07-29 14:42:33 +03:00
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
RefPtr<Directory> directory = Directory::Create(global, directoryFile);
|
2016-11-03 09:55:30 +03:00
|
|
|
entry = new FileSystemDirectoryEntry(global, directory, nullptr, fs);
|
2016-07-29 14:42:33 +03:00
|
|
|
} else {
|
2016-11-03 09:55:30 +03:00
|
|
|
entry = new FileSystemFileEntry(global, file, nullptr, fs);
|
2016-07-29 14:42:33 +03:00
|
|
|
}
|
|
|
|
|
2016-08-18 10:17:25 +03:00
|
|
|
Sequence<RefPtr<FileSystemEntry>> entries;
|
2016-07-29 14:42:33 +03:00
|
|
|
if (!entries.AppendElement(entry, fallible)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
fs->CreateRoot(entries);
|
|
|
|
return entry.forget();
|
|
|
|
}
|
|
|
|
|
2015-08-28 22:21:08 +03:00
|
|
|
already_AddRefed<File>
|
2016-06-21 20:07:17 +03:00
|
|
|
DataTransferItem::CreateFileFromInputStream(nsIInputStream* aStream)
|
2015-08-28 22:21:08 +03:00
|
|
|
{
|
|
|
|
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;
|
2016-06-21 20:07:17 +03:00
|
|
|
nsresult rv = nsContentUtils::GetLocalizedString(nsContentUtils::eDOM_PROPERTIES,
|
|
|
|
key, fileName);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2015-08-28 22:21:08 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t available;
|
2016-06-21 20:07:17 +03:00
|
|
|
rv = aStream->Available(&available);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2015-08-28 22:21:08 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
void* data = nullptr;
|
2016-06-21 20:07:17 +03:00
|
|
|
rv = NS_ReadInputStreamToBuffer(aStream, &data, available);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
2015-08-28 22:21:08 +03:00
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2016-08-18 01:39:15 +03:00
|
|
|
return File::CreateMemoryFile(mDataTransfer, data, available, fileName,
|
2015-08-28 22:21:08 +03:00
|
|
|
mType, PR_Now());
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
2016-05-31 22:03:44 +03:00
|
|
|
DataTransferItem::GetAsString(FunctionStringCallback* aCallback,
|
2016-10-11 04:07:48 +03:00
|
|
|
nsIPrincipal& aSubjectPrincipal,
|
2015-08-28 22:21:08 +03:00
|
|
|
ErrorResult& aRv)
|
|
|
|
{
|
2016-09-23 21:44:47 +03:00
|
|
|
if (!aCallback) {
|
2015-08-28 22:21:08 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-06-21 20:07:17 +03:00
|
|
|
// Theoretically this should be done inside of the runnable, as it might be an
|
2016-08-18 01:39:15 +03:00
|
|
|
// expensive operation on some systems, however we wouldn't get access to the
|
|
|
|
// NS_ERROR_DOM_SECURITY_ERROR messages which may be raised by this method.
|
2016-10-11 04:07:48 +03:00
|
|
|
nsCOMPtr<nsIVariant> data = Data(&aSubjectPrincipal, aRv);
|
2016-08-18 01:39:15 +03:00
|
|
|
if (NS_WARN_IF(!data || aRv.Failed())) {
|
2015-08-28 22:21:08 +03:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2016-09-23 21:44:47 +03:00
|
|
|
// We have to check our kind after getting the data, because if we have
|
|
|
|
// external data and the OS lied to us (which unfortunately does happen
|
|
|
|
// sometimes), then we might not have the same type of data as we did coming
|
|
|
|
// into this function.
|
|
|
|
if (NS_WARN_IF(mKind != KIND_STRING)) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2015-08-28 22:21:08 +03:00
|
|
|
nsAutoString stringData;
|
2016-08-18 01:39:15 +03:00
|
|
|
nsresult rv = data->GetAsAString(stringData);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return;
|
|
|
|
}
|
2015-08-28 22:21:08 +03:00
|
|
|
|
|
|
|
// Dispatch the callback to the main thread
|
|
|
|
class GASRunnable final : public Runnable
|
|
|
|
{
|
|
|
|
public:
|
2016-05-31 22:03:44 +03:00
|
|
|
GASRunnable(FunctionStringCallback* aCallback,
|
2015-08-28 22:21:08 +03:00
|
|
|
const nsAString& aStringData)
|
|
|
|
: mCallback(aCallback), mStringData(aStringData)
|
|
|
|
{}
|
|
|
|
|
|
|
|
NS_IMETHOD Run() override
|
|
|
|
{
|
|
|
|
ErrorResult rv;
|
|
|
|
mCallback->Call(mStringData, rv);
|
2016-09-02 10:12:24 +03:00
|
|
|
NS_WARNING_ASSERTION(!rv.Failed(), "callback failed");
|
2015-08-28 22:21:08 +03:00
|
|
|
return rv.StealNSResult();
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
RefPtr<FunctionStringCallback> mCallback;
|
|
|
|
nsString mStringData;
|
|
|
|
};
|
|
|
|
|
|
|
|
RefPtr<GASRunnable> runnable = new GASRunnable(aCallback, stringData);
|
2017-03-28 19:50:14 +03:00
|
|
|
rv = NS_DispatchToMainThread(runnable);
|
2015-08-28 22:21:08 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
2017-03-28 19:50:14 +03:00
|
|
|
NS_WARNING("NS_DispatchToMainThread Failed in "
|
2015-08-28 22:21:08 +03:00
|
|
|
"DataTransferItem::GetAsString!");
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-08-18 01:39:15 +03:00
|
|
|
already_AddRefed<nsIVariant>
|
|
|
|
DataTransferItem::DataNoSecurityCheck()
|
|
|
|
{
|
|
|
|
if (!mData) {
|
|
|
|
FillInExternalData();
|
|
|
|
}
|
|
|
|
nsCOMPtr<nsIVariant> data = mData;
|
|
|
|
return data.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<nsIVariant>
|
|
|
|
DataTransferItem::Data(nsIPrincipal* aPrincipal, ErrorResult& aRv)
|
|
|
|
{
|
|
|
|
MOZ_ASSERT(aPrincipal);
|
|
|
|
|
|
|
|
nsCOMPtr<nsIVariant> variant = DataNoSecurityCheck();
|
|
|
|
|
|
|
|
// If the inbound principal is system, we can skip the below checks, as
|
|
|
|
// they will trivially succeed.
|
|
|
|
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
|
|
|
return variant.forget();
|
|
|
|
}
|
|
|
|
|
|
|
|
MOZ_ASSERT(!ChromeOnly(), "Non-chrome code shouldn't see a ChromeOnly DataTransferItem");
|
|
|
|
if (ChromeOnly()) {
|
|
|
|
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool checkItemPrincipal = mDataTransfer->IsCrossDomainSubFrameDrop() ||
|
|
|
|
(mDataTransfer->GetEventMessage() != eDrop &&
|
|
|
|
mDataTransfer->GetEventMessage() != ePaste);
|
|
|
|
|
|
|
|
// 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.
|
Bug 1330979 - Don't raise NS_ERROR_DOM_SECURITY_ERROR on DataTransfer access violations, r=baku
In our previous code, we would accidentially not perform the correct
security checks when retreiving data from DataTransferItems in some
situations. The regressing patch fixed this behavior and ensured that we
always perform the correct security checks, making any "insecure"
accesses (such as accesses to file data from the OS in DragEnter) would
raise an NS_ERROR_DOM_SECURTY_ERROR. This behavior is not consistent
with Chrome, and thus OneDrive would accidentally trigger an exception
and break its d&d handling logic.
With this patch our behavior for "insecure" accesses to file data from
the OS is more in line with Chrome's, in that we now don't raise an
exception, but instead just produce the value "null" when the data
should not be avaliable yet. From my quick test this fixes the problem
with OneDrive, and should be a fairly trivial patch to uplift to Beta,
with very little risk.
This patch doesn't include a test, as Drag and Drop is a very difficult
component to test in automation, however I am currently working on
defining a set of manual tests for the Drag and Drop component, and I'll
make sure that our behavior for "insecure" accesses is tested in those
tests in the future.
MozReview-Commit-ID: 4pnPyL1tgcV
2017-01-13 22:35:51 +03:00
|
|
|
//
|
|
|
|
// We don't want to fail with an exception in this siutation, rather we want
|
|
|
|
// to just pretend as though the stored data is "nullptr". This is consistent
|
|
|
|
// with Chrome's behavior and is less surprising for web applications which
|
|
|
|
// don't expect execptions to be raised when performing certain operations.
|
2016-08-18 01:39:15 +03:00
|
|
|
if (Principal() && checkItemPrincipal &&
|
|
|
|
!aPrincipal->Subsumes(Principal())) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!variant) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> data;
|
|
|
|
nsresult rv = variant->GetAsISupports(getter_AddRefs(data));
|
|
|
|
if (NS_SUCCEEDED(rv) && data) {
|
|
|
|
nsCOMPtr<EventTarget> pt = do_QueryInterface(data);
|
|
|
|
if (pt) {
|
|
|
|
nsIScriptContext* c = pt->GetContextForEventHandlers(&rv);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv) || !c)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsIGlobalObject* go = c->GetGlobalObject();
|
|
|
|
if (NS_WARN_IF(!go)) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIScriptObjectPrincipal> sp = do_QueryInterface(go);
|
|
|
|
MOZ_ASSERT(sp, "This cannot fail on the main thread.");
|
|
|
|
|
|
|
|
nsIPrincipal* dataPrincipal = sp->GetPrincipal();
|
|
|
|
if (NS_WARN_IF(!dataPrincipal || !aPrincipal->Equals(dataPrincipal))) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return variant.forget();
|
|
|
|
}
|
|
|
|
|
2015-08-28 22:21:08 +03:00
|
|
|
} // namespace dom
|
|
|
|
} // namespace mozilla
|