summaryrefslogtreecommitdiffstats
path: root/widget/cocoa/nsDragService.mm
diff options
context:
space:
mode:
Diffstat (limited to 'widget/cocoa/nsDragService.mm')
-rw-r--r--widget/cocoa/nsDragService.mm669
1 files changed, 669 insertions, 0 deletions
diff --git a/widget/cocoa/nsDragService.mm b/widget/cocoa/nsDragService.mm
new file mode 100644
index 000000000..b92db1b2a
--- /dev/null
+++ b/widget/cocoa/nsDragService.mm
@@ -0,0 +1,669 @@
+/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "mozilla/Logging.h"
+
+#include "nsArrayUtils.h"
+#include "nsDragService.h"
+#include "nsArrayUtils.h"
+#include "nsObjCExceptions.h"
+#include "nsITransferable.h"
+#include "nsString.h"
+#include "nsClipboard.h"
+#include "nsXPCOM.h"
+#include "nsISupportsPrimitives.h"
+#include "nsCOMPtr.h"
+#include "nsPrimitiveHelpers.h"
+#include "nsLinebreakConverter.h"
+#include "nsIMacUtils.h"
+#include "nsIDOMNode.h"
+#include "nsRect.h"
+#include "nsPoint.h"
+#include "nsIIOService.h"
+#include "nsIDocument.h"
+#include "nsIContent.h"
+#include "nsView.h"
+#include "gfxContext.h"
+#include "nsCocoaUtils.h"
+#include "mozilla/gfx/2D.h"
+#include "gfxPlatform.h"
+
+using namespace mozilla;
+using namespace mozilla::gfx;
+
+extern PRLogModuleInfo* sCocoaLog;
+
+extern void EnsureLogInitialized();
+
+extern NSPasteboard* globalDragPboard;
+extern NSView* gLastDragView;
+extern NSEvent* gLastDragMouseDownEvent;
+extern bool gUserCancelledDrag;
+
+// This global makes the transferable array available to Cocoa's promised
+// file destination callback.
+nsIArray *gDraggedTransferables = nullptr;
+
+NSString* const kWildcardPboardType = @"MozillaWildcard";
+NSString* const kCorePboardType_url = @"CorePasteboardFlavorType 0x75726C20"; // 'url ' url
+NSString* const kCorePboardType_urld = @"CorePasteboardFlavorType 0x75726C64"; // 'urld' desc
+NSString* const kCorePboardType_urln = @"CorePasteboardFlavorType 0x75726C6E"; // 'urln' title
+NSString* const kUTTypeURLName = @"public.url-name";
+NSString* const kCustomTypesPboardType = @"org.mozilla.custom-clipdata";
+
+nsDragService::nsDragService()
+{
+ mNativeDragView = nil;
+ mNativeDragEvent = nil;
+
+ EnsureLogInitialized();
+}
+
+nsDragService::~nsDragService()
+{
+}
+
+static nsresult SetUpDragClipboard(nsIArray* aTransferableArray)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (!aTransferableArray)
+ return NS_ERROR_FAILURE;
+
+ uint32_t count = 0;
+ aTransferableArray->GetLength(&count);
+
+ NSPasteboard* dragPBoard = [NSPasteboard pasteboardWithName:NSDragPboard];
+
+ for (uint32_t j = 0; j < count; j++) {
+ nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(aTransferableArray, j);
+ if (!currentTransferable)
+ return NS_ERROR_FAILURE;
+
+ // Transform the transferable to an NSDictionary
+ NSDictionary* pasteboardOutputDict = nsClipboard::PasteboardDictFromTransferable(currentTransferable);
+ if (!pasteboardOutputDict)
+ return NS_ERROR_FAILURE;
+
+ // write everything out to the general pasteboard
+ unsigned int typeCount = [pasteboardOutputDict count];
+ NSMutableArray* types = [NSMutableArray arrayWithCapacity:typeCount + 1];
+ [types addObjectsFromArray:[pasteboardOutputDict allKeys]];
+ // Gecko is initiating this drag so we always want its own views to consider
+ // it. Add our wildcard type to the pasteboard to accomplish this.
+ [types addObject:kWildcardPboardType]; // we don't increase the count for the loop below on purpose
+ [dragPBoard declareTypes:types owner:nil];
+ for (unsigned int k = 0; k < typeCount; k++) {
+ NSString* currentKey = [types objectAtIndex:k];
+ id currentValue = [pasteboardOutputDict valueForKey:currentKey];
+ if (currentKey == NSStringPboardType ||
+ currentKey == kCorePboardType_url ||
+ currentKey == kCorePboardType_urld ||
+ currentKey == kCorePboardType_urln) {
+ [dragPBoard setString:currentValue forType:currentKey];
+ }
+ else if (currentKey == NSHTMLPboardType) {
+ [dragPBoard setString:(nsClipboard::WrapHtmlForSystemPasteboard(currentValue))
+ forType:currentKey];
+ }
+ else if (currentKey == NSTIFFPboardType ||
+ currentKey == kCustomTypesPboardType) {
+ [dragPBoard setData:currentValue forType:currentKey];
+ }
+ else if (currentKey == NSFilesPromisePboardType ||
+ currentKey == NSFilenamesPboardType) {
+ [dragPBoard setPropertyList:currentValue forType:currentKey];
+ }
+ }
+ }
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NSImage*
+nsDragService::ConstructDragImage(nsIDOMNode* aDOMNode,
+ LayoutDeviceIntRect* aDragRect,
+ nsIScriptableRegion* aRegion)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView);
+
+ RefPtr<SourceSurface> surface;
+ nsPresContext* pc;
+ nsresult rv = DrawDrag(aDOMNode, aRegion, mScreenPosition,
+ aDragRect, &surface, &pc);
+ if (pc && (!aDragRect->width || !aDragRect->height)) {
+ // just use some suitable defaults
+ int32_t size = nsCocoaUtils::CocoaPointsToDevPixels(20, scaleFactor);
+ aDragRect->SetRect(pc->CSSPixelsToDevPixels(mScreenPosition.x),
+ pc->CSSPixelsToDevPixels(mScreenPosition.y), size, size);
+ }
+
+ if (NS_FAILED(rv) || !surface)
+ return nil;
+
+ uint32_t width = aDragRect->width;
+ uint32_t height = aDragRect->height;
+
+ RefPtr<DataSourceSurface> dataSurface =
+ Factory::CreateDataSourceSurface(IntSize(width, height),
+ SurfaceFormat::B8G8R8A8);
+ DataSourceSurface::MappedSurface map;
+ if (!dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) {
+ return nil;
+ }
+
+ RefPtr<DrawTarget> dt =
+ Factory::CreateDrawTargetForData(BackendType::CAIRO,
+ map.mData,
+ dataSurface->GetSize(),
+ map.mStride,
+ dataSurface->GetFormat());
+ if (!dt) {
+ dataSurface->Unmap();
+ return nil;
+ }
+
+ dt->FillRect(gfx::Rect(0, 0, width, height),
+ SurfacePattern(surface, ExtendMode::CLAMP),
+ DrawOptions(1.0f, CompositionOp::OP_SOURCE));
+
+ NSBitmapImageRep* imageRep =
+ [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL
+ pixelsWide:width
+ pixelsHigh:height
+ bitsPerSample:8
+ samplesPerPixel:4
+ hasAlpha:YES
+ isPlanar:NO
+ colorSpaceName:NSDeviceRGBColorSpace
+ bytesPerRow:width * 4
+ bitsPerPixel:32];
+
+ uint8_t* dest = [imageRep bitmapData];
+ for (uint32_t i = 0; i < height; ++i) {
+ uint8_t* src = map.mData + i * map.mStride;
+ for (uint32_t j = 0; j < width; ++j) {
+ // Reduce transparency overall by multipying by a factor. Remember, Alpha
+ // is premultipled here. Also, Quartz likes RGBA, so do that translation as well.
+#ifdef IS_BIG_ENDIAN
+ dest[0] = uint8_t(src[1] * DRAG_TRANSLUCENCY);
+ dest[1] = uint8_t(src[2] * DRAG_TRANSLUCENCY);
+ dest[2] = uint8_t(src[3] * DRAG_TRANSLUCENCY);
+ dest[3] = uint8_t(src[0] * DRAG_TRANSLUCENCY);
+#else
+ dest[0] = uint8_t(src[2] * DRAG_TRANSLUCENCY);
+ dest[1] = uint8_t(src[1] * DRAG_TRANSLUCENCY);
+ dest[2] = uint8_t(src[0] * DRAG_TRANSLUCENCY);
+ dest[3] = uint8_t(src[3] * DRAG_TRANSLUCENCY);
+#endif
+ src += 4;
+ dest += 4;
+ }
+ }
+ dataSurface->Unmap();
+
+ NSImage* image =
+ [[NSImage alloc] initWithSize:NSMakeSize(width / scaleFactor,
+ height / scaleFactor)];
+ [image addRepresentation:imageRep];
+ [imageRep release];
+
+ return [image autorelease];
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+bool
+nsDragService::IsValidType(NSString* availableType, bool allowFileURL)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
+
+ // Prevent exposing fileURL for non-fileURL type.
+ // We need URL provided by dropped webloc file, but don't need file's URL.
+ // kUTTypeFileURL is returned by [NSPasteboard availableTypeFromArray:] for
+ // kUTTypeURL, since it conforms to kUTTypeURL.
+ if (!allowFileURL && [availableType isEqualToString:(id)kUTTypeFileURL]) {
+ return false;
+ }
+
+ return true;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK(false);
+}
+
+NSString*
+nsDragService::GetStringForType(NSPasteboardItem* item, const NSString* type,
+ bool allowFileURL)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ NSString* availableType = [item availableTypeFromArray:[NSArray arrayWithObjects:(id)type, nil]];
+ if (availableType && IsValidType(availableType, allowFileURL)) {
+ return [item stringForType:(id)availableType];
+ }
+
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+NSString*
+nsDragService::GetTitleForURL(NSPasteboardItem* item)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ NSString* name = GetStringForType(item, (const NSString*)kUTTypeURLName);
+ if (name) {
+ return name;
+ }
+
+ NSString* filePath = GetFilePath(item);
+ if (filePath) {
+ return [filePath lastPathComponent];
+ }
+
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+NSString*
+nsDragService::GetFilePath(NSPasteboardItem* item)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
+
+ NSString* urlString = GetStringForType(item, (const NSString*)kUTTypeFileURL, true);
+ if (urlString) {
+ NSURL* url = [NSURL URLWithString:urlString];
+ if (url) {
+ return [url path];
+ }
+ }
+
+ return nil;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
+}
+
+// We can only invoke NSView's 'dragImage:at:offset:event:pasteboard:source:slideBack:' from
+// within NSView's 'mouseDown:' or 'mouseDragged:'. Luckily 'mouseDragged' is always on the
+// stack when InvokeDragSession gets called.
+nsresult
+nsDragService::InvokeDragSessionImpl(nsIArray* aTransferableArray,
+ nsIScriptableRegion* aDragRgn,
+ uint32_t aActionType)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ mDataItems = aTransferableArray;
+
+ // put data on the clipboard
+ if (NS_FAILED(SetUpDragClipboard(aTransferableArray)))
+ return NS_ERROR_FAILURE;
+
+ CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(gLastDragView);
+
+ LayoutDeviceIntRect dragRect(0, 0, 20, 20);
+ NSImage* image = ConstructDragImage(mSourceNode, &dragRect, aDragRgn);
+ if (!image) {
+ // if no image was returned, just draw a rectangle
+ NSSize size;
+ size.width = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.width, scaleFactor);
+ size.height = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.height, scaleFactor);
+ image = [[NSImage alloc] initWithSize:size];
+ [image lockFocus];
+ [[NSColor grayColor] set];
+ NSBezierPath* path = [NSBezierPath bezierPath];
+ [path setLineWidth:2.0];
+ [path moveToPoint:NSMakePoint(0, 0)];
+ [path lineToPoint:NSMakePoint(0, size.height)];
+ [path lineToPoint:NSMakePoint(size.width, size.height)];
+ [path lineToPoint:NSMakePoint(size.width, 0)];
+ [path lineToPoint:NSMakePoint(0, 0)];
+ [path stroke];
+ [image unlockFocus];
+ }
+
+ LayoutDeviceIntPoint pt(dragRect.x, dragRect.YMost());
+ NSPoint point = nsCocoaUtils::DevPixelsToCocoaPoints(pt, scaleFactor);
+ point.y = nsCocoaUtils::FlippedScreenY(point.y);
+
+ point = nsCocoaUtils::ConvertPointFromScreen([gLastDragView window], point);
+ NSPoint localPoint = [gLastDragView convertPoint:point fromView:nil];
+
+ // Save the transferables away in case a promised file callback is invoked.
+ gDraggedTransferables = aTransferableArray;
+
+ nsBaseDragService::StartDragSession();
+ nsBaseDragService::OpenDragPopup();
+
+ // We need to retain the view and the event during the drag in case either gets destroyed.
+ mNativeDragView = [gLastDragView retain];
+ mNativeDragEvent = [gLastDragMouseDownEvent retain];
+
+ gUserCancelledDrag = false;
+ [mNativeDragView dragImage:image
+ at:localPoint
+ offset:NSZeroSize
+ event:mNativeDragEvent
+ pasteboard:[NSPasteboard pasteboardWithName:NSDragPboard]
+ source:mNativeDragView
+ slideBack:YES];
+ gUserCancelledDrag = false;
+
+ if (mDoingDrag)
+ nsBaseDragService::EndDragSession(false);
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (!aTransferable)
+ return NS_ERROR_FAILURE;
+
+ // get flavor list that includes all acceptable flavors (including ones obtained through conversion)
+ nsCOMPtr<nsIArray> flavorList;
+ nsresult rv = aTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
+ if (NS_FAILED(rv))
+ return NS_ERROR_FAILURE;
+
+ uint32_t acceptableFlavorCount;
+ flavorList->GetLength(&acceptableFlavorCount);
+
+ // if this drag originated within Mozilla we should just use the cached data from
+ // when the drag started if possible
+ if (mDataItems) {
+ nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(mDataItems, aItemIndex);
+ if (currentTransferable) {
+ for (uint32_t i = 0; i < acceptableFlavorCount; i++) {
+ nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i);
+ if (!currentFlavor)
+ continue;
+ nsXPIDLCString flavorStr;
+ currentFlavor->ToString(getter_Copies(flavorStr));
+
+ nsCOMPtr<nsISupports> dataSupports;
+ uint32_t dataSize = 0;
+ rv = currentTransferable->GetTransferData(flavorStr, getter_AddRefs(dataSupports), &dataSize);
+ if (NS_SUCCEEDED(rv)) {
+ aTransferable->SetTransferData(flavorStr, dataSupports, dataSize);
+ return NS_OK; // maybe try to fill in more types? Is there a point?
+ }
+ }
+ }
+ }
+
+ // now check the actual clipboard for data
+ for (uint32_t i = 0; i < acceptableFlavorCount; i++) {
+ nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, i);
+ if (!currentFlavor)
+ continue;
+
+ nsXPIDLCString flavorStr;
+ currentFlavor->ToString(getter_Copies(flavorStr));
+
+ MOZ_LOG(sCocoaLog, LogLevel::Info, ("nsDragService::GetData: looking for clipboard data of type %s\n", flavorStr.get()));
+
+ NSArray* droppedItems = [globalDragPboard pasteboardItems];
+ if (!droppedItems) {
+ continue;
+ }
+
+ uint32_t itemCount = [droppedItems count];
+ if (aItemIndex >= itemCount) {
+ continue;
+ }
+
+ NSPasteboardItem* item = [droppedItems objectAtIndex:aItemIndex];
+ if (!item) {
+ continue;
+ }
+
+ if (flavorStr.EqualsLiteral(kFileMime)) {
+ NSString* filePath = GetFilePath(item);
+ if (!filePath)
+ continue;
+
+ unsigned int stringLength = [filePath length];
+ unsigned int dataLength = (stringLength + 1) * sizeof(char16_t); // in bytes
+ char16_t* clipboardDataPtr = (char16_t*)malloc(dataLength);
+ if (!clipboardDataPtr)
+ return NS_ERROR_OUT_OF_MEMORY;
+ [filePath getCharacters:reinterpret_cast<unichar*>(clipboardDataPtr)];
+ clipboardDataPtr[stringLength] = 0; // null terminate
+
+ nsCOMPtr<nsIFile> file;
+ rv = NS_NewLocalFile(nsDependentString(clipboardDataPtr), true, getter_AddRefs(file));
+ free(clipboardDataPtr);
+ if (NS_FAILED(rv))
+ continue;
+
+ aTransferable->SetTransferData(flavorStr, file, dataLength);
+
+ break;
+ }
+ else if (flavorStr.EqualsLiteral(kCustomTypesMime)) {
+ NSString* availableType = [item availableTypeFromArray:[NSArray arrayWithObject:kCustomTypesPboardType]];
+ if (!availableType || !IsValidType(availableType, false)) {
+ continue;
+ }
+ NSData *pasteboardData = [item dataForType:availableType];
+ if (!pasteboardData) {
+ continue;
+ }
+
+ unsigned int dataLength = [pasteboardData length];
+ void* clipboardDataPtr = malloc(dataLength);
+ if (!clipboardDataPtr) {
+ return NS_ERROR_OUT_OF_MEMORY;
+ }
+ [pasteboardData getBytes:clipboardDataPtr];
+
+ nsCOMPtr<nsISupports> genericDataWrapper;
+ nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtr, dataLength,
+ getter_AddRefs(genericDataWrapper));
+
+ aTransferable->SetTransferData(flavorStr, genericDataWrapper, sizeof(nsIInputStream*));
+ free(clipboardDataPtr);
+ break;
+ }
+
+ NSString* pString = nil;
+ if (flavorStr.EqualsLiteral(kUnicodeMime)) {
+ pString = GetStringForType(item, (const NSString*)kUTTypeUTF8PlainText);
+ } else if (flavorStr.EqualsLiteral(kHTMLMime)) {
+ pString = GetStringForType(item, (const NSString*)kUTTypeHTML);
+ } else if (flavorStr.EqualsLiteral(kURLMime)) {
+ pString = GetStringForType(item, (const NSString*)kUTTypeURL);
+ if (pString) {
+ NSString* title = GetTitleForURL(item);
+ if (!title) {
+ title = pString;
+ }
+ pString = [NSString stringWithFormat:@"%@\n%@", pString, title];
+ }
+ } else if (flavorStr.EqualsLiteral(kURLDataMime)) {
+ pString = GetStringForType(item, (const NSString*)kUTTypeURL);
+ } else if (flavorStr.EqualsLiteral(kURLDescriptionMime)) {
+ pString = GetTitleForURL(item);
+ } else if (flavorStr.EqualsLiteral(kRTFMime)) {
+ pString = GetStringForType(item, (const NSString*)kUTTypeRTF);
+ }
+ if (pString) {
+ NSData* stringData;
+ if (flavorStr.EqualsLiteral(kRTFMime)) {
+ stringData = [pString dataUsingEncoding:NSASCIIStringEncoding];
+ } else {
+ stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding];
+ }
+ unsigned int dataLength = [stringData length];
+ void* clipboardDataPtr = malloc(dataLength);
+ if (!clipboardDataPtr)
+ return NS_ERROR_OUT_OF_MEMORY;
+ [stringData getBytes:clipboardDataPtr];
+
+ // The DOM only wants LF, so convert from MacOS line endings to DOM line endings.
+ int32_t signedDataLength = dataLength;
+ nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, &signedDataLength);
+ dataLength = signedDataLength;
+
+ // skip BOM (Byte Order Mark to distinguish little or big endian)
+ char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr;
+ if ((dataLength > 2) &&
+ ((clipboardDataPtrNoBOM[0] == 0xFEFF) ||
+ (clipboardDataPtrNoBOM[0] == 0xFFFE))) {
+ dataLength -= sizeof(char16_t);
+ clipboardDataPtrNoBOM += 1;
+ }
+
+ nsCOMPtr<nsISupports> genericDataWrapper;
+ nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength,
+ getter_AddRefs(genericDataWrapper));
+ aTransferable->SetTransferData(flavorStr, genericDataWrapper, dataLength);
+ free(clipboardDataPtr);
+ break;
+ }
+
+ // We have never supported this on Mac OS X, we should someday. Normally dragging images
+ // in is accomplished with a file path drag instead of the image data itself.
+ /*
+ if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) ||
+ flavorStr.EqualsLiteral(kJPGImageMime) || flavorStr.EqualsLiteral(kGIFImageMime)) {
+
+ }
+ */
+ }
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsDragService::IsDataFlavorSupported(const char *aDataFlavor, bool *_retval)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ *_retval = false;
+
+ if (!globalDragPboard)
+ return NS_ERROR_FAILURE;
+
+ nsDependentCString dataFlavor(aDataFlavor);
+
+ // first see if we have data for this in our cached transferable
+ if (mDataItems) {
+ uint32_t dataItemsCount;
+ mDataItems->GetLength(&dataItemsCount);
+ for (unsigned int i = 0; i < dataItemsCount; i++) {
+ nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(mDataItems, i);
+ if (!currentTransferable)
+ continue;
+
+ nsCOMPtr<nsIArray> flavorList;
+ nsresult rv = currentTransferable->FlavorsTransferableCanImport(getter_AddRefs(flavorList));
+ if (NS_FAILED(rv))
+ continue;
+
+ uint32_t flavorCount;
+ flavorList->GetLength(&flavorCount);
+ for (uint32_t j = 0; j < flavorCount; j++) {
+ nsCOMPtr<nsISupportsCString> currentFlavor = do_QueryElementAt(flavorList, j);
+ if (!currentFlavor)
+ continue;
+ nsXPIDLCString flavorStr;
+ currentFlavor->ToString(getter_Copies(flavorStr));
+ if (dataFlavor.Equals(flavorStr)) {
+ *_retval = true;
+ return NS_OK;
+ }
+ }
+ }
+ }
+
+ const NSString* type = nil;
+ bool allowFileURL = false;
+ if (dataFlavor.EqualsLiteral(kFileMime)) {
+ type = (const NSString*)kUTTypeFileURL;
+ allowFileURL = true;
+ } else if (dataFlavor.EqualsLiteral(kUnicodeMime)) {
+ type = (const NSString*)kUTTypeUTF8PlainText;
+ } else if (dataFlavor.EqualsLiteral(kHTMLMime)) {
+ type = (const NSString*)kUTTypeHTML;
+ } else if (dataFlavor.EqualsLiteral(kURLMime) ||
+ dataFlavor.EqualsLiteral(kURLDataMime)) {
+ type = (const NSString*)kUTTypeURL;
+ } else if (dataFlavor.EqualsLiteral(kURLDescriptionMime)) {
+ type = (const NSString*)kUTTypeURLName;
+ } else if (dataFlavor.EqualsLiteral(kRTFMime)) {
+ type = (const NSString*)kUTTypeRTF;
+ } else if (dataFlavor.EqualsLiteral(kCustomTypesMime)) {
+ type = (const NSString*)kCustomTypesPboardType;
+ }
+
+ NSString* availableType = [globalDragPboard availableTypeFromArray:[NSArray arrayWithObjects:(id)type, nil]];
+ if (availableType && IsValidType(availableType, allowFileURL)) {
+ *_retval = true;
+ }
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsDragService::GetNumDropItems(uint32_t* aNumItems)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ *aNumItems = 0;
+
+ // first check to see if we have a number of items cached
+ if (mDataItems) {
+ mDataItems->GetLength(aNumItems);
+ return NS_OK;
+ }
+
+ NSArray* droppedItems = [globalDragPboard pasteboardItems];
+ if (droppedItems) {
+ *aNumItems = [droppedItems count];
+ }
+
+ return NS_OK;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}
+
+NS_IMETHODIMP
+nsDragService::EndDragSession(bool aDoneDrag)
+{
+ NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT;
+
+ if (mNativeDragView) {
+ [mNativeDragView release];
+ mNativeDragView = nil;
+ }
+ if (mNativeDragEvent) {
+ [mNativeDragEvent release];
+ mNativeDragEvent = nil;
+ }
+
+ mUserCancelled = gUserCancelledDrag;
+
+ nsresult rv = nsBaseDragService::EndDragSession(aDoneDrag);
+ mDataItems = nullptr;
+ return rv;
+
+ NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT;
+}