Bug 1437281 - OSX dragging image to desktop changes OSX File associations r=mystor

On Mac, 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 to avoid content sandboxing issues.

MozReview-Commit-ID: 3cb4fCr6GnL

--HG--
extra : rebase_source : 43720237b467765401b5504c57bbc1b43d4dfdc0
This commit is contained in:
Haik Aftandilian 2018-03-27 14:55:33 -07:00
Родитель 2eec0e80c1
Коммит c6b21edd12
5 изменённых файлов: 210 добавлений и 80 удалений

Просмотреть файл

@ -77,9 +77,11 @@ 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,
@ -99,6 +101,9 @@ private:
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;
@ -138,22 +143,16 @@ NS_IMPL_ISUPPORTS(nsContentAreaDragDropDataProvider, nsIFlavorDataProvider)
// 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,
@ -166,12 +165,56 @@ nsContentAreaDragDropDataProvider::SaveURIToFile(nsAString& inSourceURIString,
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:
//
@ -214,6 +257,10 @@ nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable,
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);
@ -225,6 +272,60 @@ nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable,
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;
@ -244,7 +345,7 @@ nsContentAreaDragDropDataProvider::GetFlavorData(nsITransferable *aTransferable,
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);
@ -362,6 +463,80 @@ DragDataProducer::GetNodeString(nsIContent* inNode,
}
}
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,
@ -564,67 +739,9 @@ DragDataProducer::Produce(DataTransfer* aDataTransfer,
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) {
@ -730,11 +847,12 @@ 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
@ -810,6 +928,10 @@ DragDataProducer::AddStringsToDataTransfer(nsIContent* aDragNode,
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) {

Просмотреть файл

@ -76,7 +76,7 @@ public:
NS_DECL_ISUPPORTS
NS_DECL_NSIFLAVORDATAPROVIDER
nsresult SaveURIToFile(nsAString& inSourceURIString,
nsresult SaveURIToFile(nsIURI* inSourceURI,
nsIFile* inDestFile, bool isPrivate);
};

Просмотреть файл

@ -898,7 +898,7 @@ DataTransfer::GetTransferable(uint32_t aIndex, nsILoadContext* aLoadContext)
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
@ -1202,7 +1202,8 @@ nsresult
DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
nsIVariant* aData,
uint32_t aIndex,
nsIPrincipal* aPrincipal)
nsIPrincipal* aPrincipal,
bool aHidden)
{
nsAutoString format;
GetRealFormat(aFormat, format);
@ -1211,7 +1212,7 @@ DataTransfer::SetDataWithPrincipal(const nsAString& aFormat,
RefPtr<DataTransferItem> item =
mItems->SetDataWithPrincipal(format, aData, aIndex, aPrincipal,
/* aInsertOnly = */ false,
/* aHidden= */ false,
aHidden,
rv);
return rv.StealNSResult();
}

Просмотреть файл

@ -374,7 +374,8 @@ public:
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.

Просмотреть файл

@ -41,6 +41,12 @@ interface nsIPrincipal;
#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