template<> nsresult BodyExtractor<nsIDocument>::GetAsStream(nsIInputStream** aResult, uint64_t* aContentLength, nsACString& aContentTypeWithCharset, nsACString& aCharset) const { nsCOMPtr<nsIDOMDocument> domdoc(do_QueryInterface(mBody)); NS_ENSURE_STATE(domdoc); aCharset.AssignLiteral("UTF-8"); nsresult rv; nsCOMPtr<nsIStorageStream> storStream; rv = NS_NewStorageStream(4096, UINT32_MAX, getter_AddRefs(storStream)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIOutputStream> output; rv = storStream->GetOutputStream(0, getter_AddRefs(output)); NS_ENSURE_SUCCESS(rv, rv); if (mBody->IsHTMLDocument()) { aContentTypeWithCharset.AssignLiteral("text/html;charset=UTF-8"); nsString serialized; if (!nsContentUtils::SerializeNodeToMarkup(mBody, true, serialized)) { return NS_ERROR_OUT_OF_MEMORY; } nsAutoCString utf8Serialized; if (!AppendUTF16toUTF8(serialized, utf8Serialized, fallible)) { return NS_ERROR_OUT_OF_MEMORY; } uint32_t written; rv = output->Write(utf8Serialized.get(), utf8Serialized.Length(), &written); NS_ENSURE_SUCCESS(rv, rv); MOZ_ASSERT(written == utf8Serialized.Length()); } else { aContentTypeWithCharset.AssignLiteral("application/xml;charset=UTF-8"); nsCOMPtr<nsIDOMSerializer> serializer = do_CreateInstance(NS_XMLSERIALIZER_CONTRACTID, &rv); NS_ENSURE_SUCCESS(rv, rv); // Make sure to use the encoding we'll send rv = serializer->SerializeToStream(domdoc, output, aCharset); NS_ENSURE_SUCCESS(rv, rv); } output->Close(); uint32_t length; rv = storStream->GetLength(&length); NS_ENSURE_SUCCESS(rv, rv); *aContentLength = length; rv = storStream->NewInputStream(0, aResult); NS_ENSURE_SUCCESS(rv, rv); return NS_OK; }
nsresult nsMemoryCacheDevice::OpenOutputStreamForEntry( nsCacheEntry * entry, nsCacheAccessMode mode, uint32_t offset, nsIOutputStream ** result) { NS_ENSURE_ARG_POINTER(entry); NS_ENSURE_ARG_POINTER(result); nsCOMPtr<nsIStorageStream> storage; nsresult rv; nsISupports *data = entry->Data(); if (data) { storage = do_QueryInterface(data, &rv); if (NS_FAILED(rv)) return rv; } else { rv = NS_NewStorageStream(4096, uint32_t(-1), getter_AddRefs(storage)); if (NS_FAILED(rv)) return rv; entry->SetData(storage); } return storage->GetOutputStream(offset, result); }
nsresult nsAboutCacheEntry::GetContentStream(nsIURI *uri, nsIInputStream **result) { nsCOMPtr<nsIStorageStream> storageStream; nsCOMPtr<nsIOutputStream> outputStream; PRUint32 n; nsCString buffer; nsresult rv; nsCOMPtr<nsICacheEntryDescriptor> descriptor; OpenCacheEntry(uri, getter_AddRefs(descriptor)); // Init: (block size, maximum length) rv = NS_NewStorageStream(256, PRUint32(-1), getter_AddRefs(storageStream)); if (NS_FAILED(rv)) return rv; rv = storageStream->GetOutputStream(0, getter_AddRefs(outputStream)); if (NS_FAILED(rv)) return rv; buffer.AssignLiteral( "<!DOCTYPE html>\n" "<html>\n" "<head>\n" " <title>Cache entry information</title>\n" " <link rel=\"stylesheet\" " "href=\"chrome://global/skin/about.css\" type=\"text/css\"/>\n" " <link rel=\"stylesheet\" " "href=\"chrome://global/skin/aboutCacheEntry.css\" type=\"text/css\"/>\n" "</head>\n" "<body>\n" "<h1>Cache entry information</h1>\n"); outputStream->Write(buffer.get(), buffer.Length(), &n); if (descriptor) rv = WriteCacheEntryDescription(outputStream, descriptor); else rv = WriteCacheEntryUnavailable(outputStream); if (NS_FAILED(rv)) return rv; buffer.AssignLiteral("</body>\n</html>\n"); outputStream->Write(buffer.get(), buffer.Length(), &n); nsCOMPtr<nsIInputStream> inStr; PRUint32 size; rv = storageStream->GetLength(&size); if (NS_FAILED(rv)) return rv; return storageStream->NewInputStream(0, result); }
TEST(StorageStreams, EarlyInputStream) { // generate some test data we will write in 4k chunks to the stream nsTArray<char> kData; testing::CreateData(4096, kData); // track how much data was written so we can compare at the end nsAutoCString dataWritten; nsresult rv; nsCOMPtr<nsIStorageStream> stor; rv = NS_NewStorageStream(kData.Length(), UINT32_MAX, getter_AddRefs(stor)); EXPECT_TRUE(NS_SUCCEEDED(rv)); // Get input stream before writing data into the output stream nsCOMPtr<nsIInputStream> in; rv = stor->NewInputStream(0, getter_AddRefs(in)); EXPECT_TRUE(NS_SUCCEEDED(rv)); // Write data to output stream nsCOMPtr<nsIOutputStream> out; rv = stor->GetOutputStream(0, getter_AddRefs(out)); EXPECT_TRUE(NS_SUCCEEDED(rv)); WriteData(out, kData, kData.Length(), dataWritten); WriteData(out, kData, kData.Length(), dataWritten); rv = out->Close(); EXPECT_TRUE(NS_SUCCEEDED(rv)); out = nullptr; // Should be able to consume input stream testing::ConsumeAndValidateStream(in, dataWritten); in = nullptr; }
NS_EXPORT nsresult NewObjectOutputWrappedStorageStream(nsIObjectOutputStream **wrapperStream, nsIStorageStream** stream, bool wantDebugStream) { nsCOMPtr<nsIStorageStream> storageStream; nsresult rv = NS_NewStorageStream(256, PR_UINT32_MAX, getter_AddRefs(storageStream)); NS_ENSURE_SUCCESS(rv, rv); nsCOMPtr<nsIObjectOutputStream> objectOutput = do_CreateInstance("@mozilla.org/binaryoutputstream;1"); nsCOMPtr<nsIOutputStream> outputStream = do_QueryInterface(storageStream); objectOutput->SetOutputStream(outputStream); #ifdef DEBUG if (wantDebugStream) { // Wrap in debug stream to detect unsupported writes of // multiply-referenced non-singleton objects StartupCache* sc = StartupCache::GetSingleton(); NS_ENSURE_TRUE(sc, NS_ERROR_UNEXPECTED); nsCOMPtr<nsIObjectOutputStream> debugStream; sc->GetDebugObjectOutputStream(objectOutput, getter_AddRefs(debugStream)); debugStream.forget(wrapperStream); } else { objectOutput.forget(wrapperStream); } #else objectOutput.forget(wrapperStream); #endif storageStream.forget(stream); return NS_OK; }
NS_IMETHODIMP nsAboutCache::NewChannel(nsIURI *aURI, nsIChannel **result) { NS_ENSURE_ARG_POINTER(aURI); nsresult rv; PRUint32 bytesWritten; *result = nsnull; /* nsXPIDLCString path; rv = aURI->GetPath(getter_Copies(path)); if (NS_FAILED(rv)) return rv; PRBool clear = PR_FALSE; PRBool leaks = PR_FALSE; nsCAutoString p(path); PRInt32 pos = p.Find("?"); if (pos > 0) { nsCAutoString param; (void)p.Right(param, p.Length() - (pos+1)); if (param.Equals("new")) statType = nsTraceRefcnt::NEW_STATS; else if (param.Equals("clear")) clear = PR_TRUE; else if (param.Equals("leaks")) leaks = PR_TRUE; } */ // Get the cache manager service nsCOMPtr<nsICacheService> cacheService = do_GetService(NS_CACHESERVICE_CONTRACTID, &rv); if (NS_FAILED(rv)) return rv; nsCOMPtr<nsIStorageStream> storageStream; nsCOMPtr<nsIOutputStream> outputStream; // Init: (block size, maximum length) rv = NS_NewStorageStream(256, (PRUint32)-1, getter_AddRefs(storageStream)); if (NS_FAILED(rv)) return rv; rv = storageStream->GetOutputStream(0, getter_AddRefs(outputStream)); if (NS_FAILED(rv)) return rv; mBuffer.AssignLiteral( "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.1//EN\"\n" " \"http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd\">\n" "<html xmlns=\"http://www.w3.org/1999/xhtml\">\n" "<head>\n<title>Information about the Cache Service</title>\n</head>\n" "<body>\n<div>\n"); outputStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); rv = ParseURI(aURI, mDeviceID); if (NS_FAILED(rv)) return rv; mStream = outputStream; rv = cacheService->VisitEntries(this); if (NS_FAILED(rv)) return rv; if (!mDeviceID.IsEmpty()) { mBuffer.AssignLiteral("</pre>\n"); } else { mBuffer.Truncate(); } mBuffer.AppendLiteral("</div>\n</body>\n</html>\n"); outputStream->Write(mBuffer.get(), mBuffer.Length(), &bytesWritten); nsCOMPtr<nsIInputStream> inStr; rv = storageStream->NewInputStream(0, getter_AddRefs(inStr)); if (NS_FAILED(rv)) return rv; nsIChannel* channel; rv = NS_NewInputStreamChannel(&channel, aURI, inStr, NS_LITERAL_CSTRING("text/html"), NS_LITERAL_CSTRING("utf-8")); if (NS_FAILED(rv)) return rv; *result = channel; return rv; }
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); 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, 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++) { const TransferItem& formatitem = item[f]; if (!formatitem.mData) { // skip empty items continue; } // 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 (formatitem.mFormat.EqualsASCII(knownFormats[f])) { isCustomFormat = false; break; } } uint32_t lengthInBytes; nsCOMPtr<nsISupports> convertedData; if (handlingCustomFormats) { if (!ConvertFromVariant(formatitem.mData, 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 = formatitem.mFormat.Length() * sizeof(nsString::char_type); stream->Write32(eCustomClipboardTypeId_String); stream->Write32(formatLength); stream->WriteBytes((const char *)formatitem.mFormat.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(formatitem.mData, 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(formatitem.mFormat); 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; }
TEST(StorageStreams, Main) { // generate some test data we will write in 4k chunks to the stream nsTArray<char> kData; testing::CreateData(4096, kData); // track how much data was written so we can compare at the end nsAutoCString dataWritten; nsresult rv; nsCOMPtr<nsIStorageStream> stor; rv = NS_NewStorageStream(kData.Length(), UINT32_MAX, getter_AddRefs(stor)); EXPECT_TRUE(NS_SUCCEEDED(rv)); nsCOMPtr<nsIOutputStream> out; rv = stor->GetOutputStream(0, getter_AddRefs(out)); EXPECT_TRUE(NS_SUCCEEDED(rv)); WriteData(out, kData, kData.Length(), dataWritten); WriteData(out, kData, kData.Length(), dataWritten); rv = out->Close(); EXPECT_TRUE(NS_SUCCEEDED(rv)); out = nullptr; nsCOMPtr<nsIInputStream> in; rv = stor->NewInputStream(0, getter_AddRefs(in)); EXPECT_TRUE(NS_SUCCEEDED(rv)); nsCOMPtr<nsICloneableInputStream> cloneable = do_QueryInterface(in); ASSERT_TRUE(cloneable != nullptr); ASSERT_TRUE(cloneable->GetCloneable()); nsCOMPtr<nsIInputStream> clone; rv = cloneable->Clone(getter_AddRefs(clone)); testing::ConsumeAndValidateStream(in, dataWritten); testing::ConsumeAndValidateStream(clone, dataWritten); in = nullptr; clone = nullptr; // now, write 3 more full 4k segments + 11 bytes, starting at 8192 // total written equals 20491 bytes rv = stor->GetOutputStream(dataWritten.Length(), getter_AddRefs(out)); EXPECT_TRUE(NS_SUCCEEDED(rv)); WriteData(out, kData, kData.Length(), dataWritten); WriteData(out, kData, kData.Length(), dataWritten); WriteData(out, kData, kData.Length(), dataWritten); WriteData(out, kData, 11, dataWritten); rv = out->Close(); EXPECT_TRUE(NS_SUCCEEDED(rv)); out = nullptr; // now, read all rv = stor->NewInputStream(0, getter_AddRefs(in)); EXPECT_TRUE(NS_SUCCEEDED(rv)); testing::ConsumeAndValidateStream(in, dataWritten); in = nullptr; }
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; // When writing the custom data, we need to ensure that there is sufficient // space for a (uint32_t) data ending type, and the null byte character at // the end of the nsCString. We claim that space upfront and store it in // baseLength. This value will be set to zero if a write error occurs // indicating that the data and length are no longer valid. const uint32_t baseLength = sizeof(uint32_t) + 1; uint32_t totalCustomLength = baseLength; 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->GetInternalType(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 totalCustomLength is 0, then a write error occurred // on a previous item, so ignore any others. if (isCustomFormat && totalCustomLength > 0) { // 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); } CheckedInt<uint32_t> formatLength = CheckedInt<uint32_t>(type.Length()) * sizeof(nsString::char_type); // 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. Guard against large data by ignoring any that // don't fit. CheckedInt<uint32_t> newSize = formatLength + totalCustomLength + lengthInBytes + (sizeof(uint32_t) * 3); if (newSize.isValid()) { // If a write error occurs, set totalCustomLength to 0 so that // further processing gets ignored. nsresult rv = stream->Write32(eCustomClipboardTypeId_String); if (NS_WARN_IF(NS_FAILED(rv))) { totalCustomLength = 0; continue; } rv = stream->Write32(formatLength.value()); if (NS_WARN_IF(NS_FAILED(rv))) { totalCustomLength = 0; continue; } rv = stream->WriteBytes((const char *)type.get(), formatLength.value()); if (NS_WARN_IF(NS_FAILED(rv))) { totalCustomLength = 0; continue; } rv = stream->Write32(lengthInBytes); if (NS_WARN_IF(NS_FAILED(rv))) { totalCustomLength = 0; continue; } rv = stream->WriteBytes((const char *)data.get(), lengthInBytes); if (NS_WARN_IF(NS_FAILED(rv))) { totalCustomLength = 0; continue; } totalCustomLength = newSize.value(); } } } } 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. If totalCustomLength is 0 indicating a write error // occurred, or no data has been added to it, don't output anything, if (totalCustomLength > baseLength) { // Write out an end of data terminator. nsresult rv = stream->Write32(eCustomClipboardTypeId_None); if (NS_SUCCEEDED(rv)) { nsCOMPtr<nsIInputStream> inputStream; storageStream->NewInputStream(0, getter_AddRefs(inputStream)); RefPtr<nsStringBuffer> stringBuffer = nsStringBuffer::Alloc(totalCustomLength); // Subtract off the null terminator when reading. totalCustomLength--; // Read the data from the stream and add a null-terminator as // ToString needs it. uint32_t amountRead; rv = inputStream->Read(static_cast<char*>(stringBuffer->Data()), totalCustomLength, &amountRead); if (NS_SUCCEEDED(rv)) { 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; }