2017-10-27 01:08:41 +03:00
|
|
|
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
|
|
|
|
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
|
2015-08-28 22:21:08 +03:00
|
|
|
/* 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"
|
|
|
|
|
2019-03-19 08:24:39 +03:00
|
|
|
#include "mozilla/Attributes.h"
|
2015-08-28 22:21:08 +03:00
|
|
|
#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, mPrincipal,
|
2016-08-18 01:39:15 +03:00
|
|
|
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) {
|
2018-06-26 00:20:54 +03:00
|
|
|
return DataTransferItem_Binding::Wrap(aCx, this, aGivenProto);
|
2015-08-28 22:21:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
already_AddRefed<DataTransferItem> DataTransferItem::Clone(
|
2016-08-18 01:39:15 +03:00
|
|
|
DataTransfer* aDataTransfer) const {
|
|
|
|
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
|
2018-05-22 03:32:44 +03:00
|
|
|
if (RefPtr<Blob>(do_QueryObject(supports)) ||
|
2016-06-21 20:07:17 +03:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
Bug 998941 - part 2-3: Create new constructors of DataTransfer to set only plain text or nsITransferable r=smaug
DataTransfer should have more 2 constructors. One takes |const nsAString&| and
sets its data to the string. The other takes |nsITransferable*| and stores only
its data with DataTransferItem.
If given data is external resource like the case of `HTMLEditor::PasteTransferable()`,
we should copy its data to each DataTransferItem because nsITransferable is not
cycle-collectable, but DataTransfer may be grabbed by JS. Unfortunately, adding
new path to initialize DataTransfer with nsITransferable instance is too risky
because DataTransfer and DataTransferItem work together to initialize each of
them if DataTransfer is in external mode. Therefore, this patch makes the
new constructor temporarily sets it to in external mode, then, cache usable types
first, then, call `FillAllExternalData()` to make each DataTransferItem initializes
its data by itself, finally, make the constructor set it to internal mode and release
nsITransferable instance. This is ugly implementation but the most reasonable
way for now because:
- We don't need to change DataTransfer nor DataTransferItem a lot in this bug.
- We don't need to duplicate any code like a loop in `CacheExternalData()`.
In another bug, we should redesign DataTransfer and DataTransferItem to
make DataTransferable easier to be added new constructors.
Differential Revision: https://phabricator.services.mozilla.com/D19298
--HG--
extra : moz-landing-system : lando
2019-02-19 10:13:20 +03:00
|
|
|
nsCOMPtr<nsITransferable> trans = mDataTransfer->GetTransferable();
|
|
|
|
if (!trans) {
|
|
|
|
trans = do_CreateInstance("@mozilla.org/widget/transferable;1");
|
|
|
|
if (NS_WARN_IF(!trans)) {
|
|
|
|
return;
|
|
|
|
}
|
2015-08-28 22:21:08 +03:00
|
|
|
|
Bug 998941 - part 2-3: Create new constructors of DataTransfer to set only plain text or nsITransferable r=smaug
DataTransfer should have more 2 constructors. One takes |const nsAString&| and
sets its data to the string. The other takes |nsITransferable*| and stores only
its data with DataTransferItem.
If given data is external resource like the case of `HTMLEditor::PasteTransferable()`,
we should copy its data to each DataTransferItem because nsITransferable is not
cycle-collectable, but DataTransfer may be grabbed by JS. Unfortunately, adding
new path to initialize DataTransfer with nsITransferable instance is too risky
because DataTransfer and DataTransferItem work together to initialize each of
them if DataTransfer is in external mode. Therefore, this patch makes the
new constructor temporarily sets it to in external mode, then, cache usable types
first, then, call `FillAllExternalData()` to make each DataTransferItem initializes
its data by itself, finally, make the constructor set it to internal mode and release
nsITransferable instance. This is ugly implementation but the most reasonable
way for now because:
- We don't need to change DataTransfer nor DataTransferItem a lot in this bug.
- We don't need to duplicate any code like a loop in `CacheExternalData()`.
In another bug, we should redesign DataTransfer and DataTransferItem to
make DataTransferable easier to be added new constructors.
Differential Revision: https://phabricator.services.mozilla.com/D19298
--HG--
extra : moz-landing-system : lando
2019-02-19 10:13:20 +03:00
|
|
|
trans->Init(nullptr);
|
|
|
|
trans->AddDataFlavor(format);
|
2015-08-28 22:21:08 +03:00
|
|
|
|
Bug 998941 - part 2-3: Create new constructors of DataTransfer to set only plain text or nsITransferable r=smaug
DataTransfer should have more 2 constructors. One takes |const nsAString&| and
sets its data to the string. The other takes |nsITransferable*| and stores only
its data with DataTransferItem.
If given data is external resource like the case of `HTMLEditor::PasteTransferable()`,
we should copy its data to each DataTransferItem because nsITransferable is not
cycle-collectable, but DataTransfer may be grabbed by JS. Unfortunately, adding
new path to initialize DataTransfer with nsITransferable instance is too risky
because DataTransfer and DataTransferItem work together to initialize each of
them if DataTransfer is in external mode. Therefore, this patch makes the
new constructor temporarily sets it to in external mode, then, cache usable types
first, then, call `FillAllExternalData()` to make each DataTransferItem initializes
its data by itself, finally, make the constructor set it to internal mode and release
nsITransferable instance. This is ugly implementation but the most reasonable
way for now because:
- We don't need to change DataTransfer nor DataTransferItem a lot in this bug.
- We don't need to duplicate any code like a loop in `CacheExternalData()`.
In another bug, we should redesign DataTransfer and DataTransferItem to
make DataTransferable easier to be added new constructors.
Differential Revision: https://phabricator.services.mozilla.com/D19298
--HG--
extra : moz-landing-system : lando
2019-02-19 10:13:20 +03:00
|
|
|
if (mDataTransfer->GetEventMessage() == ePaste) {
|
|
|
|
MOZ_ASSERT(mIndex == 0, "index in clipboard must be 0");
|
2015-08-28 22:21:08 +03:00
|
|
|
|
Bug 998941 - part 2-3: Create new constructors of DataTransfer to set only plain text or nsITransferable r=smaug
DataTransfer should have more 2 constructors. One takes |const nsAString&| and
sets its data to the string. The other takes |nsITransferable*| and stores only
its data with DataTransferItem.
If given data is external resource like the case of `HTMLEditor::PasteTransferable()`,
we should copy its data to each DataTransferItem because nsITransferable is not
cycle-collectable, but DataTransfer may be grabbed by JS. Unfortunately, adding
new path to initialize DataTransfer with nsITransferable instance is too risky
because DataTransfer and DataTransferItem work together to initialize each of
them if DataTransfer is in external mode. Therefore, this patch makes the
new constructor temporarily sets it to in external mode, then, cache usable types
first, then, call `FillAllExternalData()` to make each DataTransferItem initializes
its data by itself, finally, make the constructor set it to internal mode and release
nsITransferable instance. This is ugly implementation but the most reasonable
way for now because:
- We don't need to change DataTransfer nor DataTransferItem a lot in this bug.
- We don't need to duplicate any code like a loop in `CacheExternalData()`.
In another bug, we should redesign DataTransfer and DataTransferItem to
make DataTransferable easier to be added new constructors.
Differential Revision: https://phabricator.services.mozilla.com/D19298
--HG--
extra : moz-landing-system : lando
2019-02-19 10:13:20 +03:00
|
|
|
nsCOMPtr<nsIClipboard> clipboard =
|
|
|
|
do_GetService("@mozilla.org/widget/clipboard;1");
|
|
|
|
if (!clipboard || mDataTransfer->ClipboardType() < 0) {
|
|
|
|
return;
|
|
|
|
}
|
2015-08-28 22:21:08 +03:00
|
|
|
|
Bug 998941 - part 2-3: Create new constructors of DataTransfer to set only plain text or nsITransferable r=smaug
DataTransfer should have more 2 constructors. One takes |const nsAString&| and
sets its data to the string. The other takes |nsITransferable*| and stores only
its data with DataTransferItem.
If given data is external resource like the case of `HTMLEditor::PasteTransferable()`,
we should copy its data to each DataTransferItem because nsITransferable is not
cycle-collectable, but DataTransfer may be grabbed by JS. Unfortunately, adding
new path to initialize DataTransfer with nsITransferable instance is too risky
because DataTransfer and DataTransferItem work together to initialize each of
them if DataTransfer is in external mode. Therefore, this patch makes the
new constructor temporarily sets it to in external mode, then, cache usable types
first, then, call `FillAllExternalData()` to make each DataTransferItem initializes
its data by itself, finally, make the constructor set it to internal mode and release
nsITransferable instance. This is ugly implementation but the most reasonable
way for now because:
- We don't need to change DataTransfer nor DataTransferItem a lot in this bug.
- We don't need to duplicate any code like a loop in `CacheExternalData()`.
In another bug, we should redesign DataTransfer and DataTransferItem to
make DataTransferable easier to be added new constructors.
Differential Revision: https://phabricator.services.mozilla.com/D19298
--HG--
extra : moz-landing-system : lando
2019-02-19 10:13:20 +03:00
|
|
|
nsresult rv = clipboard->GetData(trans, mDataTransfer->ClipboardType());
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
|
|
|
|
if (!dragSession) {
|
|
|
|
return;
|
|
|
|
}
|
2015-08-28 22:21:08 +03:00
|
|
|
|
Bug 998941 - part 2-3: Create new constructors of DataTransfer to set only plain text or nsITransferable r=smaug
DataTransfer should have more 2 constructors. One takes |const nsAString&| and
sets its data to the string. The other takes |nsITransferable*| and stores only
its data with DataTransferItem.
If given data is external resource like the case of `HTMLEditor::PasteTransferable()`,
we should copy its data to each DataTransferItem because nsITransferable is not
cycle-collectable, but DataTransfer may be grabbed by JS. Unfortunately, adding
new path to initialize DataTransfer with nsITransferable instance is too risky
because DataTransfer and DataTransferItem work together to initialize each of
them if DataTransfer is in external mode. Therefore, this patch makes the
new constructor temporarily sets it to in external mode, then, cache usable types
first, then, call `FillAllExternalData()` to make each DataTransferItem initializes
its data by itself, finally, make the constructor set it to internal mode and release
nsITransferable instance. This is ugly implementation but the most reasonable
way for now because:
- We don't need to change DataTransfer nor DataTransferItem a lot in this bug.
- We don't need to duplicate any code like a loop in `CacheExternalData()`.
In another bug, we should redesign DataTransfer and DataTransferItem to
make DataTransferable easier to be added new constructors.
Differential Revision: https://phabricator.services.mozilla.com/D19298
--HG--
extra : moz-landing-system : lando
2019-02-19 10:13:20 +03:00
|
|
|
nsresult rv = dragSession->GetData(trans, mIndex);
|
|
|
|
if (NS_WARN_IF(NS_FAILED(rv))) {
|
|
|
|
return;
|
|
|
|
}
|
2015-08-28 22:21:08 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsISupports> data;
|
2018-11-20 19:59:56 +03:00
|
|
|
nsresult rv = trans->GetTransferData(format, getter_AddRefs(data));
|
2015-08-28 22:21:08 +03:00
|
|
|
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);
|
|
|
|
}
|
|
|
|
|
2016-10-11 04:07:48 +03:00
|
|
|
already_AddRefed<File> DataTransferItem::GetAsFile(
|
|
|
|
nsIPrincipal& aSubjectPrincipal, 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;
|
|
|
|
}
|
|
|
|
|
2018-05-22 03:32:44 +03:00
|
|
|
if (RefPtr<Blob> blob = do_QueryObject(supports)) {
|
2016-06-21 20:07:17 +03:00
|
|
|
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> DataTransferItem::GetAsEntry(
|
2016-10-11 04:07:48 +03:00
|
|
|
nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) {
|
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 {
|
2018-04-20 19:55:31 +03:00
|
|
|
RefPtr<Event> event = do_QueryObject(mDataTransfer->GetParentObject());
|
2016-07-29 14:42:33 +03:00
|
|
|
if (event) {
|
2018-04-20 19:55:31 +03:00
|
|
|
global = event->GetParentObject();
|
2016-07-29 14:42:33 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
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();
|
|
|
|
}
|
|
|
|
|
2016-06-21 20:07:17 +03:00
|
|
|
already_AddRefed<File> 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";
|
|
|
|
}
|
|
|
|
|
2017-08-04 07:40:52 +03:00
|
|
|
nsAutoString 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;
|
|
|
|
void* data = nullptr;
|
2017-11-09 13:18:09 +03:00
|
|
|
rv = NS_ReadInputStreamToBuffer(aStream, &data, -1, &available);
|
2016-06-21 20:07:17 +03:00
|
|
|
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, mType,
|
2015-08-28 22:21:08 +03:00
|
|
|
PR_Now());
|
|
|
|
}
|
|
|
|
|
2016-05-31 22:03:44 +03:00
|
|
|
void 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:
|
2017-06-12 22:34:10 +03:00
|
|
|
GASRunnable(FunctionStringCallback* aCallback, const nsAString& aStringData)
|
|
|
|
: mozilla::Runnable("GASRunnable"),
|
|
|
|
mCallback(aCallback),
|
|
|
|
mStringData(aStringData) {}
|
2015-08-28 22:21:08 +03:00
|
|
|
|
2019-03-19 08:24:39 +03:00
|
|
|
// MOZ_CAN_RUN_SCRIPT_BOUNDARY until runnables are opted into
|
|
|
|
// MOZ_CAN_RUN_SCRIPT. See bug 1535398.
|
|
|
|
MOZ_CAN_RUN_SCRIPT_BOUNDARY
|
2015-08-28 22:21:08 +03:00
|
|
|
NS_IMETHOD Run() override {
|
|
|
|
ErrorResult rv;
|
2019-03-21 08:33:33 +03:00
|
|
|
// mCallback is const, so we never null it out until our destructor.
|
|
|
|
MOZ_KnownLive(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();
|
|
|
|
}
|
2018-11-30 13:46:48 +03:00
|
|
|
|
2015-08-28 22:21:08 +03:00
|
|
|
private:
|
2019-03-19 08:24:39 +03:00
|
|
|
const RefPtr<FunctionStringCallback> mCallback;
|
2015-08-28 22:21:08 +03:00
|
|
|
nsString mStringData;
|
|
|
|
};
|
|
|
|
|
|
|
|
RefPtr<GASRunnable> runnable = new GASRunnable(aCallback, stringData);
|
2017-03-16 03:11:50 +03:00
|
|
|
|
|
|
|
// DataTransfer.mParent might be EventTarget, nsIGlobalObject, ClipboardEvent
|
|
|
|
// nsPIDOMWindowOuter, null
|
|
|
|
nsISupports* parent = mDataTransfer->GetParentObject();
|
|
|
|
nsCOMPtr<nsIGlobalObject> global = do_QueryInterface(parent);
|
|
|
|
if (parent && !global) {
|
|
|
|
if (nsCOMPtr<dom::EventTarget> target = do_QueryInterface(parent)) {
|
|
|
|
global = target->GetOwnerGlobal();
|
2018-04-20 19:55:31 +03:00
|
|
|
} else if (RefPtr<Event> event = do_QueryObject(parent)) {
|
|
|
|
global = event->GetParentObject();
|
2017-03-16 03:11:50 +03:00
|
|
|
}
|
|
|
|
}
|
|
|
|
if (global) {
|
2017-07-26 11:13:35 +03:00
|
|
|
rv = global->Dispatch(TaskCategory::Other, runnable.forget());
|
2017-03-16 03:11:50 +03:00
|
|
|
} else {
|
|
|
|
rv = NS_DispatchToMainThread(runnable);
|
|
|
|
}
|
2015-08-28 22:21:08 +03:00
|
|
|
if (NS_FAILED(rv)) {
|
2017-03-16 03:11:50 +03:00
|
|
|
NS_WARNING(
|
|
|
|
"Dispatch to main thread 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);
|
|
|
|
|
|
|
|
// If the inbound principal is system, we can skip the below checks, as
|
|
|
|
// they will trivially succeed.
|
|
|
|
if (nsContentUtils::IsSystemPrincipal(aPrincipal)) {
|
2017-09-06 18:25:01 +03:00
|
|
|
return DataNoSecurityCheck();
|
2016-08-18 01:39:15 +03:00
|
|
|
}
|
|
|
|
|
2017-09-06 18:25:01 +03:00
|
|
|
// We should not allow raw data to be accessed from a Protected DataTransfer.
|
|
|
|
// We don't prevent this access if the accessing document is Chrome.
|
|
|
|
if (mDataTransfer->IsProtected()) {
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
nsCOMPtr<nsIVariant> variant = DataNoSecurityCheck();
|
|
|
|
|
2016-08-18 01:39:15 +03:00
|
|
|
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 &&
|
Bug 998941 - part 2-3: Create new constructors of DataTransfer to set only plain text or nsITransferable r=smaug
DataTransfer should have more 2 constructors. One takes |const nsAString&| and
sets its data to the string. The other takes |nsITransferable*| and stores only
its data with DataTransferItem.
If given data is external resource like the case of `HTMLEditor::PasteTransferable()`,
we should copy its data to each DataTransferItem because nsITransferable is not
cycle-collectable, but DataTransfer may be grabbed by JS. Unfortunately, adding
new path to initialize DataTransfer with nsITransferable instance is too risky
because DataTransfer and DataTransferItem work together to initialize each of
them if DataTransfer is in external mode. Therefore, this patch makes the
new constructor temporarily sets it to in external mode, then, cache usable types
first, then, call `FillAllExternalData()` to make each DataTransferItem initializes
its data by itself, finally, make the constructor set it to internal mode and release
nsITransferable instance. This is ugly implementation but the most reasonable
way for now because:
- We don't need to change DataTransfer nor DataTransferItem a lot in this bug.
- We don't need to duplicate any code like a loop in `CacheExternalData()`.
In another bug, we should redesign DataTransfer and DataTransferItem to
make DataTransferable easier to be added new constructors.
Differential Revision: https://phabricator.services.mozilla.com/D19298
--HG--
extra : moz-landing-system : lando
2019-02-19 10:13:20 +03:00
|
|
|
mDataTransfer->GetEventMessage() != ePaste &&
|
|
|
|
mDataTransfer->GetEventMessage() != eEditorInput);
|
2016-08-18 01:39:15 +03:00
|
|
|
|
|
|
|
// 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) {
|
2018-04-05 20:42:40 +03:00
|
|
|
nsIGlobalObject* go = pt->GetOwnerGlobal();
|
2016-08-18 01:39:15 +03:00
|
|
|
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
|