void DataTransfer::FillAllExternalData() { if (mIsExternal) { for (uint32_t i = 0; i < MozItemCount(); ++i) { const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(i); for (uint32_t j = 0; j < items.Length(); ++j) { MOZ_ASSERT(items[j]->Index() == i); items[j]->FillInExternalData(); } } } }
nsresult DataTransfer::GetDataAtInternal(const nsAString& aFormat, uint32_t aIndex, nsIPrincipal* aSubjectPrincipal, nsIVariant** aData) { *aData = nullptr; if (aFormat.IsEmpty()) { return NS_OK; } if (aIndex >= MozItemCount()) { 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); MOZ_ASSERT(aSubjectPrincipal); RefPtr<DataTransferItem> item = mItems->MozItemByTypeAt(format, aIndex); if (!item) { // The index exists but there's no data for the specified format, in this // case we just return undefined return NS_OK; } // If we have chrome only content, and we aren't chrome, don't allow access if (!nsContentUtils::IsSystemPrincipal(aSubjectPrincipal) && item->ChromeOnly()) { return NS_OK; } // DataTransferItem::Data() handles the principal checks ErrorResult result; nsCOMPtr<nsIVariant> data = item->Data(aSubjectPrincipal, result); if (NS_WARN_IF(!data || result.Failed())) { return result.StealNSResult(); } data.forget(aData); return NS_OK; }
void DataTransfer::MozClearDataAtHelper(const nsAString& aFormat, uint32_t aIndex, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) { MOZ_ASSERT(!mReadOnly); MOZ_ASSERT(aIndex < MozItemCount()); MOZ_ASSERT(aIndex == 0 || (mEventMessage != eCut && mEventMessage != eCopy && mEventMessage != ePaste)); nsAutoString format; GetRealFormat(aFormat, format); mItems->MozRemoveByTypeAt(format, aIndex, aSubjectPrincipal, aRv); }
already_AddRefed<DOMStringList> DataTransfer::MozTypesAt(uint32_t aIndex, CallerType aCallerType, 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 < MozItemCount()) { // note that you can retrieve the types regardless of their principal const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(aIndex); bool addFile = false; for (uint32_t i = 0; i < items.Length(); i++) { if (items[i]->ChromeOnly() && aCallerType != CallerType::System) { continue; } // NOTE: The reason why we get the internal type here is because we want // kFileMime to appear in the types list for backwards compatibility // reasons. nsAutoString type; items[i]->GetInternalType(type); if (NS_WARN_IF(!types->Add(type))) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } if (items[i]->Kind() == DataTransferItem::KIND_FILE) { addFile = true; } } if (addFile) { types->Add(NS_LITERAL_STRING("Files")); } } return types.forget(); }
already_AddRefed<nsIArray> DataTransfer::GetTransferables(nsILoadContext* aLoadContext) { nsCOMPtr<nsIMutableArray> transArray = nsArray::Create(); if (!transArray) { return nullptr; } uint32_t count = MozItemCount(); for (uint32_t i = 0; i < count; i++) { nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext); if (transferable) { transArray->AppendElement(transferable, /*weak =*/ false); } } return transArray.forget(); }
void DataTransfer::ClearData(const Optional<nsAString>& aFormat, nsIPrincipal& aSubjectPrincipal, ErrorResult& aRv) { if (mReadOnly) { aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); return; } if (MozItemCount() == 0) { return; } if (aFormat.WasPassed()) { MozClearDataAtHelper(aFormat.Value(), 0, aSubjectPrincipal, aRv); } else { MozClearDataAtHelper(EmptyString(), 0, aSubjectPrincipal, aRv); } }
already_AddRefed<nsISupportsArray> DataTransfer::GetTransferables(nsILoadContext* aLoadContext) { nsCOMPtr<nsISupportsArray> transArray = do_CreateInstance("@mozilla.org/supports-array;1"); if (!transArray) { return nullptr; } uint32_t count = MozItemCount(); for (uint32_t i = 0; i < count; i++) { nsCOMPtr<nsITransferable> transferable = GetTransferable(i, aLoadContext); if (transferable) { transArray->AppendElement(transferable); } } return transArray.forget(); }
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 < MozItemCount()) { // note that you can retrieve the types regardless of their principal const nsTArray<RefPtr<DataTransferItem>>& items = *mItems->MozItemsAt(aIndex); bool addFile = false; for (uint32_t i = 0; i < items.Length(); i++) { if (items[i]->ChromeOnly() && !nsContentUtils::LegacyIsCallerChromeOrNativeCode()) { continue; } nsAutoString type; items[i]->GetType(type); if (NS_WARN_IF(!types->Add(type))) { aRv.Throw(NS_ERROR_FAILURE); return nullptr; } if (items[i]->Kind() == DataTransferItem::KIND_FILE) { addFile = true; } } if (addFile) { types->Add(NS_LITERAL_STRING("Files")); } } return types.forget(); }
void DataTransfer::MozClearDataAt(const nsAString& aFormat, uint32_t aIndex, const Maybe<nsIPrincipal*>& aSubjectPrincipal, ErrorResult& aRv) { MOZ_ASSERT(aSubjectPrincipal.isSome()); if (mReadOnly) { aRv.Throw(NS_ERROR_DOM_NO_MODIFICATION_ALLOWED_ERR); return; } if (aIndex >= MozItemCount()) { 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, aSubjectPrincipal, aRv); // If we just cleared the 0-th index, and there are still more than 1 indexes // remaining, MozClearDataAt should cause the 1st index to become the 0th // index. This should _only_ happen when the MozClearDataAt function is // explicitly called by script, as this behavior is inconsistent with spec. // (however, so is the MozClearDataAt API) if (aIndex == 0 && mItems->MozItemCount() > 1 && mItems->MozItemsAt(0)->Length() == 0) { mItems->PopIndexZero(); } }
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 > MozItemCount()) { 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 the custom type to be assigned. if (aFormat.EqualsLiteral(kCustomTypesMime)) { return NS_ERROR_TYPE_ERR; } if (!PrincipalMaySetData(aFormat, aData, aSubjectPrincipal)) { return NS_ERROR_DOM_SECURITY_ERR; } return SetDataWithPrincipal(aFormat, aData, aIndex, aSubjectPrincipal); }
NS_IMETHODIMP DataTransfer::GetMozItemCount(uint32_t* aCount) { *aCount = MozItemCount(); return NS_OK; }
already_AddRefed<nsITransferable> DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext) { if (aIndex >= MozItemCount()) { return nullptr; } const nsTArray<RefPtr<DataTransferItem>>& item = *mItems->MozItemsAt(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); nsCOMPtr<nsIStorageStream> storageStream; nsCOMPtr<nsIBinaryOutputStream> stream; bool added = false; bool handlingCustomFormats = true; uint32_t totalCustomLength = 0; const char* knownFormats[] = { kTextMime, kHTMLMime, kNativeHTMLMime, kRTFMime, kURLMime, kURLDataMime, kURLDescriptionMime, kURLPrivateMime, kPNGImageMime, kJPEGImageMime, kGIFImageMime, kNativeImageMime, kFileMime, kFilePromiseMime, kFilePromiseURLMime, kFilePromiseDestFilename, kFilePromiseDirectoryMime, kMozTextInternal, kHTMLContext, kHTMLInfo }; /* * Two passes are made here to iterate over all of the types. First, look for * any types that are not in the list of known types. For this pass, * handlingCustomFormats will be true. Data that corresponds to unknown types * will be pulled out and inserted into a single type (kCustomTypesMime) by * writing the data into a stream. * * The second pass will iterate over the formats looking for known types. * These are added as is. The unknown types are all then inserted as a single * type (kCustomTypesMime) in the same position of the first custom type. This * model is used to maintain the format order as best as possible. * * The format of the kCustomTypesMime type is one or more of the following * stored sequentially: * <32-bit> type (only none or string is supported) * <32-bit> length of format * <wide string> format * <32-bit> length of data * <wide string> data * A type of eCustomClipboardTypeId_None ends the list, without any following * data. */ do { for (uint32_t f = 0; f < count; f++) { RefPtr<DataTransferItem> formatitem = item[f]; nsCOMPtr<nsIVariant> variant = formatitem->DataNoSecurityCheck(); if (!variant) { // skip empty items continue; } nsAutoString type; formatitem->GetType(type); // If the data is of one of the well-known formats, use it directly. bool isCustomFormat = true; for (uint32_t f = 0; f < ArrayLength(knownFormats); f++) { if (type.EqualsASCII(knownFormats[f])) { isCustomFormat = false; break; } } uint32_t lengthInBytes; nsCOMPtr<nsISupports> convertedData; if (handlingCustomFormats) { if (!ConvertFromVariant(variant, getter_AddRefs(convertedData), &lengthInBytes)) { continue; } // When handling custom types, add the data to the stream if this is a // custom type. if (isCustomFormat) { // If it isn't a string, just ignore it. The dataTransfer is cached in // the drag sesion during drag-and-drop, so non-strings will be // available when dragging locally. nsCOMPtr<nsISupportsString> str(do_QueryInterface(convertedData)); if (str) { nsAutoString data; str->GetData(data); if (!stream) { // Create a storage stream to write to. NS_NewStorageStream(1024, UINT32_MAX, getter_AddRefs(storageStream)); nsCOMPtr<nsIOutputStream> outputStream; storageStream->GetOutputStream(0, getter_AddRefs(outputStream)); stream = do_CreateInstance("@mozilla.org/binaryoutputstream;1"); stream->SetOutputStream(outputStream); } int32_t formatLength = type.Length() * sizeof(nsString::char_type); stream->Write32(eCustomClipboardTypeId_String); stream->Write32(formatLength); stream->WriteBytes((const char *)type.get(), formatLength); stream->Write32(lengthInBytes); stream->WriteBytes((const char *)data.get(), lengthInBytes); // The total size of the stream is the format length, the data // length, two integers to hold the lengths and one integer for the // string flag. totalCustomLength += formatLength + lengthInBytes + (sizeof(uint32_t) * 3); } } } else if (isCustomFormat && stream) { // This is the second pass of the loop (handlingCustomFormats is false). // When encountering the first custom format, append all of the stream // at this position. // Write out a terminator. totalCustomLength += sizeof(uint32_t); stream->Write32(eCustomClipboardTypeId_None); nsCOMPtr<nsIInputStream> inputStream; storageStream->NewInputStream(0, getter_AddRefs(inputStream)); RefPtr<nsStringBuffer> stringBuffer = nsStringBuffer::Alloc(totalCustomLength + 1); // Read the data from the string and add a null-terminator as ToString // needs it. uint32_t amountRead; inputStream->Read(static_cast<char*>(stringBuffer->Data()), totalCustomLength, &amountRead); static_cast<char*>(stringBuffer->Data())[amountRead] = 0; nsCString str; stringBuffer->ToString(totalCustomLength, str); nsCOMPtr<nsISupportsCString> strSupports(do_CreateInstance(NS_SUPPORTS_CSTRING_CONTRACTID)); strSupports->SetData(str); nsresult rv = transferable->SetTransferData(kCustomTypesMime, strSupports, totalCustomLength); if (NS_FAILED(rv)) { return nullptr; } added = true; // Clear the stream so it doesn't get used again. stream = nullptr; } else { // This is the second pass of the loop and a known type is encountered. // Add it as is. if (!ConvertFromVariant(variant, getter_AddRefs(convertedData), &lengthInBytes)) { continue; } // The underlying drag code uses text/unicode, so use that instead of // text/plain const char* format; NS_ConvertUTF16toUTF8 utf8format(type); if (utf8format.EqualsLiteral(kTextMime)) { 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, lengthInBytes); if (NS_FAILED(rv)) { return nullptr; } added = true; } } handlingCustomFormats = !handlingCustomFormats; } while (!handlingCustomFormats); // only return the transferable if data was successfully added to it if (added) { return transferable.forget(); } return nullptr; }