Bug 1437281 - OSX dragging image to desktop changes OSX File associations draft
authorHaik Aftandilian <haftandilian@mozilla.com>
Tue, 06 Mar 2018 23:22:21 -0800
changeset 764332 e98764018cf212ca2d0748c4b865a383c1b712cf
parent 761382 e90463f17048c6f065db818e00215bdadd266cbf
push id101746
push userhaftandilian@mozilla.com
push dateWed, 07 Mar 2018 18:07:08 +0000
bugs1437281
milestone60.0a1
Bug 1437281 - OSX dragging image to desktop changes OSX File associations MozReview-Commit-ID: 5Hv49doWQmr
dom/base/nsContentAreaDragDrop.cpp
widget/nsITransferable.idl
--- a/dom/base/nsContentAreaDragDrop.cpp
+++ b/dom/base/nsContentAreaDragDrop.cpp
@@ -96,16 +96,17 @@ private:
   nsCOMPtr<nsPIDOMWindowOuter> mWindow;
   nsCOMPtr<nsIContent> mTarget;
   nsCOMPtr<nsIContent> mSelectionTargetNode;
   bool mIsAltKeyPressed;
 
   nsString mUrlString;
   nsString mImageSourceString;
   nsString mImageDestFileName;
+  nsString mImageMime;
   nsString mTitleString;
   // will be filled automatically if you fill urlstring
   nsString mHtmlString;
   nsString mContextString;
   nsString mInfoString;
 
   bool mIsAnchor;
   nsCOMPtr<imgIContainer> mImage;
@@ -169,16 +170,59 @@ nsContentAreaDragDropDataProvider::SaveU
 
   // referrer policy can be anything since the referrer is nullptr
   return persist->SavePrivacyAwareURI(sourceURI, 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,
+                            nsCString* aPrimaryExtension)
+{
+  nsresult rv;
+
+  nsCOMPtr<nsIMIMEService> mimeService =
+    do_GetService("@mozilla.org/mime;1", &rv);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  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
@@ -222,16 +266,61 @@ nsContentAreaDragDropDataProvider::GetFl
     if (!supportsString)
       return NS_ERROR_FAILURE;
 
     nsAutoString targetFilename;
     supportsString->GetData(targetFilename);
     if (targetFilename.IsEmpty())
       return NS_ERROR_FAILURE;
 
+    aTransferable->GetTransferData(kImageRequestMime,
+                                   getter_AddRefs(tmp), &dataSize);
+    supportsString = do_QueryInterface(tmp);
+    if (!supportsString)
+      return NS_ERROR_FAILURE;
+
+    // Rewrite the filename extension if it doesn't match the MIME
+    nsCOMPtr<nsIURI> imageURI;
+    rv = NS_NewURI(getter_AddRefs(imageURI), sourceURLString);
+    NS_ENSURE_SUCCESS(rv, rv);
+    nsCOMPtr<nsIURL> imageURL = do_QueryInterface(imageURI);
+    if (XRE_IsParentProcess() && imageURL) {
+      nsAutoString targetMime;
+      supportsString->GetData(targetMime);
+      if (!targetMime.IsEmpty()) {
+        nsAutoCString extension;
+        rv = imageURL->GetFileExtension(extension);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        bool isValidExtension;
+        nsAutoCString mimeCString(ToNewUTF8String(targetMime));
+        nsAutoCString primaryExtension;
+        rv = CheckAndGetExtensionForMime(extension,
+                                         mimeCString,
+                                         &isValidExtension,
+                                         &primaryExtension);
+        NS_ENSURE_SUCCESS(rv, rv);
+
+        if (!isValidExtension) {
+          rv = NS_MutateURI(imageURL)
+            .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileExtension,
+                                    primaryExtension, nullptr))
+            .Finalize(imageURL);
+          NS_ENSURE_SUCCESS(rv, rv);
+
+          nsAutoCString newFileName;
+          rv = imageURL->GetFileName(newFileName);
+          NS_ENSURE_SUCCESS(rv, rv);
+          newFileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS,
+                                  '-');
+          targetFilename = NS_ConvertUTF8toUTF16(newFileName);
+        }
+      }
+    }
+
     // 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)
@@ -561,76 +650,41 @@ 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) {
+        if (imgRequest) {
           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);
+            CopyUTF8toUTF16(mimeType, mImageMime);
 
-              // 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);
+            nsAutoCString spec;
+            rv = imgUrl->GetSpec(spec);
+            NS_ENSURE_SUCCESS(rv, rv);
+            CopyUTF8toUTF16(spec, mImageSourceString);
 
-                rv = NS_MutateURI(imgUrl)
-                       .Apply(NS_MutatorMethod(&nsIURLMutator::SetFileExtension,
-                                               primaryExtension, nullptr))
-                       .Finalize(imgUrl);
-                NS_ENSURE_SUCCESS(rv, rv);
-              }
-
-              nsAutoCString fileName;
-              imgUrl->GetFileName(fileName);
+            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);
 
-              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;
-            }
+            // and the image object
+            mImage = img;
           }
         }
 
         if (parentLink) {
           // If we are dragging around an image in an anchor, then we
           // are dragging the entire anchor
           linkNode = parentLink;
           nodeToSerialize = linkNode;
@@ -807,16 +861,18 @@ 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);
+    AddString(aDataTransfer, NS_LITERAL_STRING(kImageRequestMime),
+              mImageMime, principal);
 
     // 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/widget/nsITransferable.idl
+++ b/widget/nsITransferable.idl
@@ -33,21 +33,23 @@ interface nsIPrincipal;
 #define kURLPrivateMime             "text/x-moz-url-priv"   // same as kURLDataMime but for private uses
 #define kNativeImageMime            "application/x-moz-nativeimage"
 #define kNativeHTMLMime             "application/x-moz-nativehtml"
 
 // These are used to indicate the context for a fragment of HTML source, such
 // that some parent structure and style can be preserved. kHTMLContext
 // contains the serialized ancestor elements, whereas kHTMLInfo are numbers
 // identifying where in the context the fragment was from.
-#define kHTMLContext   "text/_moz_htmlcontext"
-#define kHTMLInfo      "text/_moz_htmlinfo"
+#define kHTMLContext                "text/_moz_htmlcontext"
+#define kHTMLInfo                   "text/_moz_htmlinfo"
+#define kImageRequestMime           "text/_moz_requestmime"
 
 // the source URL for a file promise
 #define kFilePromiseURLMime         "application/x-moz-file-promise-url"
+// the source URL for a file promise
 // the destination filename for a file promise
 #define kFilePromiseDestFilename    "application/x-moz-file-promise-dest-filename"
 // a dataless flavor used to interact with the OS during file drags
 #define kFilePromiseMime            "application/x-moz-file-promise"
 // a synthetic flavor, put into the transferable once we know the destination directory of a file drag
 #define kFilePromiseDirectoryMime   "application/x-moz-file-promise-dir"
 
 #define kCustomTypesMime "application/x-moz-custom-clipdata"