--- a/dom/base/nsContentAreaDragDrop.cpp
+++ b/dom/base/nsContentAreaDragDrop.cpp
@@ -72,19 +72,21 @@ public:
nsISelection** aSelection,
nsIContent** aDragNode,
nsACString& aPrincipalURISpec);
private:
void AddString(DataTransfer* aDataTransfer,
const nsAString& aFlavor,
const nsAString& aData,
- nsIPrincipal* aPrincipal);
+ nsIPrincipal* aPrincipal,
+ bool aHidden=false);
nsresult AddStringsToDataTransfer(nsIContent* aDragNode,
DataTransfer* aDataTransfer);
+ nsresult GetImageData(imgIContainer* aImage, imgIRequest* aRequest);
static nsresult GetDraggableSelectionData(nsISelection* inSelection,
nsIContent* inRealTargetNode,
nsIContent **outImageOrLinkNode,
bool* outDragSelectedText);
static already_AddRefed<nsIContent> FindParentLinkNode(nsIContent* inNode);
static MOZ_MUST_USE nsresult
GetAnchorURL(nsIContent* inNode, nsAString& outURL);
static void GetNodeString(nsIContent* inNode, nsAString & outNodeString);
@@ -94,16 +96,19 @@ private:
nsCOMPtr<nsPIDOMWindowOuter> mWindow;
nsCOMPtr<nsIContent> mTarget;
nsCOMPtr<nsIContent> mSelectionTargetNode;
bool mIsAltKeyPressed;
nsString mUrlString;
nsString mImageSourceString;
nsString mImageDestFileName;
+#if defined (XP_MACOSX)
+ nsString mImageRequestMime;
+#endif
nsString mTitleString;
// will be filled automatically if you fill urlstring
nsString mHtmlString;
nsString mContextString;
nsString mInfoString;
bool mIsAnchor;
nsCOMPtr<imgIContainer> mImage;
@@ -133,50 +138,88 @@ nsContentAreaDragDrop::GetDragData(nsPID
NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
// SaveURIToFile
// used on platforms where it's possible to drag items (e.g. images)
// into the file system
nsresult
-nsContentAreaDragDropDataProvider::SaveURIToFile(nsAString& inSourceURIString,
+nsContentAreaDragDropDataProvider::SaveURIToFile(nsIURI* inSourceURI,
nsIFile* inDestFile,
bool isPrivate)
{
- nsCOMPtr<nsIURI> sourceURI;
- nsresult rv = NS_NewURI(getter_AddRefs(sourceURI), inSourceURIString);
- if (NS_FAILED(rv)) {
- return NS_ERROR_FAILURE;
- }
-
- nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(sourceURI);
+ nsCOMPtr<nsIURL> sourceURL = do_QueryInterface(inSourceURI);
if (!sourceURL) {
return NS_ERROR_NO_INTERFACE;
}
- rv = inDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
+ nsresult rv = inDestFile->CreateUnique(nsIFile::NORMAL_FILE_TYPE, 0600);
NS_ENSURE_SUCCESS(rv, rv);
// we rely on the fact that the WPB is refcounted by the channel etc,
// so we don't keep a ref to it. It will die when finished.
nsCOMPtr<nsIWebBrowserPersist> persist =
do_CreateInstance("@mozilla.org/embedding/browser/nsWebBrowserPersist;1",
&rv);
NS_ENSURE_SUCCESS(rv, rv);
persist->SetPersistFlags(nsIWebBrowserPersist::PERSIST_FLAGS_AUTODETECT_APPLY_CONVERSION);
// referrer policy can be anything since the referrer is nullptr
- return persist->SavePrivacyAwareURI(sourceURI, nullptr, nullptr,
+ return persist->SavePrivacyAwareURI(inSourceURI, nullptr, nullptr,
mozilla::net::RP_Unset,
nullptr, nullptr,
inDestFile, isPrivate);
}
+/*
+ * Check if the provided filename extension is valid for the MIME type and
+ * return the MIME type's primary extension.
+ *
+ * @param aExtension [in] the extension to check
+ * @param aMimeType [in] the MIME type to check the extension with
+ * @param aIsValidExtension [out] true if |aExtension| is valid for
+ * |aMimeType|
+ * @param aPrimaryExtension [out] the primary extension for the MIME type
+ * to potentially be used as a replacement
+ * for |aExtension|
+ */
+nsresult
+CheckAndGetExtensionForMime(const nsCString& aExtension,
+ const nsCString& aMimeType,
+ bool* aIsValidExtension,
+ nsACString* aPrimaryExtension)
+{
+ nsresult rv;
+
+ nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
+ if (NS_WARN_IF(!mimeService)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIMIMEInfo> mimeInfo;
+ rv = mimeService->GetFromTypeAndExtension(aMimeType, EmptyCString(),
+ getter_AddRefs(mimeInfo));
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ rv = mimeInfo->GetPrimaryExtension(*aPrimaryExtension);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (aExtension.IsEmpty()) {
+ *aIsValidExtension = false;
+ return NS_OK;
+ }
+
+ rv = mimeInfo->ExtensionExists(aExtension, aIsValidExtension);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ return NS_OK;
+}
+
// This is our nsIFlavorDataProvider callback. There are several
// assumptions here that make this work:
//
// 1. Someone put a kFilePromiseURLMime flavor into the transferable
// with the source URI of the file to save (as a string). We did
// that in AddStringsToDataTransfer.
//
// 2. Someone put a kFilePromiseDirectoryMime flavor into the
@@ -209,27 +252,85 @@ nsContentAreaDragDropDataProvider::GetFl
if (!supportsString)
return NS_ERROR_FAILURE;
nsAutoString sourceURLString;
supportsString->GetData(sourceURLString);
if (sourceURLString.IsEmpty())
return NS_ERROR_FAILURE;
+ nsCOMPtr<nsIURI> sourceURI;
+ rv = NS_NewURI(getter_AddRefs(sourceURI), sourceURLString);
+ NS_ENSURE_SUCCESS(rv, rv);
+
aTransferable->GetTransferData(kFilePromiseDestFilename,
getter_AddRefs(tmp), &dataSize);
supportsString = do_QueryInterface(tmp);
if (!supportsString)
return NS_ERROR_FAILURE;
nsAutoString targetFilename;
supportsString->GetData(targetFilename);
if (targetFilename.IsEmpty())
return NS_ERROR_FAILURE;
+#if defined(XP_MACOSX)
+ // Use the image request's MIME type to ensure the filename's
+ // extension is compatible with the OS's handler for this type.
+ // If it isn't, or is missing, replace the extension with the
+ // primary extension. On Mac, do this in the parent process
+ // because sandboxing blocks access to MIME-handler info from
+ // content processes.
+ if (XRE_IsParentProcess()) {
+ aTransferable->GetTransferData(kImageRequestMime,
+ getter_AddRefs(tmp), &dataSize);
+ supportsString = do_QueryInterface(tmp);
+ if (!supportsString)
+ return NS_ERROR_FAILURE;
+
+ nsAutoString imageRequestMime;
+ supportsString->GetData(imageRequestMime);
+
+ // If we have a MIME type, check the extension is compatible
+ if (!imageRequestMime.IsEmpty()) {
+ // Build a URL to get the filename extension
+ nsCOMPtr<nsIURL> imageURL = do_QueryInterface(sourceURI, &rv);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ nsAutoCString extension;
+ rv = imageURL->GetFileExtension(extension);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ NS_ConvertUTF16toUTF8 mimeCString(imageRequestMime);
+ bool isValidExtension;
+ nsAutoCString primaryExtension;
+ rv = CheckAndGetExtensionForMime(extension,
+ mimeCString,
+ &isValidExtension,
+ &primaryExtension);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ if (!isValidExtension) {
+ // The filename extension is missing or incompatible
+ // with the MIME type, replace it with the primary
+ // extension.
+ nsAutoCString newFileName;
+ rv = imageURL->GetFileBaseName(newFileName);
+ NS_ENSURE_SUCCESS(rv, rv);
+ newFileName.Append(".");
+ newFileName.Append(primaryExtension);
+ targetFilename = NS_ConvertUTF8toUTF16(newFileName);
+ }
+ }
+ }
+ // make the filename safe for the filesystem
+ targetFilename.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS,
+ '-');
+#endif /* defined(XP_MACOSX) */
+
// get the target directory from the kFilePromiseDirectoryMime
// flavor
nsCOMPtr<nsISupports> dirPrimitive;
dataSize = 0;
aTransferable->GetTransferData(kFilePromiseDirectoryMime,
getter_AddRefs(dirPrimitive), &dataSize);
nsCOMPtr<nsIFile> destDirectory = do_QueryInterface(dirPrimitive);
if (!destDirectory)
@@ -239,17 +340,17 @@ nsContentAreaDragDropDataProvider::GetFl
rv = destDirectory->Clone(getter_AddRefs(file));
NS_ENSURE_SUCCESS(rv, rv);
file->Append(targetFilename);
bool isPrivate;
aTransferable->GetIsPrivateData(&isPrivate);
- rv = SaveURIToFile(sourceURLString, file, isPrivate);
+ rv = SaveURIToFile(sourceURI, file, isPrivate);
// send back an nsIFile
if (NS_SUCCEEDED(rv)) {
CallQueryInterface(file, aData);
*aDataLen = sizeof(nsIFile*);
}
}
return rv;
@@ -358,16 +459,90 @@ DragDataProducer::GetNodeString(nsIConte
RefPtr<nsRange> range = doc->CreateRange(IgnoreErrors());
if (range) {
range->SelectNode(*node, IgnoreErrors());
range->ToString(outNodeString, IgnoreErrors());
}
}
nsresult
+DragDataProducer::GetImageData(imgIContainer* aImage, imgIRequest* aRequest)
+{
+ nsCOMPtr<nsIURI> imgUri;
+ aRequest->GetURI(getter_AddRefs(imgUri));
+
+ nsCOMPtr<nsIURL> imgUrl(do_QueryInterface(imgUri));
+ if (imgUrl) {
+ nsAutoCString spec;
+ nsresult rv = imgUrl->GetSpec(spec);
+ NS_ENSURE_SUCCESS(rv, rv);
+
+ // pass out the image source string
+ CopyUTF8toUTF16(spec, mImageSourceString);
+
+ nsCString mimeType;
+ aRequest->GetMimeType(getter_Copies(mimeType));
+
+#if defined(XP_MACOSX)
+ // Save the MIME type so we can make sure the extension
+ // is compatible (and replace it if it isn't) when the
+ // image is dropped. On Mac, we need to get the OS MIME
+ // handler information in the parent due to sandboxing.
+ CopyUTF8toUTF16(mimeType, mImageRequestMime);
+#else
+ nsCOMPtr<nsIMIMEService> mimeService = do_GetService("@mozilla.org/mime;1");
+ if (NS_WARN_IF(!mimeService)) {
+ return NS_ERROR_FAILURE;
+ }
+
+ nsCOMPtr<nsIMIMEInfo> mimeInfo;
+ mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
+ getter_AddRefs(mimeInfo));
+ if (mimeInfo) {
+ nsAutoCString extension;
+ imgUrl->GetFileExtension(extension);
+
+ bool validExtension;
+ if (extension.IsEmpty() ||
+ NS_FAILED(mimeInfo->ExtensionExists(extension,
+ &validExtension)) ||
+ !validExtension) {
+ // Fix the file extension in the URL
+ nsAutoCString primaryExtension;
+ mimeInfo->GetPrimaryExtension(primaryExtension);
+
+ rv = NS_MutateURI(imgUrl)
+ .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileExtension,
+ primaryExtension, nullptr))
+ .Finalize(imgUrl);
+ NS_ENSURE_SUCCESS(rv, rv);
+ }
+ }
+#endif /* defined(XP_MACOSX) */
+
+ nsAutoCString fileName;
+ imgUrl->GetFileName(fileName);
+
+ NS_UnescapeURL(fileName);
+
+#if !defined(XP_MACOSX)
+ // make the filename safe for the filesystem
+ fileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS, '-');
+#endif
+
+ CopyUTF8toUTF16(fileName, mImageDestFileName);
+
+ // and the image object
+ mImage = aImage;
+ }
+
+ return NS_OK;
+}
+
+nsresult
DragDataProducer::Produce(DataTransfer* aDataTransfer,
bool* aCanDrag,
nsISelection** aSelection,
nsIContent** aDragNode,
nsACString& aPrincipalURISpec)
{
NS_PRECONDITION(aCanDrag && aSelection && aDataTransfer && aDragNode,
"null pointer passed to Produce");
@@ -559,77 +734,19 @@ DragDataProducer::Produce(DataTransfer*
}
nsCOMPtr<imgIRequest> imgRequest;
// grab the image data, and its request.
nsCOMPtr<imgIContainer> img =
nsContentUtils::GetImageFromContent(image,
getter_AddRefs(imgRequest));
-
- nsCOMPtr<nsIMIMEService> mimeService =
- do_GetService("@mozilla.org/mime;1");
-
- // Fix the file extension in the URL if necessary
- if (imgRequest && mimeService) {
- nsCOMPtr<nsIURI> imgUri;
- imgRequest->GetURI(getter_AddRefs(imgUri));
-
- nsCOMPtr<nsIURL> imgUrl(do_QueryInterface(imgUri));
-
- if (imgUrl) {
- nsAutoCString extension;
- imgUrl->GetFileExtension(extension);
-
- nsCString mimeType;
- imgRequest->GetMimeType(getter_Copies(mimeType));
-
- nsCOMPtr<nsIMIMEInfo> mimeInfo;
- mimeService->GetFromTypeAndExtension(mimeType, EmptyCString(),
- getter_AddRefs(mimeInfo));
-
- if (mimeInfo) {
- nsAutoCString spec;
- rv = imgUrl->GetSpec(spec);
- NS_ENSURE_SUCCESS(rv, rv);
-
- // pass out the image source string
- CopyUTF8toUTF16(spec, mImageSourceString);
-
- bool validExtension;
- if (extension.IsEmpty() ||
- NS_FAILED(mimeInfo->ExtensionExists(extension,
- &validExtension)) ||
- !validExtension) {
- // Fix the file extension in the URL
- nsAutoCString primaryExtension;
- mimeInfo->GetPrimaryExtension(primaryExtension);
-
- rv = NS_MutateURI(imgUrl)
- .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileExtension,
- primaryExtension, nullptr))
- .Finalize(imgUrl);
- NS_ENSURE_SUCCESS(rv, rv);
- }
-
- nsAutoCString fileName;
- imgUrl->GetFileName(fileName);
-
- NS_UnescapeURL(fileName);
-
- // make the filename safe for the filesystem
- fileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS,
- '-');
-
- CopyUTF8toUTF16(fileName, mImageDestFileName);
-
- // and the image object
- mImage = img;
- }
- }
+ if (imgRequest) {
+ rv = GetImageData(img, imgRequest);
+ NS_ENSURE_SUCCESS(rv, rv);
}
if (parentLink) {
// If we are dragging around an image in an anchor, then we
// are dragging the entire anchor
linkNode = parentLink;
nodeToSerialize = linkNode;
} else {
@@ -725,21 +842,22 @@ DragDataProducer::Produce(DataTransfer*
NS_IF_ADDREF(*aDragNode = dragNode);
return NS_OK;
}
void
DragDataProducer::AddString(DataTransfer* aDataTransfer,
const nsAString& aFlavor,
const nsAString& aData,
- nsIPrincipal* aPrincipal)
+ nsIPrincipal* aPrincipal,
+ bool aHidden)
{
RefPtr<nsVariantCC> variant = new nsVariantCC();
variant->SetAsAString(aData);
- aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal);
+ aDataTransfer->SetDataWithPrincipal(aFlavor, variant, 0, aPrincipal, aHidden);
}
nsresult
DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode,
DataTransfer* aDataTransfer)
{
NS_ASSERTION(aDragNode, "adding strings for null node");
@@ -805,16 +923,20 @@ DragDataProducer::AddStringsToDataTransf
aDataTransfer->SetDataWithPrincipal(NS_LITERAL_STRING(kFilePromiseMime),
variant, 0, principal);
}
AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseURLMime),
mImageSourceString, principal);
AddString(aDataTransfer, NS_LITERAL_STRING(kFilePromiseDestFilename),
mImageDestFileName, principal);
+#if defined(XP_MACOSX)
+ AddString(aDataTransfer, NS_LITERAL_STRING(kImageRequestMime),
+ mImageRequestMime, principal, /* aHidden= */ true);
+#endif
// if not an anchor, add the image url
if (!mIsAnchor) {
AddString(aDataTransfer, NS_LITERAL_STRING(kURLDataMime), mUrlString, principal);
AddString(aDataTransfer, NS_LITERAL_STRING("text/uri-list"), mUrlString, principal);
}
}
--- a/dom/events/DataTransfer.cpp
+++ b/dom/events/DataTransfer.cpp
@@ -893,17 +893,17 @@ DataTransfer::GetTransferable(uint32_t a
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 };
+ kMozTextInternal, kHTMLContext, kHTMLInfo, kImageRequestMime };
/*
* 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.
*
@@ -1197,26 +1197,27 @@ DataTransfer::MozItemCount() const
{
return mItems->MozItemCount();
}
nsresult
DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
nsIVariant* aData,
uint32_t aIndex,
- nsIPrincipal* aPrincipal)
+ nsIPrincipal* aPrincipal,
+ bool aHidden)
{
nsAutoString format;
GetRealFormat(aFormat, format);
ErrorResult rv;
RefPtr<DataTransferItem> item =
mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
/* aInsertOnly = */ false,
- /* aHidden= */ false,
+ aHidden,
rv);
return rv.StealNSResult();
}
void
DataTransfer::SetDataWithPrincipalFromOtherProcess(const nsAString& aFormat,
nsIVariant* aData,
uint32_t aIndex,