Files
palemoon27/dom/events/DataTransfer.cpp
T
roytam1 cf2d7d1ae9 import changes from `dev' branch of rmottola/Arctic-Fox:
- Bug 1212114 - Stop using dom::Promise::MaybeRejectBrokenly() in various FileSystemTaskBase subclasses. r=baku (9f6ea5db27)
- Bug 1253534 - Suspicious code with probably reversed parms in call to IsSingleLineTextControl, r=mounir (1a5ee1fd1e)
- Bug 769117 - Mochitests for youtube flash -> html5 rewriting; r=bz r=hsivonen (6324471dd9)
- Bug 1250148 - FormData should treat empty input type=file as empty string in FormData and as unnamed Blob in HTML submission, r=smaug (7043113247)
- Bug 1173320 - patch 1/8 - Implement Directory object as string and not as BlobImpl, r=smaug (eca4bec6ea)
- Bug 1173320 - patch 2/8 - Proper naming for the FileSystem path serialization, r=smaug (e1604ff2b1)
- Bug 1173320 - patch 3/8 - Improve the Windows path management, r=smaug (f8da8541b5)
- Bug 1252347 - Provide missing implementations of nsIBaseWindow::SetPositionDesktopPix. r=emk (97f9b92465)
- Bug 1235066 - SVG elements should not display title attributes as tooltips. r=enndeakin (a77e809688)
- Bug 1251809 - Add input[type=file] tooltip support for e10s. r=ehsan (9f72cf3cd2)
- Bug 1173320 - patch 4/8 - Directory in FileList, r=smaug (c09d445043)
- Bug 1246244 - Regression test. r=jaws,Margaret (8de084f9db)
- Bug 1224105 - Allow windowless chrome docshells containing content docshells (r=smaug) (3343a2a966)
- Bug 1230267 - Inconsistent display of SVG title as tooltip when multi-process is enabled. r=jst (b3fc298b7f)
- Bug 1173320 - patch 5/8 - Cleanup manual string path management, r=smaug (8f6f0c4e44)
- Bug 1250403 - Part 1. Define ARCH_CPU_ARM64 instead of ARCH_CPU_AARCH64. r=billm (d93a0b54da)
- Bug 1250403 - Part 2. Import crbug #354405 for aarch64. r=billm (9dca949bcc)
- Bug 1246501 - Add ppc specific atomic operations to ipc/chromium. r=Waldo (8054b612be)
- Bug 1257305 - Avoid VS2015 about casting int to void*. r=dvander (6234acf6fc)
- Bug 1207401 - Send B2G sandbox logging to both stderr and logcat. r=kang (ae404aa5ca)
- Bug 1173320 - patch 6/8 - Make FileList clonable to workers if it doesn't contain Directories, r=smaug (ea6ba42f31)
- Bug 1173320 - patch 7/8 - Tests for FileList and Directories, r=smaug (3f11503300)
- Bug 1222522, part 1 - Make most dom/devicestorage/ tests work with e10s. r=dhylands (3cc7e339a5)
- Bug 1173320 - patch 8/8 - Fix e10s tests for DeviceStorage API, r=smaug (949454bae7)
- Bug 1222522, part 2 - Inline devicestorage_cleanup() in dom/devicestorage/ tests. r=dhylands (bcdee11385)
- Bug 1258137 - OSFileSystem should not be kept alive by more than 1 Directory, r=smaug (225775f48d)
- Bug 1255867. Remove some unnecessary AutoJSAPI uses. r=bholley (618cf018e8)
- Bug 1237173 - Part2: Change type of duration to Maybe<StickyTimeDuration>. r=birtles (6b83473e05)
- Bug 1238469 - Part 1: Refactor b2g emulator tests to remove the usage of custom mozharness configs; r=ahal (a128c8a8bb)
- Bug 1238469 - Part 2: Refactor b2g mulet tests to remove the usage of custom mozharness configs; r=ahal (6c7ced3419)
- Bug 1238469 - Part 3: Use b2g_emulator_unittest.py for b2g marionette tests; r=ahal (5fdb88b0e1)
- Bug 1237173 - Part3: Throw TypeError if duration is NaN, negative value or not 'auto' string. r=birtles, r=smaug (d60b0318d2)
- Bug 1136567 - Marionette test for selection carets' positions after changing orientation of device. r=Automatedtester (e7b25b1e4f)
- Bug 1138839 - Part1 - Marionette test for selecting text inside an iframe. r=automatedtester (45db397a94)
- Bug 1138839 - Part2 - Fix naming issue in marionette test for selection carets. r=automatedtester (c54850d9d3)
- Bug 1198542: Update Marionette element IDs to be valid UUID. r=ato This allows WebDriver compatibility to create valid URI. (1b3de245de)
- Bug 1204496: When searching by link text start from the startNode and not the rootNode; r=ato (0dfa3b8830)
- Bug 1157725 - Rewrite test_mouse_action.py to be more robust. r=ato (40273abca2)
- Bug 1141519: added test that puts marionette into a position that can cause hangs when in content scope; r=jgriffin (c99d8cf325)
- Bug 1246407 - Rename parent directories for Marionette client and test harness; r=automatedtester (79d4e521fd)
- Bug 1253989 Part 3 - Use @parameterized to rewrite selection mode tests. r=mtseng (baf189f1d5)
- Bug 1253989 Part 4 - Rename AccessibleCaret test files. r=mtseng (d562edc8f4)
- Bug 1251519 Part 3 - Add regression tests for caret dragging. r=mats (048dd5f103)
- Bug 1253989 Part 5 - Remove touch caret and selection carets naming. r=mtseng (9929425b6d)
- Bug 1253989 Part 6 - Refactor open_test_html(). r=mtseng (25a2424ca1)
- Bug 1251519 Part 4 - Add tests for dragging caret to content boundary. r=mats (489b47269b)
- Bug 1251519 Part 5 - Use union rect of child frames for clamping. r=mats (5520416749)
- Bug 1216924 - Don't align to tiles (either real or virtual) if displayport suppression is enabled. r=BenWa (d470d188e4)
- Bug 1254273 - Align the displayport to a max of 256 pixels even if the layer is larger. r=BenWa (0cf8cc0b6c)
- Bug 1257938 part 3: Remove support for the "layout.css.sticky.enabled" pref (so we'll unconditionally support "position: sticky"). r=corey (fe12efd18f)
- Bug 1257938 part 1: Adjust automated tests to assume position:sticky is unconditionally supported. r=corey (e749ac579e)
- Bug 1257938 part 2: Remove separation between test_position_sticky.html & its helper-file, now that it doesn't need to tweak a pref. r=corey (631edab31a)
- Bug 1257491 - Ensure that if the peek-messages code modifies the displayport, we schedule a repaint. r=BenWa (d6fb6ff96c)
- Bug 1255006 - Ensure the displayport rect takes priority over a suppressed-margins displayport. r=kats (aa6cbc0250)
- Bug 1259235 - Add IsScrollFrameWithSnapping to speed up event regions. r=mstange (2a744c311e)
- Bug 1185140 - [css-grid][flexbox] Make grid/flex item blockification happen before creating table pseudos, per the latest specs. r=dholbert (dddb8b17ef)
- Bug 1224424 - Replace mask-mode:auto keyword by mask-mode:match-source; r=dbaron (5a51b3b301)
- Bug 1252039 - corrected MOZ_ASSERT expression in SeparatorRequiredBetweenTokens. r=dbaron (08790aa514)
- Bug 1243734 - Part 1. Use MOZ_ENABLE_MASK_AS_SHORTHAND to define the type of mask property; r=dbaron (d8cd3a1c4a)
- Bug 1243734 - Part 2. Set up gCSSProperties depends on mask-as-shorth and; r=dbaron (aa6b0259d8)
- Bug 1243734 - Part 3. Set mask-mode reftest as failure before enable mask-as-shorthand; r=dbaron (6a326fbaf2)
- Bug 1243734 - Part 5. Add MOZ_ENABLE_MASK_AS_SHORTHAND compile flag; r=ted r=dbaron (fcc1344ac8)
- Bug 1142531: Check more bits in nsStyleContext::MoveTo assertion. r=heycam (8b62b139df)
- Bug 1258147 - Pierce through display:contents style context ancestors when looking for CB context to compare our writing-mode to. r=jfkthame (956d8c25e5)
- Bug 823483 patch 1 - Check for percentage max-width in addition to percentage width when deciding to ignore intrinsic min-width of replaced elements. r=dholbert (f88cb5f6a8)
- Bug 823483 patch 3 - Limit effect of percentage width and max-width on intrinsic size to elements with replaced element sizing. r=dholbert (2573c3cfff)
- Bug 823483 patch 4 - Make a percentage max-width override a fixed width for replaced element intrinsic size computation. r=dholbert (dda859f06c)
- Bug 823483 patch 5 - Make (again) percentage width on text inputs make intrinsic minimum width be 0. r=dholbert (d46ada73ef)
- Bug 1247929 patch 2 - Hard-code the Web-compatible set of form controls whose intrinsic minimum inline-size shrinks to 0 when inline-size (width) is specified as a percentage. r=dholbert (e4f0c80fcb)
- Bug 1254968 - Add support for running JS builtins' constructors over Xray wrappers without unwrapping the newTarget. r=bholley,f=bz (56213ae395)
- Bug 1249123 - Add telemetry for __defineGetter__/__defineSetter__ |this| values. data-review=bsmedberg r=till (52c5fd3488)
- Bug 1232639 - Implement Object.{values,entries} in C++ to avoid native call overhead in tight loop. r=jorendorff (7262497283)
- Bug 1254966 - Disambiguate JS Telemetry macro names. r=evilpie (781d0916c4)
- Bug 1254384: Use generic shell switch syntax in js/src/jit-test tests. r=nbp (6f5975cc55)
- Bug 1253016 - Remove legacy __defineGetter__/__defineSetter__ this behavior. r=till (cf1b7ad28c)
- Bug 1253016 - Implement and test the new spec for legacy functions. r=till (1ff7762e3e)
2024-02-28 12:10:08 +08:00

1363 lines
38 KiB
C++

/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* 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 "mozilla/ArrayUtils.h"
#include "mozilla/BasicEvents.h"
#include "DataTransfer.h"
#include "nsIDOMDocument.h"
#include "nsISupportsPrimitives.h"
#include "nsIScriptSecurityManager.h"
#include "mozilla/dom/DOMStringList.h"
#include "nsError.h"
#include "nsIDragService.h"
#include "nsIClipboard.h"
#include "nsContentUtils.h"
#include "nsIContent.h"
#include "nsCRT.h"
#include "nsIScriptObjectPrincipal.h"
#include "nsIScriptContext.h"
#include "nsIDocument.h"
#include "nsIScriptGlobalObject.h"
#include "nsVariant.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DataTransferBinding.h"
#include "mozilla/dom/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"
namespace mozilla {
namespace dom {
inline void
ImplCycleCollectionTraverse(nsCycleCollectionTraversalCallback& aCallback,
TransferItem& aField,
const char* aName,
uint32_t aFlags = 0)
{
ImplCycleCollectionTraverse(aCallback, aField.mData, aName, aFlags);
}
NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mFileList)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mItems)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget)
NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage)
NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFileList)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mItems)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DataTransfer)
NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer)
NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer)
NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY
NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer)
NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer)
NS_INTERFACE_MAP_END
// the size of the array
const char DataTransfer::sEffects[8][9] = {
"none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all"
};
DataTransfer::DataTransfer(nsISupports* aParent, EventMessage aEventMessage,
bool aIsExternal, int32_t aClipboardType)
: mParent(aParent)
, mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE)
, mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED)
, mEventMessage(aEventMessage)
, mCursorState(false)
, mReadOnly(true)
, mIsExternal(aIsExternal)
, mUserCancelled(false)
, mIsCrossDomainSubFrameDrop(false)
, mClipboardType(aClipboardType)
, mDragImageX(0)
, mDragImageY(0)
{
// For these events, we want to be able to add data to the data transfer, so
// clear the readonly state. Otherwise, the data is already present. For
// external usage, cache the data from the native clipboard or drag.
if (aEventMessage == eCut ||
aEventMessage == eCopy ||
aEventMessage == eDragStart ||
aEventMessage == eLegacyDragGesture) {
mReadOnly = false;
} else if (mIsExternal) {
if (aEventMessage == ePaste) {
CacheExternalClipboardFormats();
} else if (aEventMessage >= eDragDropEventFirst &&
aEventMessage <= eDragDropEventLast) {
CacheExternalDragFormats();
}
}
}
DataTransfer::DataTransfer(nsISupports* aParent,
EventMessage aEventMessage,
const uint32_t aEffectAllowed,
bool aCursorState,
bool aIsExternal,
bool aUserCancelled,
bool aIsCrossDomainSubFrameDrop,
int32_t aClipboardType,
nsTArray<nsTArray<TransferItem> >& aItems,
Element* aDragImage,
uint32_t aDragImageX,
uint32_t aDragImageY)
: mParent(aParent)
, mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE)
, mEffectAllowed(aEffectAllowed)
, mEventMessage(aEventMessage)
, mCursorState(aCursorState)
, mReadOnly(true)
, mIsExternal(aIsExternal)
, mUserCancelled(aUserCancelled)
, mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop)
, mClipboardType(aClipboardType)
, mItems(aItems)
, mDragImage(aDragImage)
, mDragImageX(aDragImageX)
, mDragImageY(aDragImageY)
{
MOZ_ASSERT(mParent);
// 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
// modifiable, but those events should have been using the first constructor
// above.
NS_ASSERTION(aEventMessage != eLegacyDragGesture &&
aEventMessage != eDragStart,
"invalid event type for DataTransfer constructor");
}
DataTransfer::~DataTransfer()
{}
// static
already_AddRefed<DataTransfer>
DataTransfer::Constructor(const GlobalObject& aGlobal,
const nsAString& aEventType, bool aIsExternal,
ErrorResult& aRv)
{
nsAutoCString onEventType("on");
AppendUTF16toUTF8(aEventType, onEventType);
nsCOMPtr<nsIAtom> eventTypeAtom = do_GetAtom(onEventType);
if (!eventTypeAtom) {
aRv.Throw(NS_ERROR_OUT_OF_MEMORY);
return nullptr;
}
EventMessage eventMessage = nsContentUtils::GetEventMessage(eventTypeAtom);
RefPtr<DataTransfer> transfer = new DataTransfer(aGlobal.GetAsSupports(),
eventMessage, aIsExternal,
-1);
return transfer.forget();
}
JSObject*
DataTransfer::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aGivenProto)
{
return DataTransferBinding::Wrap(aCx, this, aGivenProto);
}
NS_IMETHODIMP
DataTransfer::GetDropEffect(nsAString& aDropEffect)
{
nsString dropEffect;
GetDropEffect(dropEffect);
aDropEffect = dropEffect;
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::SetDropEffect(const nsAString& aDropEffect)
{
// the drop effect can only be 'none', 'copy', 'move' or 'link'.
for (uint32_t e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) {
if (aDropEffect.EqualsASCII(sEffects[e])) {
// don't allow copyMove
if (e != (nsIDragService::DRAGDROP_ACTION_COPY |
nsIDragService::DRAGDROP_ACTION_MOVE))
mDropEffect = e;
break;
}
}
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::GetEffectAllowed(nsAString& aEffectAllowed)
{
nsString effectAllowed;
GetEffectAllowed(effectAllowed);
aEffectAllowed = effectAllowed;
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed)
{
if (aEffectAllowed.EqualsLiteral("uninitialized")) {
mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
return NS_OK;
}
static_assert(nsIDragService::DRAGDROP_ACTION_NONE == 0,
"DRAGDROP_ACTION_NONE constant is wrong");
static_assert(nsIDragService::DRAGDROP_ACTION_COPY == 1,
"DRAGDROP_ACTION_COPY constant is wrong");
static_assert(nsIDragService::DRAGDROP_ACTION_MOVE == 2,
"DRAGDROP_ACTION_MOVE constant is wrong");
static_assert(nsIDragService::DRAGDROP_ACTION_LINK == 4,
"DRAGDROP_ACTION_LINK constant is wrong");
for (uint32_t e = 0; e < ArrayLength(sEffects); e++) {
if (aEffectAllowed.EqualsASCII(sEffects[e])) {
mEffectAllowed = e;
break;
}
}
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::GetDropEffectInt(uint32_t* aDropEffect)
{
*aDropEffect = mDropEffect;
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::SetDropEffectInt(uint32_t aDropEffect)
{
mDropEffect = aDropEffect;
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::GetEffectAllowedInt(uint32_t* aEffectAllowed)
{
*aEffectAllowed = mEffectAllowed;
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::SetEffectAllowedInt(uint32_t aEffectAllowed)
{
mEffectAllowed = aEffectAllowed;
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::GetMozUserCancelled(bool* aUserCancelled)
{
*aUserCancelled = MozUserCancelled();
return NS_OK;
}
FileList*
DataTransfer::GetFiles(ErrorResult& aRv)
{
return GetFileListInternal(aRv, nsContentUtils::SubjectPrincipal());
}
FileList*
DataTransfer::GetFileListInternal(ErrorResult& aRv,
nsIPrincipal* aSubjectPrincipal)
{
if (mEventMessage != eDrop &&
mEventMessage != 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 (aRv.Failed()) {
return nullptr;
}
if (!variant)
continue;
nsCOMPtr<nsISupports> supports;
nsresult rv = variant->GetAsISupports(getter_AddRefs(supports));
if (NS_FAILED(rv))
continue;
nsCOMPtr<nsIFile> file = do_QueryInterface(supports);
RefPtr<File> domFile;
if (file) {
#ifdef DEBUG
if (XRE_GetProcessType() == GeckoProcessType_Default) {
bool isDir;
file->IsDirectory(&isDir);
MOZ_ASSERT(!isDir, "How did we get here?");
}
#endif
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)
{
ErrorResult rv;
NS_IF_ADDREF(*aFileList = GetFileListInternal(rv, nsContentUtils::GetSystemPrincipal()));
return rv.StealNSResult();
}
already_AddRefed<DOMStringList>
DataTransfer::Types()
{
ErrorResult rv;
return MozTypesAt(0, rv);
}
NS_IMETHODIMP
DataTransfer::GetTypes(nsISupports** aTypes)
{
RefPtr<DOMStringList> types = Types();
types.forget(aTypes);
return NS_OK;
}
void
DataTransfer::GetData(const nsAString& aFormat, nsAString& aData,
ErrorResult& aRv)
{
// return an empty string if data for the format was not found
aData.Truncate();
nsCOMPtr<nsIVariant> data;
nsresult rv = GetDataAtInternal(aFormat, 0, nsContentUtils::SubjectPrincipal(), getter_AddRefs(data));
if (NS_FAILED(rv)) {
if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) {
aRv.Throw(rv);
}
return;
}
if (data) {
nsAutoString stringdata;
data->GetAsAString(stringdata);
// for the URL type, parse out the first URI from the list. The URIs are
// separated by newlines
nsAutoString lowercaseFormat;
nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat);
if (lowercaseFormat.EqualsLiteral("url")) {
int32_t lastidx = 0, idx;
int32_t length = stringdata.Length();
while (lastidx < length) {
idx = stringdata.FindChar('\n', lastidx);
// lines beginning with # are comments
if (stringdata[lastidx] == '#') {
if (idx == -1)
break;
}
else {
if (idx == -1)
aData.Assign(Substring(stringdata, lastidx));
else
aData.Assign(Substring(stringdata, lastidx, idx - lastidx));
aData = nsContentUtils::TrimWhitespace<nsCRT::IsAsciiSpace>(aData, true);
return;
}
lastidx = idx + 1;
}
}
else {
aData = stringdata;
}
}
}
NS_IMETHODIMP
DataTransfer::GetData(const nsAString& aFormat, nsAString& aData)
{
ErrorResult rv;
GetData(aFormat, aData, rv);
return rv.StealNSResult();
}
void
DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData,
ErrorResult& aRv)
{
RefPtr<nsVariantCC> variant = new nsVariantCC();
variant->SetAsAString(aData);
aRv = SetDataAtInternal(aFormat, variant, 0, nsContentUtils::SubjectPrincipal());
}
void
DataTransfer::ClearData(const Optional<nsAString>& aFormat, ErrorResult& aRv)
{
if (mReadOnly) {
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
return;
}
if (mItems.Length() == 0) {
return;
}
if (aFormat.WasPassed()) {
MozClearDataAtHelper(aFormat.Value(), 0, aRv);
} else {
MozClearDataAtHelper(EmptyString(), 0, aRv);
}
}
NS_IMETHODIMP
DataTransfer::ClearData(const nsAString& aFormat)
{
Optional<nsAString> format;
format = &aFormat;
ErrorResult rv;
ClearData(format, rv);
return rv.StealNSResult();
}
NS_IMETHODIMP
DataTransfer::GetMozItemCount(uint32_t* aCount)
{
*aCount = MozItemCount();
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::GetMozCursor(nsAString& aCursorState)
{
nsString cursor;
GetMozCursor(cursor);
aCursorState = cursor;
return NS_OK;
}
NS_IMETHODIMP
DataTransfer::SetMozCursor(const nsAString& aCursorState)
{
// Lock the cursor to an arrow during the drag.
mCursorState = aCursorState.EqualsLiteral("default");
return NS_OK;
}
already_AddRefed<nsINode>
DataTransfer::GetMozSourceNode()
{
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
if (!dragSession) {
return nullptr;
}
nsCOMPtr<nsIDOMNode> sourceNode;
dragSession->GetSourceNode(getter_AddRefs(sourceNode));
nsCOMPtr<nsINode> node = do_QueryInterface(sourceNode);
if (node && !nsContentUtils::LegacyIsCallerNativeCode()
&& !nsContentUtils::CanCallerAccess(node)) {
return nullptr;
}
return node.forget();
}
NS_IMETHODIMP
DataTransfer::GetMozSourceNode(nsIDOMNode** aSourceNode)
{
nsCOMPtr<nsINode> sourceNode = GetMozSourceNode();
if (!sourceNode) {
*aSourceNode = nullptr;
return NS_OK;
}
return CallQueryInterface(sourceNode, aSourceNode);
}
already_AddRefed<DOMStringList>
DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv) const
{
// Only the first item is valid for clipboard events
if (aIndex > 0 &&
(mEventMessage == eCut || mEventMessage == eCopy ||
mEventMessage == ePaste)) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return nullptr;
}
RefPtr<DOMStringList> types = new DOMStringList();
if (aIndex < mItems.Length()) {
bool addFile = false;
// 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::IsCallerChrome()) {
types->Clear();
types->Add(NS_LITERAL_STRING(kFileMime));
}
types->Add(NS_LITERAL_STRING("Files"));
}
}
return types.forget();
}
NS_IMETHODIMP
DataTransfer::MozTypesAt(uint32_t aIndex, nsISupports** aTypes)
{
ErrorResult rv;
RefPtr<DOMStringList> types = MozTypesAt(aIndex, rv);
types.forget(aTypes);
return rv.StealNSResult();
}
nsresult
DataTransfer::GetDataAtNoSecurityCheck(const nsAString& aFormat, uint32_t aIndex,
nsIVariant** aData)
{
return GetDataAtInternal(aFormat, aIndex, nsContentUtils::GetSystemPrincipal(), aData);
}
nsresult
DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex,
nsIPrincipal* aSubjectPrincipal, nsIVariant** aData)
{
*aData = nullptr;
if (aFormat.IsEmpty())
return NS_OK;
if (aIndex >= mItems.Length()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
// Only the first item is valid for clipboard events
if (aIndex > 0 &&
(mEventMessage == eCut || mEventMessage == eCopy ||
mEventMessage == ePaste)) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
nsAutoString format;
GetRealFormat(aFormat, format);
nsTArray<TransferItem>& item = mItems[aIndex];
// If this is a content caller, and a file is in the data transfer, only
// return the file type.
if (!format.EqualsLiteral(kFileMime) &&
!nsContentUtils::IsSystemPrincipal(nsContentUtils::SubjectPrincipal())) {
uint32_t count = item.Length();
for (uint32_t i = 0; i < count; i++) {
if (item[i].mFormat.EqualsLiteral(kFileMime)) {
return NS_OK;
}
}
}
// Check if the caller is allowed to access the drag data. Callers with
// chrome privileges can always read the data. During the
// drop event, allow retrieving the data except in the case where the
// source of the drag is in a child frame of the caller. In that case,
// we only allow access to data of the same principal. During other events,
// only allow access to the data with the same principal.
bool checkFormatItemPrincipal = mIsCrossDomainSubFrameDrop ||
(mEventMessage != eDrop && mEventMessage != eLegacyDragDrop &&
mEventMessage != ePaste);
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 (!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;
}
}
return NS_OK;
}
void
DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat,
uint32_t aIndex,
JS::MutableHandle<JS::Value> aRetval,
mozilla::ErrorResult& aRv)
{
nsCOMPtr<nsIVariant> data;
aRv = GetDataAtInternal(aFormat, aIndex, nsContentUtils::SubjectPrincipal(), getter_AddRefs(data));
if (aRv.Failed()) {
return;
}
if (!data) {
aRetval.setNull();
return;
}
JS::Rooted<JS::Value> result(aCx);
if (!VariantToJsval(aCx, data, aRetval)) {
aRv = NS_ERROR_FAILURE;
return;
}
}
nsresult
DataTransfer::SetDataAtInternal(const nsAString& aFormat, nsIVariant* aData,
uint32_t aIndex, nsIPrincipal* aSubjectPrincipal)
{
if (aFormat.IsEmpty()) {
return NS_OK;
}
if (mReadOnly) {
return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR;
}
// Specifying an index less than the current length will replace an existing
// item. Specifying an index equal to the current length will add a new item.
if (aIndex > mItems.Length()) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
// Only the first item is valid for clipboard events
if (aIndex > 0 &&
(mEventMessage == eCut || mEventMessage == eCopy ||
mEventMessage == ePaste)) {
return NS_ERROR_DOM_INDEX_SIZE_ERR;
}
// Don't allow non-chrome to add non-string or file data. We block file
// promises as well which are used internally for drags to the desktop.
if (!nsContentUtils::IsSystemPrincipal(aSubjectPrincipal)) {
if (aFormat.EqualsLiteral(kFilePromiseMime) ||
aFormat.EqualsLiteral(kFileMime)) {
return NS_ERROR_DOM_SECURITY_ERR;
}
uint16_t type;
aData->GetDataType(&type);
if (type == nsIDataType::VTYPE_INTERFACE ||
type == nsIDataType::VTYPE_INTERFACE_IS) {
return NS_ERROR_DOM_SECURITY_ERR;
}
}
return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal);
}
void
DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat,
JS::Handle<JS::Value> aData,
uint32_t aIndex, ErrorResult& aRv)
{
nsCOMPtr<nsIVariant> data;
aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData,
getter_AddRefs(data));
if (!aRv.Failed()) {
aRv = SetDataAtInternal(aFormat, data, aIndex, nsContentUtils::SubjectPrincipal());
}
}
void
DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex,
ErrorResult& aRv)
{
if (mReadOnly) {
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
return;
}
if (aIndex >= mItems.Length()) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
// Only the first item is valid for clipboard events
if (aIndex > 0 &&
(mEventMessage == eCut || mEventMessage == eCopy ||
mEventMessage == ePaste)) {
aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR);
return;
}
MozClearDataAtHelper(aFormat, aIndex, aRv);
}
void
DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex,
ErrorResult& aRv)
{
MOZ_ASSERT(!mReadOnly);
MOZ_ASSERT(aIndex < mItems.Length());
MOZ_ASSERT(aIndex == 0 ||
(mEventMessage != eCut && mEventMessage != eCopy &&
mEventMessage != ePaste));
nsAutoString format;
GetRealFormat(aFormat, format);
nsIPrincipal* principal = nsContentUtils::SubjectPrincipal();
// if the format is empty, clear all formats
bool clearall = format.IsEmpty();
nsTArray<TransferItem>& item = mItems[aIndex];
// count backwards so that the count and index don't have to be adjusted
// after removing an element
for (int32_t i = item.Length() - 1; i >= 0; i--) {
TransferItem& formatitem = item[i];
if (clearall || formatitem.mFormat.Equals(format)) {
// don't allow removing data that has a stronger principal
bool subsumes;
if (formatitem.mPrincipal && principal &&
(NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes)) {
aRv.Throw(NS_ERROR_DOM_SECURITY_ERR);
return;
}
item.RemoveElementAt(i);
// if a format was specified, break out. Otherwise, loop around until
// all formats have been removed
if (!clearall)
break;
}
}
// if the last format for an item is removed, remove the entire item
if (!item.Length())
mItems.RemoveElementAt(aIndex);
}
NS_IMETHODIMP
DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex)
{
ErrorResult rv;
MozClearDataAt(aFormat, aIndex, rv);
return rv.StealNSResult();
}
void
DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY,
ErrorResult& aRv)
{
if (mReadOnly) {
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
return;
}
mDragImage = &aImage;
mDragImageX = aX;
mDragImageY = aY;
}
NS_IMETHODIMP
DataTransfer::SetDragImage(nsIDOMElement* aImage, int32_t aX, int32_t aY)
{
ErrorResult rv;
nsCOMPtr<Element> image = do_QueryInterface(aImage);
if (image) {
SetDragImage(*image, aX, aY, rv);
}
return rv.StealNSResult();
}
already_AddRefed<Promise>
DataTransfer::GetFilesAndDirectories(ErrorResult& aRv)
{
nsCOMPtr<nsINode> parentNode = do_QueryInterface(mParent);
if (!parentNode) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
nsCOMPtr<nsIGlobalObject> global = parentNode->OwnerDoc()->GetScopeObject();
MOZ_ASSERT(global);
if (!global) {
aRv.Throw(NS_ERROR_FAILURE);
return nullptr;
}
RefPtr<Promise> p = Promise::Create(global, aRv);
if (aRv.Failed()) {
return nullptr;
}
if (!mFileList) {
GetFiles(aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
}
Sequence<OwningFileOrDirectory> filesAndDirsSeq;
mFileList->ToSequence(filesAndDirsSeq, aRv);
if (NS_WARN_IF(aRv.Failed())) {
return nullptr;
}
p->MaybeResolve(filesAndDirsSeq);
return p.forget();
}
void
DataTransfer::AddElement(Element& aElement, ErrorResult& aRv)
{
if (mReadOnly) {
aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR);
return;
}
mDragTarget = &aElement;
}
NS_IMETHODIMP
DataTransfer::AddElement(nsIDOMElement* aElement)
{
NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER);
nsCOMPtr<Element> element = do_QueryInterface(aElement);
NS_ENSURE_TRUE(element, NS_ERROR_INVALID_ARG);
ErrorResult rv;
AddElement(*element, rv);
return rv.StealNSResult();
}
nsresult
DataTransfer::Clone(nsISupports* aParent, EventMessage aEventMessage,
bool aUserCancelled, bool aIsCrossDomainSubFrameDrop,
DataTransfer** aNewDataTransfer)
{
DataTransfer* newDataTransfer =
new DataTransfer(aParent, aEventMessage, mEffectAllowed, mCursorState,
mIsExternal, aUserCancelled, aIsCrossDomainSubFrameDrop,
mClipboardType, mItems, mDragImage, mDragImageX,
mDragImageY);
*aNewDataTransfer = newDataTransfer;
NS_ADDREF(*aNewDataTransfer);
return NS_OK;
}
already_AddRefed<nsISupportsArray>
DataTransfer::GetTransferables(nsIDOMNode* aDragTarget)
{
MOZ_ASSERT(aDragTarget);
nsCOMPtr<nsINode> dragNode = do_QueryInterface(aDragTarget);
if (!dragNode) {
return nullptr;
}
nsIDocument* doc = dragNode->GetCurrentDoc();
if (!doc) {
return nullptr;
}
return GetTransferables(doc->GetLoadContext());
}
already_AddRefed<nsISupportsArray>
DataTransfer::GetTransferables(nsILoadContext* aLoadContext)
{
nsCOMPtr<nsISupportsArray> transArray =
do_CreateInstance("@mozilla.org/supports-array;1");
if (!transArray) {
return nullptr;
}
uint32_t count = mItems.Length();
for (uint32_t i = 0; i < count; i++) {
nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext);
if (transferable) {
transArray->AppendElement(transferable);
}
}
return transArray.forget();
}
already_AddRefed<nsITransferable>
DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
{
if (aIndex >= mItems.Length()) {
return nullptr;
}
nsTArray<TransferItem>& item = mItems[aIndex];
uint32_t count = item.Length();
if (!count) {
return nullptr;
}
nsCOMPtr<nsITransferable> transferable =
do_CreateInstance("@mozilla.org/widget/transferable;1");
if (!transferable) {
return nullptr;
}
transferable->Init(aLoadContext);
bool added = false;
for (uint32_t f = 0; f < count; f++) {
const TransferItem& formatitem = item[f];
if (!formatitem.mData) { // skip empty items
continue;
}
uint32_t length;
nsCOMPtr<nsISupports> convertedData;
if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData), &length)) {
continue;
}
// the underlying drag code uses text/unicode, so use that instead of text/plain
const char* format;
NS_ConvertUTF16toUTF8 utf8format(formatitem.mFormat);
if (utf8format.EqualsLiteral("text/plain")) {
format = kUnicodeMime;
} else {
format = utf8format.get();
}
// if a converter is set for a format, set the converter for the
// transferable and don't add the item
nsCOMPtr<nsIFormatConverter> converter = do_QueryInterface(convertedData);
if (converter) {
transferable->AddDataFlavor(format);
transferable->SetConverter(converter);
continue;
}
nsresult rv = transferable->SetTransferData(format, convertedData, length);
if (NS_FAILED(rv)) {
return nullptr;
}
added = true;
}
// only return the transferable if data was successfully added to it
if (added) {
return transferable.forget();
}
return nullptr;
}
bool
DataTransfer::ConvertFromVariant(nsIVariant* aVariant,
nsISupports** aSupports,
uint32_t* aLength)
{
*aSupports = nullptr;
*aLength = 0;
uint16_t type;
aVariant->GetDataType(&type);
if (type == nsIDataType::VTYPE_INTERFACE ||
type == nsIDataType::VTYPE_INTERFACE_IS) {
nsCOMPtr<nsISupports> data;
if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data))))
return false;
nsCOMPtr<nsIFlavorDataProvider> fdp = do_QueryInterface(data);
if (fdp) {
// for flavour data providers, use kFlavorHasDataProvider (which has the
// value 0) as the length.
fdp.forget(aSupports);
*aLength = nsITransferable::kFlavorHasDataProvider;
}
else {
// wrap the item in an nsISupportsInterfacePointer
nsCOMPtr<nsISupportsInterfacePointer> ptrSupports =
do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID);
if (!ptrSupports)
return false;
ptrSupports->SetData(data);
ptrSupports.forget(aSupports);
*aLength = sizeof(nsISupportsInterfacePointer *);
}
return true;
}
char16_t* chrs;
uint32_t len = 0;
nsresult rv = aVariant->GetAsWStringWithSize(&len, &chrs);
if (NS_FAILED(rv))
return false;
nsAutoString str;
str.Adopt(chrs, len);
nsCOMPtr<nsISupportsString>
strSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID));
if (!strSupports)
return false;
strSupports->SetData(str);
strSupports.forget(aSupports);
// each character is two bytes
*aLength = str.Length() << 1;
return true;
}
void
DataTransfer::ClearAll()
{
mItems.Clear();
}
nsresult
DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
nsIVariant* aData,
uint32_t aIndex,
nsIPrincipal* aPrincipal)
{
nsAutoString format;
GetRealFormat(aFormat, format);
// check if the item for the format already exists. In that case,
// just replace it.
TransferItem* formatitem;
if (aIndex < mItems.Length()) {
nsTArray<TransferItem>& item = mItems[aIndex];
uint32_t count = item.Length();
for (uint32_t i = 0; i < count; i++) {
TransferItem& itemformat = item[i];
if (itemformat.mFormat.Equals(format)) {
// don't allow replacing data that has a stronger principal
bool subsumes;
if (itemformat.mPrincipal && aPrincipal &&
(NS_FAILED(aPrincipal->Subsumes(itemformat.mPrincipal, &subsumes)) || !subsumes))
return NS_ERROR_DOM_SECURITY_ERR;
itemformat.mPrincipal = aPrincipal;
itemformat.mData = aData;
return NS_OK;
}
}
// add a new format
formatitem = item.AppendElement();
}
else {
NS_ASSERTION(aIndex == mItems.Length(), "Index out of range");
// add a new index
nsTArray<TransferItem>* item = mItems.AppendElement();
NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY);
formatitem = item->AppendElement();
}
NS_ENSURE_TRUE(formatitem, NS_ERROR_OUT_OF_MEMORY);
formatitem->mFormat = format;
formatitem->mPrincipal = aPrincipal;
formatitem->mData = aData;
return NS_OK;
}
void
DataTransfer::GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat)
{
// treat text/unicode as equivalent to text/plain
nsAutoString lowercaseFormat;
nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat);
if (lowercaseFormat.EqualsLiteral("text") || lowercaseFormat.EqualsLiteral("text/unicode"))
aOutFormat.AssignLiteral("text/plain");
else if (lowercaseFormat.EqualsLiteral("url"))
aOutFormat.AssignLiteral("text/uri-list");
else
aOutFormat.Assign(lowercaseFormat);
}
void
DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex, nsIPrincipal* aPrincipal)
{
if (strcmp(aFormat, kUnicodeMime) == 0) {
SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nullptr, aIndex, aPrincipal);
} else {
if (strcmp(aFormat, kURLDataMime) == 0) {
SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nullptr, aIndex, aPrincipal);
}
SetDataWithPrincipal(NS_ConvertUTF8toUTF16(aFormat), nullptr, aIndex, aPrincipal);
}
}
void
DataTransfer::CacheExternalDragFormats()
{
// Called during the constructor to cache the formats available from an
// external drag. The data associated with each format will be set to null.
// This data will instead only be retrieved in FillInExternalDragData when
// asked for, as it may be time consuming for the source application to
// generate it.
nsCOMPtr<nsIDragSession> dragSession = nsContentUtils::GetDragSession();
if (!dragSession)
return;
// make sure that the system principal is used for external drags
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsCOMPtr<nsIPrincipal> sysPrincipal;
ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
// 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* formats[] = { kFileMime, kHTMLMime, kRTFMime, kURLMime, kURLDataMime, kUnicodeMime };
uint32_t count;
dragSession->GetNumDropItems(&count);
for (uint32_t c = 0; c < count; c++) {
for (uint32_t f = 0; f < ArrayLength(formats); f++) {
// IsDataFlavorSupported doesn't take an index as an argument and just
// checks if any of the items support a particular flavor, even though
// the GetData method does take an index. Here, we just assume that
// every item being dragged has the same set of flavors.
bool supported;
dragSession->IsDataFlavorSupported(formats[f], &supported);
// if the format is supported, add an item to the array with null as
// the data. When retrieved, GetRealData will read the data.
if (supported) {
CacheExternalData(formats[f], c, sysPrincipal);
}
}
}
}
void
DataTransfer::CacheExternalClipboardFormats()
{
NS_ASSERTION(mEventMessage == ePaste,
"caching clipboard data for invalid event");
// Called during the constructor for paste events to cache the formats
// available on the clipboard. As with CacheExternalDragFormats, the
// data will only be retrieved when needed.
nsCOMPtr<nsIClipboard> clipboard = do_GetService("@mozilla.org/widget/clipboard;1");
if (!clipboard || mClipboardType < 0) {
return;
}
nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager();
nsCOMPtr<nsIPrincipal> sysPrincipal;
ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal));
// 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
const char* formats[] = { kFileMime, kHTMLMime, kRTFMime, kURLMime, kURLDataMime, kUnicodeMime };
for (uint32_t f = 0; f < mozilla::ArrayLength(formats); ++f) {
// check each format one at a time
bool supported;
clipboard->HasDataMatchingFlavors(&(formats[f]), 1, mClipboardType, &supported);
// if the format is supported, add an item to the array with null as
// the data. When retrieved, GetRealData will read the data.
if (supported) {
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 < 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);
}
}
}
}
}
} // namespace dom
} // namespace mozilla