Bug 1437281 - OSX dragging image to desktop changes OSX File associations r?mystor draft
authorHaik Aftandilian <haftandilian@mozilla.com>
Tue, 27 Mar 2018 10:05:12 -0700
changeset 773382 4f2450396d8cada95a8867112219c7782a15f60d
parent 773381 357f9666875c217a883c2335ba597bad9b0e898e
push id104221
push userhaftandilian@mozilla.com
push dateTue, 27 Mar 2018 21:41:19 +0000
reviewersmystor
bugs1437281
milestone61.0a1
Bug 1437281 - OSX dragging image to desktop changes OSX File associations r?mystor When dragging an image, add the image request's MIME type to the transfer so that the MIME-extension check can be done in the parent process. Move the MIME-extension check to the parent process to avoid issues caused by content process sandboxing. MozReview-Commit-ID: 3cb4fCr6GnL
dom/base/nsContentAreaDragDrop.cpp
dom/base/nsContentAreaDragDrop.h
dom/events/DataTransfer.cpp
dom/events/DataTransfer.h
widget/nsITransferable.idl
--- 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,17 @@ private:
   nsCOMPtr<nsPIDOMWindowOuter> mWindow;
   nsCOMPtr<nsIContent> mTarget;
   nsCOMPtr<nsIContent> mSelectionTargetNode;
   bool mIsAltKeyPressed;
 
   nsString mUrlString;
   nsString mImageSourceString;
   nsString mImageDestFileName;
+  nsString mImageRequestMime;
   nsString mTitleString;
   // will be filled automatically if you fill urlstring
   nsString mHtmlString;
   nsString mContextString;
   nsString mInfoString;
 
   bool mIsAnchor;
   nsCOMPtr<imgIContainer> mImage;
@@ -133,50 +136,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 +250,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. Do this in the parent process because
+    // sandboxing may block 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);
+        }
+      }
+    }
+#endif /* defined(XP_MACOSX) */
+
+    targetFilename.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS,
+                               '-');
+
     // 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 +338,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 +457,89 @@ 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);
+
+    // make the filename safe for the filesystem
+    fileName.ReplaceChar(FILE_PATH_SEPARATOR FILE_ILLEGAL_CHARACTERS,
+                         '-');
+
+    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 +731,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 +839,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 +920,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),
+              mImageRequestMime, principal, /* aHidden= */ true);
 
     // 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/base/nsContentAreaDragDrop.h
+++ b/dom/base/nsContentAreaDragDrop.h
@@ -71,15 +71,15 @@ public:
 class nsContentAreaDragDropDataProvider : public nsIFlavorDataProvider
 {
   virtual ~nsContentAreaDragDropDataProvider() {}
 
 public:
   NS_DECL_ISUPPORTS
   NS_DECL_NSIFLAVORDATAPROVIDER
 
-  nsresult SaveURIToFile(nsAString& inSourceURIString,
+  nsresult SaveURIToFile(nsIURI* inSourceURI,
                          nsIFile* inDestFile, bool isPrivate);
 };
 
 
 #endif /* nsContentAreaDragDrop_h__ */
 
--- 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,
--- a/dom/events/DataTransfer.h
+++ b/dom/events/DataTransfer.h
@@ -369,17 +369,18 @@ public:
   void ClearAll();
 
   // Similar to SetData except also specifies the principal to store.
   // aData may be null when called from CacheExternalDragFormats or
   // CacheExternalClipboardFormats.
   nsresult SetDataWithPrincipal(const nsAString& aFormat,
                                 nsIVariant* aData,
                                 uint32_t aIndex,
-                                nsIPrincipal* aPrincipal);
+                                nsIPrincipal* aPrincipal,
+                                bool aHidden=false);
 
   // Variation of SetDataWithPrincipal with handles extracting
   // kCustomTypesMime data into separate types.
   void SetDataWithPrincipalFromOtherProcess(const nsAString& aFormat,
                                             nsIVariant* aData,
                                             uint32_t aIndex,
                                             nsIPrincipal* aPrincipal,
                                             bool aHidden);
--- a/widget/nsITransferable.idl
+++ b/widget/nsITransferable.idl
@@ -36,16 +36,22 @@ interface nsIPrincipal;
 
 // 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"
 
+// Holds the MIME type from the image request. This is used to ensure the
+// local application handler for the request's MIME type accepts images with
+// the given filename extension (from kFilePromiseDestFilename). When the
+// image is dragged out, we replace the extension with a compatible extension.
+#define kImageRequestMime           "text/_moz_requestmime"
+
 // the source URL for a file promise
 #define kFilePromiseURLMime         "application/x-moz-file-promise-url"
 // 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"