michael@0: /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ michael@0: /* vim: set ts=8 sts=2 et sw=2 tw=80: */ michael@0: /* This Source Code Form is subject to the terms of the Mozilla Public michael@0: * License, v. 2.0. If a copy of the MPL was not distributed with this michael@0: * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ michael@0: michael@0: #include "mozilla/ArrayUtils.h" michael@0: #include "mozilla/BasicEvents.h" michael@0: michael@0: #include "DataTransfer.h" michael@0: michael@0: #include "nsIDOMDocument.h" michael@0: #include "nsIVariant.h" michael@0: #include "nsISupportsPrimitives.h" michael@0: #include "nsIScriptSecurityManager.h" michael@0: #include "mozilla/dom/DOMStringList.h" michael@0: #include "nsError.h" michael@0: #include "nsIDragService.h" michael@0: #include "nsIClipboard.h" michael@0: #include "nsContentUtils.h" michael@0: #include "nsIContent.h" michael@0: #include "nsCRT.h" michael@0: #include "nsIScriptObjectPrincipal.h" michael@0: #include "nsIScriptContext.h" michael@0: #include "nsIDocument.h" michael@0: #include "nsIScriptGlobalObject.h" michael@0: #include "mozilla/dom/DataTransferBinding.h" michael@0: #include "mozilla/dom/Element.h" michael@0: #include "mozilla/dom/BindingUtils.h" michael@0: michael@0: namespace mozilla { michael@0: namespace dom { michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_CLASS(DataTransfer) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(DataTransfer) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mParent) michael@0: if (tmp->mFiles) { michael@0: tmp->mFiles->Disconnect(); michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mFiles) michael@0: } michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragTarget) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK(mDragImage) michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_PRESERVED_WRAPPER michael@0: NS_IMPL_CYCLE_COLLECTION_UNLINK_END michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(DataTransfer) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mParent) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mFiles) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragTarget) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mDragImage) michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS michael@0: NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END michael@0: NS_IMPL_CYCLE_COLLECTION_TRACE_WRAPPERCACHE(DataTransfer) michael@0: michael@0: NS_IMPL_CYCLE_COLLECTING_ADDREF(DataTransfer) michael@0: NS_IMPL_CYCLE_COLLECTING_RELEASE(DataTransfer) michael@0: michael@0: NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(DataTransfer) michael@0: NS_WRAPPERCACHE_INTERFACE_MAP_ENTRY michael@0: NS_INTERFACE_MAP_ENTRY(mozilla::dom::DataTransfer) michael@0: NS_INTERFACE_MAP_ENTRY(nsIDOMDataTransfer) michael@0: NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIDOMDataTransfer) michael@0: NS_INTERFACE_MAP_END michael@0: michael@0: // the size of the array michael@0: const char DataTransfer::sEffects[8][9] = { michael@0: "none", "copy", "move", "copyMove", "link", "copyLink", "linkMove", "all" michael@0: }; michael@0: michael@0: DataTransfer::DataTransfer(nsISupports* aParent, uint32_t aEventType, michael@0: bool aIsExternal, int32_t aClipboardType) michael@0: : mParent(aParent), michael@0: mEventType(aEventType), michael@0: mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE), michael@0: mEffectAllowed(nsIDragService::DRAGDROP_ACTION_UNINITIALIZED), michael@0: mCursorState(false), michael@0: mReadOnly(true), michael@0: mIsExternal(aIsExternal), michael@0: mUserCancelled(false), michael@0: mIsCrossDomainSubFrameDrop(false), michael@0: mClipboardType(aClipboardType), michael@0: mDragImageX(0), michael@0: mDragImageY(0) michael@0: { michael@0: MOZ_ASSERT(mParent); michael@0: SetIsDOMBinding(); michael@0: // For these events, we want to be able to add data to the data transfer, so michael@0: // clear the readonly state. Otherwise, the data is already present. For michael@0: // external usage, cache the data from the native clipboard or drag. michael@0: if (aEventType == NS_CUT || michael@0: aEventType == NS_COPY || michael@0: aEventType == NS_DRAGDROP_START || michael@0: aEventType == NS_DRAGDROP_GESTURE) { michael@0: mReadOnly = false; michael@0: } else if (mIsExternal) { michael@0: if (aEventType == NS_PASTE) { michael@0: CacheExternalClipboardFormats(); michael@0: } else if (aEventType >= NS_DRAGDROP_EVENT_START && aEventType <= NS_DRAGDROP_LEAVE_SYNTH) { michael@0: CacheExternalDragFormats(); michael@0: } michael@0: } michael@0: } michael@0: michael@0: DataTransfer::DataTransfer(nsISupports* aParent, michael@0: uint32_t aEventType, michael@0: const uint32_t aEffectAllowed, michael@0: bool aCursorState, michael@0: bool aIsExternal, michael@0: bool aUserCancelled, michael@0: bool aIsCrossDomainSubFrameDrop, michael@0: int32_t aClipboardType, michael@0: nsTArray >& aItems, michael@0: Element* aDragImage, michael@0: uint32_t aDragImageX, michael@0: uint32_t aDragImageY) michael@0: : mParent(aParent), michael@0: mEventType(aEventType), michael@0: mDropEffect(nsIDragService::DRAGDROP_ACTION_NONE), michael@0: mEffectAllowed(aEffectAllowed), michael@0: mCursorState(aCursorState), michael@0: mReadOnly(true), michael@0: mIsExternal(aIsExternal), michael@0: mUserCancelled(aUserCancelled), michael@0: mIsCrossDomainSubFrameDrop(aIsCrossDomainSubFrameDrop), michael@0: mClipboardType(aClipboardType), michael@0: mItems(aItems), michael@0: mDragImage(aDragImage), michael@0: mDragImageX(aDragImageX), michael@0: mDragImageY(aDragImageY) michael@0: { michael@0: MOZ_ASSERT(mParent); michael@0: SetIsDOMBinding(); michael@0: // The items are copied from aItems into mItems. There is no need to copy michael@0: // the actual data in the items as the data transfer will be read only. The michael@0: // draggesture and dragstart events are the only times when items are michael@0: // modifiable, but those events should have been using the first constructor michael@0: // above. michael@0: NS_ASSERTION(aEventType != NS_DRAGDROP_GESTURE && michael@0: aEventType != NS_DRAGDROP_START, michael@0: "invalid event type for DataTransfer constructor"); michael@0: } michael@0: michael@0: DataTransfer::~DataTransfer() michael@0: { michael@0: if (mFiles) { michael@0: mFiles->Disconnect(); michael@0: } michael@0: } michael@0: michael@0: // static michael@0: already_AddRefed michael@0: DataTransfer::Constructor(const GlobalObject& aGlobal, michael@0: const nsAString& aEventType, bool aIsExternal, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsAutoCString onEventType("on"); michael@0: AppendUTF16toUTF8(aEventType, onEventType); michael@0: nsCOMPtr eventTypeAtom = do_GetAtom(onEventType); michael@0: if (!eventTypeAtom) { michael@0: aRv.Throw(NS_ERROR_OUT_OF_MEMORY); michael@0: return nullptr; michael@0: } michael@0: michael@0: uint32_t eventType = nsContentUtils::GetEventId(eventTypeAtom); michael@0: nsRefPtr transfer = new DataTransfer(aGlobal.GetAsSupports(), michael@0: eventType, aIsExternal, michael@0: -1); michael@0: return transfer.forget(); michael@0: } michael@0: michael@0: JSObject* michael@0: DataTransfer::WrapObject(JSContext* aCx) michael@0: { michael@0: return DataTransferBinding::Wrap(aCx, this); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetDropEffect(nsAString& aDropEffect) michael@0: { michael@0: nsString dropEffect; michael@0: GetDropEffect(dropEffect); michael@0: aDropEffect = dropEffect; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::SetDropEffect(const nsAString& aDropEffect) michael@0: { michael@0: // the drop effect can only be 'none', 'copy', 'move' or 'link'. michael@0: for (uint32_t e = 0; e <= nsIDragService::DRAGDROP_ACTION_LINK; e++) { michael@0: if (aDropEffect.EqualsASCII(sEffects[e])) { michael@0: // don't allow copyMove michael@0: if (e != (nsIDragService::DRAGDROP_ACTION_COPY | michael@0: nsIDragService::DRAGDROP_ACTION_MOVE)) michael@0: mDropEffect = e; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetEffectAllowed(nsAString& aEffectAllowed) michael@0: { michael@0: nsString effectAllowed; michael@0: GetEffectAllowed(effectAllowed); michael@0: aEffectAllowed = effectAllowed; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::SetEffectAllowed(const nsAString& aEffectAllowed) michael@0: { michael@0: if (aEffectAllowed.EqualsLiteral("uninitialized")) { michael@0: mEffectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED; michael@0: return NS_OK; michael@0: } michael@0: michael@0: static_assert(nsIDragService::DRAGDROP_ACTION_NONE == 0, michael@0: "DRAGDROP_ACTION_NONE constant is wrong"); michael@0: static_assert(nsIDragService::DRAGDROP_ACTION_COPY == 1, michael@0: "DRAGDROP_ACTION_COPY constant is wrong"); michael@0: static_assert(nsIDragService::DRAGDROP_ACTION_MOVE == 2, michael@0: "DRAGDROP_ACTION_MOVE constant is wrong"); michael@0: static_assert(nsIDragService::DRAGDROP_ACTION_LINK == 4, michael@0: "DRAGDROP_ACTION_LINK constant is wrong"); michael@0: michael@0: for (uint32_t e = 0; e < ArrayLength(sEffects); e++) { michael@0: if (aEffectAllowed.EqualsASCII(sEffects[e])) { michael@0: mEffectAllowed = e; michael@0: break; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetDropEffectInt(uint32_t* aDropEffect) michael@0: { michael@0: *aDropEffect = mDropEffect; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::SetDropEffectInt(uint32_t aDropEffect) michael@0: { michael@0: mDropEffect = aDropEffect; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetEffectAllowedInt(uint32_t* aEffectAllowed) michael@0: { michael@0: *aEffectAllowed = mEffectAllowed; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::SetEffectAllowedInt(uint32_t aEffectAllowed) michael@0: { michael@0: mEffectAllowed = aEffectAllowed; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetMozUserCancelled(bool* aUserCancelled) michael@0: { michael@0: *aUserCancelled = MozUserCancelled(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsDOMFileList* michael@0: DataTransfer::GetFiles(ErrorResult& aRv) michael@0: { michael@0: if (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP && michael@0: mEventType != NS_PASTE) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!mFiles) { michael@0: mFiles = new nsDOMFileList(static_cast(this)); michael@0: michael@0: uint32_t count = mItems.Length(); michael@0: michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: nsCOMPtr variant; michael@0: aRv = MozGetDataAt(NS_ConvertUTF8toUTF16(kFileMime), i, getter_AddRefs(variant)); michael@0: if (aRv.Failed()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: if (!variant) michael@0: continue; michael@0: michael@0: nsCOMPtr supports; michael@0: nsresult rv = variant->GetAsISupports(getter_AddRefs(supports)); michael@0: michael@0: if (NS_FAILED(rv)) michael@0: continue; michael@0: michael@0: nsCOMPtr file = do_QueryInterface(supports); michael@0: michael@0: if (!file) michael@0: continue; michael@0: michael@0: nsRefPtr domFile = new nsDOMFileFile(file); michael@0: michael@0: if (!mFiles->Append(domFile)) { michael@0: aRv.Throw(NS_ERROR_FAILURE); michael@0: return nullptr; michael@0: } michael@0: } michael@0: } michael@0: michael@0: return mFiles; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetFiles(nsIDOMFileList** aFileList) michael@0: { michael@0: ErrorResult rv; michael@0: NS_IF_ADDREF(*aFileList = GetFiles(rv)); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: DataTransfer::Types() michael@0: { michael@0: nsRefPtr types = new DOMStringList(); michael@0: if (mItems.Length()) { michael@0: bool addFile = false; michael@0: const nsTArray& item = mItems[0]; michael@0: for (uint32_t i = 0; i < item.Length(); i++) { michael@0: const nsString& format = item[i].mFormat; michael@0: types->Add(format); michael@0: if (!addFile) { michael@0: addFile = format.EqualsASCII(kFileMime) || michael@0: format.EqualsASCII("application/x-moz-file-promise"); michael@0: } michael@0: } michael@0: michael@0: if (addFile) { michael@0: types->Add(NS_LITERAL_STRING("Files")); michael@0: } michael@0: } michael@0: michael@0: return types.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetTypes(nsISupports** aTypes) michael@0: { michael@0: nsRefPtr types = Types(); michael@0: types.forget(aTypes); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: DataTransfer::GetData(const nsAString& aFormat, nsAString& aData, michael@0: ErrorResult& aRv) michael@0: { michael@0: // return an empty string if data for the format was not found michael@0: aData.Truncate(); michael@0: michael@0: nsCOMPtr data; michael@0: nsresult rv = MozGetDataAt(aFormat, 0, getter_AddRefs(data)); michael@0: if (NS_FAILED(rv)) { michael@0: if (rv != NS_ERROR_DOM_INDEX_SIZE_ERR) { michael@0: aRv.Throw(rv); michael@0: } michael@0: return; michael@0: } michael@0: michael@0: if (data) { michael@0: nsAutoString stringdata; michael@0: data->GetAsAString(stringdata); michael@0: michael@0: // for the URL type, parse out the first URI from the list. The URIs are michael@0: // separated by newlines michael@0: nsAutoString lowercaseFormat; michael@0: aRv = nsContentUtils::ASCIIToLower(aFormat, lowercaseFormat); michael@0: if (aRv.Failed()) { michael@0: return; michael@0: } michael@0: michael@0: if (lowercaseFormat.EqualsLiteral("url")) { michael@0: int32_t lastidx = 0, idx; michael@0: int32_t length = stringdata.Length(); michael@0: while (lastidx < length) { michael@0: idx = stringdata.FindChar('\n', lastidx); michael@0: // lines beginning with # are comments michael@0: if (stringdata[lastidx] == '#') { michael@0: if (idx == -1) michael@0: break; michael@0: } michael@0: else { michael@0: if (idx == -1) michael@0: aData.Assign(Substring(stringdata, lastidx)); michael@0: else michael@0: aData.Assign(Substring(stringdata, lastidx, idx - lastidx)); michael@0: aData = nsContentUtils::TrimWhitespace(aData, true); michael@0: return; michael@0: } michael@0: lastidx = idx + 1; michael@0: } michael@0: } michael@0: else { michael@0: aData = stringdata; michael@0: } michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetData(const nsAString& aFormat, nsAString& aData) michael@0: { michael@0: ErrorResult rv; michael@0: GetData(aFormat, aData, rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: void michael@0: DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData, michael@0: ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr variant = do_CreateInstance(NS_VARIANT_CONTRACTID); michael@0: if (!variant) { michael@0: aRv.Throw(NS_ERROR_OUT_OF_MEMORY); michael@0: return; michael@0: } michael@0: michael@0: variant->SetAsAString(aData); michael@0: michael@0: aRv = MozSetDataAt(aFormat, variant, 0); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::SetData(const nsAString& aFormat, const nsAString& aData) michael@0: { michael@0: ErrorResult rv; michael@0: SetData(aFormat, aData, rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: void michael@0: DataTransfer::ClearData(const Optional& aFormat, ErrorResult& aRv) michael@0: { michael@0: if (mReadOnly) { michael@0: aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); michael@0: return; michael@0: } michael@0: michael@0: if (mItems.Length() == 0) { michael@0: return; michael@0: } michael@0: michael@0: if (aFormat.WasPassed()) { michael@0: MozClearDataAtHelper(aFormat.Value(), 0, aRv); michael@0: } else { michael@0: MozClearDataAtHelper(EmptyString(), 0, aRv); michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::ClearData(const nsAString& aFormat) michael@0: { michael@0: Optional format; michael@0: format = &aFormat; michael@0: ErrorResult rv; michael@0: ClearData(format, rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetMozItemCount(uint32_t* aCount) michael@0: { michael@0: *aCount = MozItemCount(); michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetMozCursor(nsAString& aCursorState) michael@0: { michael@0: nsString cursor; michael@0: GetMozCursor(cursor); michael@0: aCursorState = cursor; michael@0: return NS_OK; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::SetMozCursor(const nsAString& aCursorState) michael@0: { michael@0: // Lock the cursor to an arrow during the drag. michael@0: mCursorState = aCursorState.EqualsLiteral("default"); michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: DataTransfer::GetMozSourceNode() michael@0: { michael@0: nsCOMPtr dragSession = nsContentUtils::GetDragSession(); michael@0: if (!dragSession) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr sourceNode; michael@0: dragSession->GetSourceNode(getter_AddRefs(sourceNode)); michael@0: nsCOMPtr node = do_QueryInterface(sourceNode); michael@0: if (node && !nsContentUtils::CanCallerAccess(node)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: return node.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::GetMozSourceNode(nsIDOMNode** aSourceNode) michael@0: { michael@0: nsCOMPtr sourceNode = GetMozSourceNode(); michael@0: if (!sourceNode) { michael@0: *aSourceNode = nullptr; michael@0: return NS_OK; michael@0: } michael@0: michael@0: return CallQueryInterface(sourceNode, aSourceNode); michael@0: } michael@0: michael@0: already_AddRefed michael@0: DataTransfer::MozTypesAt(uint32_t aIndex, ErrorResult& aRv) michael@0: { michael@0: // Only the first item is valid for clipboard events michael@0: if (aIndex > 0 && michael@0: (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); michael@0: return nullptr; michael@0: } michael@0: michael@0: nsRefPtr types = new DOMStringList(); michael@0: if (aIndex < mItems.Length()) { michael@0: // note that you can retrieve the types regardless of their principal michael@0: nsTArray& item = mItems[aIndex]; michael@0: for (uint32_t i = 0; i < item.Length(); i++) michael@0: types->Add(item[i].mFormat); michael@0: } michael@0: michael@0: return types.forget(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::MozTypesAt(uint32_t aIndex, nsISupports** aTypes) michael@0: { michael@0: ErrorResult rv; michael@0: nsRefPtr types = MozTypesAt(aIndex, rv); michael@0: types.forget(aTypes); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::MozGetDataAt(const nsAString& aFormat, uint32_t aIndex, michael@0: nsIVariant** aData) michael@0: { michael@0: *aData = nullptr; michael@0: michael@0: if (aFormat.IsEmpty()) michael@0: return NS_OK; michael@0: michael@0: if (aIndex >= mItems.Length()) { michael@0: return NS_ERROR_DOM_INDEX_SIZE_ERR; michael@0: } michael@0: michael@0: // Only the first item is valid for clipboard events michael@0: if (aIndex > 0 && michael@0: (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) { michael@0: return NS_ERROR_DOM_INDEX_SIZE_ERR; michael@0: } michael@0: michael@0: michael@0: nsAutoString format; michael@0: GetRealFormat(aFormat, format); michael@0: michael@0: nsTArray& item = mItems[aIndex]; michael@0: michael@0: // Check if the caller is allowed to access the drag data. Callers with michael@0: // chrome privileges can always read the data. During the michael@0: // drop event, allow retrieving the data except in the case where the michael@0: // source of the drag is in a child frame of the caller. In that case, michael@0: // we only allow access to data of the same principal. During other events, michael@0: // only allow access to the data with the same principal. michael@0: nsIPrincipal* principal = nullptr; michael@0: if (mIsCrossDomainSubFrameDrop || michael@0: (mEventType != NS_DRAGDROP_DROP && mEventType != NS_DRAGDROP_DRAGDROP && michael@0: mEventType != NS_PASTE && michael@0: !nsContentUtils::IsCallerChrome())) { michael@0: nsresult rv = NS_OK; michael@0: principal = GetCurrentPrincipal(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: } michael@0: michael@0: uint32_t count = item.Length(); michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: TransferItem& formatitem = item[i]; michael@0: if (formatitem.mFormat.Equals(format)) { michael@0: bool subsumes; michael@0: if (formatitem.mPrincipal && principal && michael@0: (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes)) michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: michael@0: if (!formatitem.mData) { michael@0: FillInExternalData(formatitem, aIndex); michael@0: } else { michael@0: nsCOMPtr data; michael@0: formatitem.mData->GetAsISupports(getter_AddRefs(data)); michael@0: // Make sure the code that is calling us is same-origin with the data. michael@0: nsCOMPtr pt = do_QueryInterface(data); michael@0: if (pt) { michael@0: nsresult rv = NS_OK; michael@0: nsIScriptContext* c = pt->GetContextForEventHandlers(&rv); michael@0: NS_ENSURE_TRUE(c && NS_SUCCEEDED(rv), NS_ERROR_DOM_SECURITY_ERR); michael@0: nsIGlobalObject* go = c->GetGlobalObject(); michael@0: NS_ENSURE_TRUE(go, NS_ERROR_DOM_SECURITY_ERR); michael@0: nsCOMPtr sp = do_QueryInterface(go); michael@0: MOZ_ASSERT(sp, "This cannot fail on the main thread."); michael@0: nsIPrincipal* dataPrincipal = sp->GetPrincipal(); michael@0: NS_ENSURE_TRUE(dataPrincipal, NS_ERROR_DOM_SECURITY_ERR); michael@0: NS_ENSURE_TRUE(principal || (principal = GetCurrentPrincipal(&rv)), michael@0: NS_ERROR_DOM_SECURITY_ERR); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: bool equals = false; michael@0: NS_ENSURE_TRUE(NS_SUCCEEDED(principal->Equals(dataPrincipal, &equals)) && equals, michael@0: NS_ERROR_DOM_SECURITY_ERR); michael@0: } michael@0: } michael@0: *aData = formatitem.mData; michael@0: NS_IF_ADDREF(*aData); michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: void michael@0: DataTransfer::MozGetDataAt(JSContext* aCx, const nsAString& aFormat, michael@0: uint32_t aIndex, michael@0: JS::MutableHandle aRetval, michael@0: mozilla::ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr data; michael@0: aRv = MozGetDataAt(aFormat, aIndex, getter_AddRefs(data)); michael@0: if (aRv.Failed()) { michael@0: return; michael@0: } michael@0: michael@0: if (!data) { michael@0: return; michael@0: } michael@0: michael@0: JS::Rooted result(aCx); michael@0: if (!VariantToJsval(aCx, data, aRetval)) { michael@0: aRv = NS_ERROR_FAILURE; michael@0: return; michael@0: } michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::MozSetDataAt(const nsAString& aFormat, nsIVariant* aData, michael@0: uint32_t aIndex) michael@0: { michael@0: if (aFormat.IsEmpty()) { michael@0: return NS_OK; michael@0: } michael@0: michael@0: if (mReadOnly) { michael@0: return NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR; michael@0: } michael@0: michael@0: // Specifying an index less than the current length will replace an existing michael@0: // item. Specifying an index equal to the current length will add a new item. michael@0: if (aIndex > mItems.Length()) { michael@0: return NS_ERROR_DOM_INDEX_SIZE_ERR; michael@0: } michael@0: michael@0: // Only the first item is valid for clipboard events michael@0: if (aIndex > 0 && michael@0: (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) { michael@0: return NS_ERROR_DOM_INDEX_SIZE_ERR; michael@0: } michael@0: michael@0: // don't allow non-chrome to add file data michael@0: // XXX perhaps this should also limit any non-string type as well michael@0: if ((aFormat.EqualsLiteral("application/x-moz-file-promise") || michael@0: aFormat.EqualsLiteral("application/x-moz-file")) && michael@0: !nsContentUtils::IsCallerChrome()) { michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: } michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsIPrincipal* principal = GetCurrentPrincipal(&rv); michael@0: NS_ENSURE_SUCCESS(rv, rv); michael@0: michael@0: return SetDataWithPrincipal(aFormat, aData, aIndex, principal); michael@0: } michael@0: michael@0: void michael@0: DataTransfer::MozSetDataAt(JSContext* aCx, const nsAString& aFormat, michael@0: JS::Handle aData, michael@0: uint32_t aIndex, ErrorResult& aRv) michael@0: { michael@0: nsCOMPtr data; michael@0: aRv = nsContentUtils::XPConnect()->JSValToVariant(aCx, aData, michael@0: getter_AddRefs(data)); michael@0: if (!aRv.Failed()) { michael@0: aRv = MozSetDataAt(aFormat, data, aIndex); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (mReadOnly) { michael@0: aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); michael@0: return; michael@0: } michael@0: michael@0: if (aIndex >= mItems.Length()) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); michael@0: return; michael@0: } michael@0: michael@0: // Only the first item is valid for clipboard events michael@0: if (aIndex > 0 && michael@0: (mEventType == NS_CUT || mEventType == NS_COPY || mEventType == NS_PASTE)) { michael@0: aRv.Throw(NS_ERROR_DOM_INDEX_SIZE_ERR); michael@0: return; michael@0: } michael@0: michael@0: MozClearDataAtHelper(aFormat, aIndex, aRv); michael@0: } michael@0: michael@0: void michael@0: DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex, michael@0: ErrorResult& aRv) michael@0: { michael@0: MOZ_ASSERT(!mReadOnly); michael@0: MOZ_ASSERT(aIndex < mItems.Length()); michael@0: MOZ_ASSERT(aIndex == 0 || michael@0: (mEventType != NS_CUT && mEventType != NS_COPY && michael@0: mEventType != NS_PASTE)); michael@0: michael@0: nsAutoString format; michael@0: GetRealFormat(aFormat, format); michael@0: michael@0: nsresult rv = NS_OK; michael@0: nsIPrincipal* principal = GetCurrentPrincipal(&rv); michael@0: if (NS_FAILED(rv)) { michael@0: aRv = rv; michael@0: return; michael@0: } michael@0: michael@0: // if the format is empty, clear all formats michael@0: bool clearall = format.IsEmpty(); michael@0: michael@0: nsTArray& item = mItems[aIndex]; michael@0: // count backwards so that the count and index don't have to be adjusted michael@0: // after removing an element michael@0: for (int32_t i = item.Length() - 1; i >= 0; i--) { michael@0: TransferItem& formatitem = item[i]; michael@0: if (clearall || formatitem.mFormat.Equals(format)) { michael@0: // don't allow removing data that has a stronger principal michael@0: bool subsumes; michael@0: if (formatitem.mPrincipal && principal && michael@0: (NS_FAILED(principal->Subsumes(formatitem.mPrincipal, &subsumes)) || !subsumes)) { michael@0: aRv.Throw(NS_ERROR_DOM_SECURITY_ERR); michael@0: return; michael@0: } michael@0: michael@0: item.RemoveElementAt(i); michael@0: michael@0: // if a format was specified, break out. Otherwise, loop around until michael@0: // all formats have been removed michael@0: if (!clearall) michael@0: break; michael@0: } michael@0: } michael@0: michael@0: // if the last format for an item is removed, remove the entire item michael@0: if (!item.Length()) michael@0: mItems.RemoveElementAt(aIndex); michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex) michael@0: { michael@0: ErrorResult rv; michael@0: MozClearDataAt(aFormat, aIndex, rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: void michael@0: DataTransfer::SetDragImage(Element& aImage, int32_t aX, int32_t aY, michael@0: ErrorResult& aRv) michael@0: { michael@0: if (mReadOnly) { michael@0: aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); michael@0: return; michael@0: } michael@0: michael@0: mDragImage = &aImage; michael@0: mDragImageX = aX; michael@0: mDragImageY = aY; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::SetDragImage(nsIDOMElement* aImage, int32_t aX, int32_t aY) michael@0: { michael@0: ErrorResult rv; michael@0: nsCOMPtr image = do_QueryInterface(aImage); michael@0: if (image) { michael@0: SetDragImage(*image, aX, aY, rv); michael@0: } michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: void michael@0: DataTransfer::AddElement(Element& aElement, ErrorResult& aRv) michael@0: { michael@0: if (mReadOnly) { michael@0: aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); michael@0: return; michael@0: } michael@0: michael@0: mDragTarget = &aElement; michael@0: } michael@0: michael@0: NS_IMETHODIMP michael@0: DataTransfer::AddElement(nsIDOMElement* aElement) michael@0: { michael@0: NS_ENSURE_TRUE(aElement, NS_ERROR_NULL_POINTER); michael@0: michael@0: nsCOMPtr element = do_QueryInterface(aElement); michael@0: NS_ENSURE_TRUE(element, NS_ERROR_INVALID_ARG); michael@0: michael@0: ErrorResult rv; michael@0: AddElement(*element, rv); michael@0: return rv.ErrorCode(); michael@0: } michael@0: michael@0: nsresult michael@0: DataTransfer::Clone(nsISupports* aParent, uint32_t aEventType, michael@0: bool aUserCancelled, bool aIsCrossDomainSubFrameDrop, michael@0: DataTransfer** aNewDataTransfer) michael@0: { michael@0: DataTransfer* newDataTransfer = michael@0: new DataTransfer(aParent, aEventType, mEffectAllowed, mCursorState, michael@0: mIsExternal, aUserCancelled, aIsCrossDomainSubFrameDrop, michael@0: mClipboardType, mItems, mDragImage, mDragImageX, michael@0: mDragImageY); michael@0: michael@0: *aNewDataTransfer = newDataTransfer; michael@0: NS_ADDREF(*aNewDataTransfer); michael@0: return NS_OK; michael@0: } michael@0: michael@0: already_AddRefed michael@0: DataTransfer::GetTransferables(nsIDOMNode* aDragTarget) michael@0: { michael@0: MOZ_ASSERT(aDragTarget); michael@0: michael@0: nsCOMPtr transArray = michael@0: do_CreateInstance("@mozilla.org/supports-array;1"); michael@0: if (!transArray) { michael@0: return nullptr; michael@0: } michael@0: michael@0: michael@0: nsCOMPtr dragNode = do_QueryInterface(aDragTarget); michael@0: if (!dragNode) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsIDocument* doc = dragNode->GetCurrentDoc(); michael@0: if (!doc) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsILoadContext* loadContext = doc->GetLoadContext(); michael@0: michael@0: uint32_t count = mItems.Length(); michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: nsCOMPtr transferable = GetTransferable(i, loadContext); michael@0: if (transferable) { michael@0: transArray->AppendElement(transferable); michael@0: } michael@0: } michael@0: michael@0: return transArray.forget(); michael@0: } michael@0: michael@0: already_AddRefed michael@0: DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext) michael@0: { michael@0: if (aIndex >= mItems.Length()) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsTArray& item = mItems[aIndex]; michael@0: uint32_t count = item.Length(); michael@0: if (!count) { michael@0: return nullptr; michael@0: } michael@0: michael@0: nsCOMPtr transferable = michael@0: do_CreateInstance("@mozilla.org/widget/transferable;1"); michael@0: if (!transferable) { michael@0: return nullptr; michael@0: } michael@0: transferable->Init(aLoadContext); michael@0: michael@0: bool added = false; michael@0: for (uint32_t f = 0; f < count; f++) { michael@0: const TransferItem& formatitem = item[f]; michael@0: if (!formatitem.mData) { // skip empty items michael@0: continue; michael@0: } michael@0: michael@0: uint32_t length; michael@0: nsCOMPtr convertedData; michael@0: if (!ConvertFromVariant(formatitem.mData, getter_AddRefs(convertedData), &length)) { michael@0: continue; michael@0: } michael@0: michael@0: // the underlying drag code uses text/unicode, so use that instead of text/plain michael@0: const char* format; michael@0: NS_ConvertUTF16toUTF8 utf8format(formatitem.mFormat); michael@0: if (utf8format.EqualsLiteral("text/plain")) { michael@0: format = kUnicodeMime; michael@0: } else { michael@0: format = utf8format.get(); michael@0: } michael@0: michael@0: // if a converter is set for a format, set the converter for the michael@0: // transferable and don't add the item michael@0: nsCOMPtr converter = do_QueryInterface(convertedData); michael@0: if (converter) { michael@0: transferable->AddDataFlavor(format); michael@0: transferable->SetConverter(converter); michael@0: continue; michael@0: } michael@0: michael@0: nsresult rv = transferable->SetTransferData(format, convertedData, length); michael@0: if (NS_FAILED(rv)) { michael@0: return nullptr; michael@0: } michael@0: michael@0: added = true; michael@0: } michael@0: michael@0: // only return the transferable if data was successfully added to it michael@0: if (added) { michael@0: return transferable.forget(); michael@0: } michael@0: michael@0: return nullptr; michael@0: } michael@0: michael@0: bool michael@0: DataTransfer::ConvertFromVariant(nsIVariant* aVariant, michael@0: nsISupports** aSupports, michael@0: uint32_t* aLength) michael@0: { michael@0: *aSupports = nullptr; michael@0: *aLength = 0; michael@0: michael@0: uint16_t type; michael@0: aVariant->GetDataType(&type); michael@0: if (type == nsIDataType::VTYPE_INTERFACE || michael@0: type == nsIDataType::VTYPE_INTERFACE_IS) { michael@0: nsCOMPtr data; michael@0: if (NS_FAILED(aVariant->GetAsISupports(getter_AddRefs(data)))) michael@0: return false; michael@0: michael@0: nsCOMPtr fdp = do_QueryInterface(data); michael@0: if (fdp) { michael@0: // for flavour data providers, use kFlavorHasDataProvider (which has the michael@0: // value 0) as the length. michael@0: NS_ADDREF(*aSupports = fdp); michael@0: *aLength = nsITransferable::kFlavorHasDataProvider; michael@0: } michael@0: else { michael@0: // wrap the item in an nsISupportsInterfacePointer michael@0: nsCOMPtr ptrSupports = michael@0: do_CreateInstance(NS_SUPPORTS_INTERFACE_POINTER_CONTRACTID); michael@0: if (!ptrSupports) michael@0: return false; michael@0: michael@0: ptrSupports->SetData(data); michael@0: NS_ADDREF(*aSupports = ptrSupports); michael@0: michael@0: *aLength = sizeof(nsISupportsInterfacePointer *); michael@0: } michael@0: michael@0: return true; michael@0: } michael@0: michael@0: char16_t* chrs; michael@0: uint32_t len = 0; michael@0: nsresult rv = aVariant->GetAsWStringWithSize(&len, &chrs); michael@0: if (NS_FAILED(rv)) michael@0: return false; michael@0: michael@0: nsAutoString str; michael@0: str.Adopt(chrs, len); michael@0: michael@0: nsCOMPtr michael@0: strSupports(do_CreateInstance(NS_SUPPORTS_STRING_CONTRACTID)); michael@0: if (!strSupports) michael@0: return false; michael@0: michael@0: strSupports->SetData(str); michael@0: michael@0: *aSupports = strSupports; michael@0: NS_ADDREF(*aSupports); michael@0: michael@0: // each character is two bytes michael@0: *aLength = str.Length() << 1; michael@0: michael@0: return true; michael@0: } michael@0: michael@0: void michael@0: DataTransfer::ClearAll() michael@0: { michael@0: mItems.Clear(); michael@0: } michael@0: michael@0: nsresult michael@0: DataTransfer::SetDataWithPrincipal(const nsAString& aFormat, michael@0: nsIVariant* aData, michael@0: uint32_t aIndex, michael@0: nsIPrincipal* aPrincipal) michael@0: { michael@0: nsAutoString format; michael@0: GetRealFormat(aFormat, format); michael@0: michael@0: // check if the item for the format already exists. In that case, michael@0: // just replace it. michael@0: TransferItem* formatitem; michael@0: if (aIndex < mItems.Length()) { michael@0: nsTArray& item = mItems[aIndex]; michael@0: uint32_t count = item.Length(); michael@0: for (uint32_t i = 0; i < count; i++) { michael@0: TransferItem& itemformat = item[i]; michael@0: if (itemformat.mFormat.Equals(format)) { michael@0: // don't allow replacing data that has a stronger principal michael@0: bool subsumes; michael@0: if (itemformat.mPrincipal && aPrincipal && michael@0: (NS_FAILED(aPrincipal->Subsumes(itemformat.mPrincipal, &subsumes)) || !subsumes)) michael@0: return NS_ERROR_DOM_SECURITY_ERR; michael@0: michael@0: itemformat.mPrincipal = aPrincipal; michael@0: itemformat.mData = aData; michael@0: return NS_OK; michael@0: } michael@0: } michael@0: michael@0: // add a new format michael@0: formatitem = item.AppendElement(); michael@0: } michael@0: else { michael@0: NS_ASSERTION(aIndex == mItems.Length(), "Index out of range"); michael@0: michael@0: // add a new index michael@0: nsTArray* item = mItems.AppendElement(); michael@0: NS_ENSURE_TRUE(item, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: formatitem = item->AppendElement(); michael@0: } michael@0: michael@0: NS_ENSURE_TRUE(formatitem, NS_ERROR_OUT_OF_MEMORY); michael@0: michael@0: formatitem->mFormat = format; michael@0: formatitem->mPrincipal = aPrincipal; michael@0: formatitem->mData = aData; michael@0: michael@0: return NS_OK; michael@0: } michael@0: michael@0: nsIPrincipal* michael@0: DataTransfer::GetCurrentPrincipal(nsresult* rv) michael@0: { michael@0: nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); michael@0: michael@0: nsCOMPtr currentPrincipal; michael@0: *rv = ssm->GetSubjectPrincipal(getter_AddRefs(currentPrincipal)); michael@0: NS_ENSURE_SUCCESS(*rv, nullptr); michael@0: michael@0: if (!currentPrincipal) michael@0: ssm->GetSystemPrincipal(getter_AddRefs(currentPrincipal)); michael@0: michael@0: return currentPrincipal.get(); michael@0: } michael@0: michael@0: void michael@0: DataTransfer::GetRealFormat(const nsAString& aInFormat, nsAString& aOutFormat) michael@0: { michael@0: // treat text/unicode as equivalent to text/plain michael@0: nsAutoString lowercaseFormat; michael@0: nsContentUtils::ASCIIToLower(aInFormat, lowercaseFormat); michael@0: if (lowercaseFormat.EqualsLiteral("text") || lowercaseFormat.EqualsLiteral("text/unicode")) michael@0: aOutFormat.AssignLiteral("text/plain"); michael@0: else if (lowercaseFormat.EqualsLiteral("url")) michael@0: aOutFormat.AssignLiteral("text/uri-list"); michael@0: else michael@0: aOutFormat.Assign(lowercaseFormat); michael@0: } michael@0: michael@0: void michael@0: DataTransfer::CacheExternalData(const char* aFormat, uint32_t aIndex, nsIPrincipal* aPrincipal) michael@0: { michael@0: if (strcmp(aFormat, kUnicodeMime) == 0) { michael@0: SetDataWithPrincipal(NS_LITERAL_STRING("text/plain"), nullptr, aIndex, aPrincipal); michael@0: } else { michael@0: if (strcmp(aFormat, kURLDataMime) == 0) { michael@0: SetDataWithPrincipal(NS_LITERAL_STRING("text/uri-list"), nullptr, aIndex, aPrincipal); michael@0: } michael@0: SetDataWithPrincipal(NS_ConvertUTF8toUTF16(aFormat), nullptr, aIndex, aPrincipal); michael@0: } michael@0: } michael@0: michael@0: void michael@0: DataTransfer::CacheExternalDragFormats() michael@0: { michael@0: // Called during the constructor to cache the formats available from an michael@0: // external drag. The data associated with each format will be set to null. michael@0: // This data will instead only be retrieved in FillInExternalDragData when michael@0: // asked for, as it may be time consuming for the source application to michael@0: // generate it. michael@0: michael@0: nsCOMPtr dragSession = nsContentUtils::GetDragSession(); michael@0: if (!dragSession) michael@0: return; michael@0: michael@0: // make sure that the system principal is used for external drags michael@0: nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); michael@0: nsCOMPtr sysPrincipal; michael@0: ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal)); michael@0: michael@0: // there isn't a way to get a list of the formats that might be available on michael@0: // all platforms, so just check for the types that can actually be imported michael@0: // XXXndeakin there are some other formats but those are platform specific. michael@0: const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime, kUnicodeMime }; michael@0: michael@0: uint32_t count; michael@0: dragSession->GetNumDropItems(&count); michael@0: for (uint32_t c = 0; c < count; c++) { michael@0: for (uint32_t f = 0; f < ArrayLength(formats); f++) { michael@0: // IsDataFlavorSupported doesn't take an index as an argument and just michael@0: // checks if any of the items support a particular flavor, even though michael@0: // the GetData method does take an index. Here, we just assume that michael@0: // every item being dragged has the same set of flavors. michael@0: bool supported; michael@0: dragSession->IsDataFlavorSupported(formats[f], &supported); michael@0: // if the format is supported, add an item to the array with null as michael@0: // the data. When retrieved, GetRealData will read the data. michael@0: if (supported) { michael@0: CacheExternalData(formats[f], c, sysPrincipal); michael@0: } michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: DataTransfer::CacheExternalClipboardFormats() michael@0: { michael@0: NS_ASSERTION(mEventType == NS_PASTE, "caching clipboard data for invalid event"); michael@0: michael@0: // Called during the constructor for paste events to cache the formats michael@0: // available on the clipboard. As with CacheExternalDragFormats, the michael@0: // data will only be retrieved when needed. michael@0: michael@0: nsCOMPtr clipboard = do_GetService("@mozilla.org/widget/clipboard;1"); michael@0: if (!clipboard || mClipboardType < 0) { michael@0: return; michael@0: } michael@0: michael@0: nsIScriptSecurityManager* ssm = nsContentUtils::GetSecurityManager(); michael@0: nsCOMPtr sysPrincipal; michael@0: ssm->GetSystemPrincipal(getter_AddRefs(sysPrincipal)); michael@0: michael@0: // there isn't a way to get a list of the formats that might be available on michael@0: // all platforms, so just check for the types that can actually be imported michael@0: const char* formats[] = { kFileMime, kHTMLMime, kURLMime, kURLDataMime, kUnicodeMime }; michael@0: michael@0: for (uint32_t f = 0; f < mozilla::ArrayLength(formats); ++f) { michael@0: // check each format one at a time michael@0: bool supported; michael@0: clipboard->HasDataMatchingFlavors(&(formats[f]), 1, mClipboardType, &supported); michael@0: // if the format is supported, add an item to the array with null as michael@0: // the data. When retrieved, GetRealData will read the data. michael@0: if (supported) { michael@0: CacheExternalData(formats[f], 0, sysPrincipal); michael@0: } michael@0: } michael@0: } michael@0: michael@0: void michael@0: DataTransfer::FillInExternalData(TransferItem& aItem, uint32_t aIndex) michael@0: { michael@0: NS_PRECONDITION(mIsExternal, "Not an external data transfer"); michael@0: michael@0: if (aItem.mData) { michael@0: return; michael@0: } michael@0: michael@0: // only drag and paste events should be calling FillInExternalData michael@0: NS_ASSERTION(mEventType != NS_CUT && mEventType != NS_COPY, michael@0: "clipboard event with empty data"); michael@0: michael@0: NS_ConvertUTF16toUTF8 utf8format(aItem.mFormat); michael@0: const char* format = utf8format.get(); michael@0: if (strcmp(format, "text/plain") == 0) michael@0: format = kUnicodeMime; michael@0: else if (strcmp(format, "text/uri-list") == 0) michael@0: format = kURLDataMime; michael@0: michael@0: nsCOMPtr trans = michael@0: do_CreateInstance("@mozilla.org/widget/transferable;1"); michael@0: if (!trans) michael@0: return; michael@0: michael@0: trans->Init(nullptr); michael@0: trans->AddDataFlavor(format); michael@0: michael@0: if (mEventType == NS_PASTE) { michael@0: MOZ_ASSERT(aIndex == 0, "index in clipboard must be 0"); michael@0: michael@0: nsCOMPtr clipboard = do_GetService("@mozilla.org/widget/clipboard;1"); michael@0: if (!clipboard || mClipboardType < 0) { michael@0: return; michael@0: } michael@0: michael@0: clipboard->GetData(trans, mClipboardType); michael@0: } else { michael@0: nsCOMPtr dragSession = nsContentUtils::GetDragSession(); michael@0: if (!dragSession) { michael@0: return; michael@0: } michael@0: michael@0: #ifdef DEBUG michael@0: // Since this is an external drag, the source document will always be null. michael@0: nsCOMPtr domDoc; michael@0: dragSession->GetSourceDocument(getter_AddRefs(domDoc)); michael@0: MOZ_ASSERT(!domDoc); michael@0: #endif michael@0: michael@0: dragSession->GetData(trans, aIndex); michael@0: } michael@0: michael@0: uint32_t length = 0; michael@0: nsCOMPtr data; michael@0: trans->GetTransferData(format, getter_AddRefs(data), &length); michael@0: if (!data) michael@0: return; michael@0: michael@0: nsCOMPtr variant = do_CreateInstance(NS_VARIANT_CONTRACTID); michael@0: if (!variant) michael@0: return; michael@0: michael@0: nsCOMPtr supportsstr = do_QueryInterface(data); michael@0: if (supportsstr) { michael@0: nsAutoString str; michael@0: supportsstr->GetData(str); michael@0: variant->SetAsAString(str); michael@0: } michael@0: else { michael@0: variant->SetAsISupports(data); michael@0: } michael@0: michael@0: aItem.mData = variant; michael@0: } michael@0: michael@0: } // namespace dom michael@0: } // namespace mozilla