1/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ 2/* This Source Code Form is subject to the terms of the Mozilla Public 3 * License, v. 2.0. If a copy of the MPL was not distributed with this 4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 5 6#include "mozilla/Logging.h" 7 8#include "gfxContext.h" 9#include "nsArrayUtils.h" 10#include "nsDragService.h" 11#include "nsArrayUtils.h" 12#include "nsObjCExceptions.h" 13#include "nsITransferable.h" 14#include "nsString.h" 15#include "nsClipboard.h" 16#include "nsXPCOM.h" 17#include "nsCOMPtr.h" 18#include "nsPrimitiveHelpers.h" 19#include "nsLinebreakConverter.h" 20#include "nsINode.h" 21#include "nsRect.h" 22#include "nsPoint.h" 23#include "mozilla/PresShell.h" 24#include "mozilla/dom/Document.h" 25#include "mozilla/dom/DocumentInlines.h" 26#include "nsIContent.h" 27#include "nsView.h" 28#include "nsCocoaUtils.h" 29#include "mozilla/gfx/2D.h" 30#include "gfxPlatform.h" 31#include "nsDeviceContext.h" 32 33using namespace mozilla; 34using namespace mozilla::gfx; 35 36extern mozilla::LazyLogModule sCocoaLog; 37 38extern NSPasteboard* globalDragPboard; 39extern ChildView* gLastDragView; 40extern NSEvent* gLastDragMouseDownEvent; 41extern bool gUserCancelledDrag; 42 43// This global makes the transferable array available to Cocoa's promised 44// file destination callback. 45nsIArray* gDraggedTransferables = nullptr; 46 47NSString* const kPublicUrlPboardType = @"public.url"; 48NSString* const kPublicUrlNamePboardType = @"public.url-name"; 49NSString* const kUrlsWithTitlesPboardType = @"WebURLsWithTitlesPboardType"; 50NSString* const kMozWildcardPboardType = @"org.mozilla.MozillaWildcard"; 51NSString* const kMozCustomTypesPboardType = @"org.mozilla.custom-clipdata"; 52NSString* const kMozFileUrlsPboardType = @"org.mozilla.file-urls"; 53 54nsDragService::nsDragService() 55 : mNativeDragView(nil), mNativeDragEvent(nil), mDragImageChanged(false) {} 56 57nsDragService::~nsDragService() {} 58 59NSImage* nsDragService::ConstructDragImage(nsINode* aDOMNode, const Maybe<CSSIntRegion>& aRegion, 60 NSPoint* aDragPoint) { 61 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 62 63 CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mNativeDragView); 64 65 LayoutDeviceIntRect dragRect(0, 0, 20, 20); 66 NSImage* image = ConstructDragImage(mSourceNode, aRegion, mScreenPosition, &dragRect); 67 if (!image) { 68 // if no image was returned, just draw a rectangle 69 NSSize size; 70 size.width = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.width, scaleFactor); 71 size.height = nsCocoaUtils::DevPixelsToCocoaPoints(dragRect.height, scaleFactor); 72 image = [NSImage imageWithSize:size 73 flipped:YES 74 drawingHandler:^BOOL(NSRect dstRect) { 75 [[NSColor grayColor] set]; 76 NSBezierPath* path = [NSBezierPath bezierPathWithRect:dstRect]; 77 [path setLineWidth:2.0]; 78 [path stroke]; 79 return YES; 80 }]; 81 } 82 83 LayoutDeviceIntPoint pt(dragRect.x, dragRect.YMost()); 84 NSPoint point = nsCocoaUtils::DevPixelsToCocoaPoints(pt, scaleFactor); 85 point.y = nsCocoaUtils::FlippedScreenY(point.y); 86 87 point = nsCocoaUtils::ConvertPointFromScreen([mNativeDragView window], point); 88 *aDragPoint = [mNativeDragView convertPoint:point fromView:nil]; 89 90 return image; 91 92 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 93} 94 95NSImage* nsDragService::ConstructDragImage(nsINode* aDOMNode, const Maybe<CSSIntRegion>& aRegion, 96 CSSIntPoint aPoint, LayoutDeviceIntRect* aDragRect) { 97 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 98 99 CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mNativeDragView); 100 101 RefPtr<SourceSurface> surface; 102 nsPresContext* pc; 103 nsresult rv = DrawDrag(aDOMNode, aRegion, aPoint, aDragRect, &surface, &pc); 104 if (pc && (!aDragRect->width || !aDragRect->height)) { 105 // just use some suitable defaults 106 int32_t size = nsCocoaUtils::CocoaPointsToDevPixels(20, scaleFactor); 107 aDragRect->SetRect(pc->CSSPixelsToDevPixels(aPoint.x), pc->CSSPixelsToDevPixels(aPoint.y), size, 108 size); 109 } 110 111 if (NS_FAILED(rv) || !surface) return nil; 112 113 uint32_t width = aDragRect->width; 114 uint32_t height = aDragRect->height; 115 116 RefPtr<DataSourceSurface> dataSurface = 117 Factory::CreateDataSourceSurface(IntSize(width, height), SurfaceFormat::B8G8R8A8); 118 DataSourceSurface::MappedSurface map; 119 if (!dataSurface->Map(DataSourceSurface::MapType::READ_WRITE, &map)) { 120 return nil; 121 } 122 123 RefPtr<DrawTarget> dt = Factory::CreateDrawTargetForData( 124 BackendType::CAIRO, map.mData, dataSurface->GetSize(), map.mStride, dataSurface->GetFormat()); 125 if (!dt) { 126 dataSurface->Unmap(); 127 return nil; 128 } 129 130 dt->FillRect(gfx::Rect(0, 0, width, height), SurfacePattern(surface, ExtendMode::CLAMP), 131 DrawOptions(1.0f, CompositionOp::OP_SOURCE)); 132 133 NSBitmapImageRep* imageRep = 134 [[NSBitmapImageRep alloc] initWithBitmapDataPlanes:NULL 135 pixelsWide:width 136 pixelsHigh:height 137 bitsPerSample:8 138 samplesPerPixel:4 139 hasAlpha:YES 140 isPlanar:NO 141 colorSpaceName:NSDeviceRGBColorSpace 142 bytesPerRow:width * 4 143 bitsPerPixel:32]; 144 145 uint8_t* dest = [imageRep bitmapData]; 146 for (uint32_t i = 0; i < height; ++i) { 147 uint8_t* src = map.mData + i * map.mStride; 148 for (uint32_t j = 0; j < width; ++j) { 149 // Reduce transparency overall by multipying by a factor. Remember, Alpha 150 // is premultipled here. Also, Quartz likes RGBA, so do that translation as well. 151#ifdef IS_BIG_ENDIAN 152 dest[0] = uint8_t(src[1] * DRAG_TRANSLUCENCY); 153 dest[1] = uint8_t(src[2] * DRAG_TRANSLUCENCY); 154 dest[2] = uint8_t(src[3] * DRAG_TRANSLUCENCY); 155 dest[3] = uint8_t(src[0] * DRAG_TRANSLUCENCY); 156#else 157 dest[0] = uint8_t(src[2] * DRAG_TRANSLUCENCY); 158 dest[1] = uint8_t(src[1] * DRAG_TRANSLUCENCY); 159 dest[2] = uint8_t(src[0] * DRAG_TRANSLUCENCY); 160 dest[3] = uint8_t(src[3] * DRAG_TRANSLUCENCY); 161#endif 162 src += 4; 163 dest += 4; 164 } 165 } 166 dataSurface->Unmap(); 167 168 NSImage* image = 169 [[NSImage alloc] initWithSize:NSMakeSize(width / scaleFactor, height / scaleFactor)]; 170 [image addRepresentation:imageRep]; 171 [imageRep release]; 172 173 return [image autorelease]; 174 175 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 176} 177 178bool nsDragService::IsValidType(NSString* availableType, bool allowFileURL) { 179 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN; 180 181 // Prevent exposing fileURL for non-fileURL type. 182 // We need URL provided by dropped webloc file, but don't need file's URL. 183 // kUTTypeFileURL is returned by [NSPasteboard availableTypeFromArray:] for 184 // kPublicUrlPboardType, since it conforms to kPublicUrlPboardType. 185 bool isValid = true; 186 if (!allowFileURL && 187 [availableType isEqualToString:[UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL]]) { 188 isValid = false; 189 } 190 191 return isValid; 192 193 NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(false); 194} 195 196NSString* nsDragService::GetStringForType(NSPasteboardItem* item, const NSString* type, 197 bool allowFileURL) { 198 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 199 200 NSString* availableType = [item availableTypeFromArray:[NSArray arrayWithObjects:(id)type, nil]]; 201 if (availableType && IsValidType(availableType, allowFileURL)) { 202 return [item stringForType:(id)availableType]; 203 } 204 205 return nil; 206 207 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 208} 209 210NSString* nsDragService::GetTitleForURL(NSPasteboardItem* item) { 211 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 212 213 NSString* name = 214 GetStringForType(item, [UTIHelper stringFromPboardType:kPublicUrlNamePboardType]); 215 if (name) { 216 return name; 217 } 218 219 NSString* filePath = GetFilePath(item); 220 if (filePath) { 221 return [filePath lastPathComponent]; 222 } 223 224 return nil; 225 226 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 227} 228 229NSString* nsDragService::GetFilePath(NSPasteboardItem* item) { 230 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL; 231 232 NSString* urlString = 233 GetStringForType(item, [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL], true); 234 if (urlString) { 235 NSURL* url = [NSURL URLWithString:urlString]; 236 if (url) { 237 return [url path]; 238 } 239 } 240 241 return nil; 242 243 NS_OBJC_END_TRY_ABORT_BLOCK_NIL; 244} 245 246nsresult nsDragService::InvokeDragSessionImpl(nsIArray* aTransferableArray, 247 const Maybe<CSSIntRegion>& aRegion, 248 uint32_t aActionType) { 249 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 250 251 if (!gLastDragView) { 252 // gLastDragView is non-null between -[ChildView mouseDown:] and -[ChildView mouseUp:]. 253 // If we get here with gLastDragView being null, that means that the mouse button has already 254 // been released. In that case we need to abort the drag because the OS won't know where to drop 255 // whatever's being dragged, and we might end up with a stuck drag & drop session. 256 return NS_ERROR_FAILURE; 257 } 258 259 mDataItems = aTransferableArray; 260 261 // Save the transferables away in case a promised file callback is invoked. 262 gDraggedTransferables = aTransferableArray; 263 264 // We need to retain the view and the event during the drag in case either 265 // gets destroyed. 266 mNativeDragView = [gLastDragView retain]; 267 mNativeDragEvent = [gLastDragMouseDownEvent retain]; 268 269 gUserCancelledDrag = false; 270 271 NSPasteboardItem* pbItem = [NSPasteboardItem new]; 272 NSMutableArray* types = [NSMutableArray arrayWithCapacity:5]; 273 274 if (gDraggedTransferables) { 275 uint32_t count = 0; 276 gDraggedTransferables->GetLength(&count); 277 278 for (uint32_t j = 0; j < count; j++) { 279 nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(aTransferableArray, j); 280 if (!currentTransferable) { 281 return NS_ERROR_FAILURE; 282 } 283 284 // Transform the transferable to an NSDictionary 285 NSDictionary* pasteboardOutputDict = 286 nsClipboard::PasteboardDictFromTransferable(currentTransferable); 287 if (!pasteboardOutputDict) { 288 return NS_ERROR_FAILURE; 289 } 290 291 // write everything out to the general pasteboard 292 [types addObjectsFromArray:[pasteboardOutputDict allKeys]]; 293 // Gecko is initiating this drag so we always want its own views to 294 // consider it. Add our wildcard type to the pasteboard to accomplish 295 // this. 296 [types addObject:[UTIHelper stringFromPboardType:kMozWildcardPboardType]]; 297 } 298 } 299 [pbItem setDataProvider:mNativeDragView forTypes:types]; 300 301 NSPoint draggingPoint; 302 NSImage* image = ConstructDragImage(mSourceNode, aRegion, &draggingPoint); 303 304 NSRect localDragRect = image.alignmentRect; 305 localDragRect.origin.x = draggingPoint.x; 306 localDragRect.origin.y = draggingPoint.y - localDragRect.size.height; 307 308 NSDraggingItem* dragItem = [[NSDraggingItem alloc] initWithPasteboardWriter:pbItem]; 309 [pbItem release]; 310 [dragItem setDraggingFrame:localDragRect contents:image]; 311 312 nsBaseDragService::StartDragSession(); 313 nsBaseDragService::OpenDragPopup(); 314 315 NSDraggingSession* draggingSession = [mNativeDragView 316 beginDraggingSessionWithItems:[NSArray arrayWithObject:[dragItem autorelease]] 317 event:mNativeDragEvent 318 source:mNativeDragView]; 319 draggingSession.animatesToStartingPositionsOnCancelOrFail = YES; 320 321 return NS_OK; 322 323 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 324} 325 326NS_IMETHODIMP 327nsDragService::GetData(nsITransferable* aTransferable, uint32_t aItemIndex) { 328 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 329 330 if (!aTransferable) return NS_ERROR_FAILURE; 331 332 // get flavor list that includes all acceptable flavors (including ones obtained through 333 // conversion) 334 nsTArray<nsCString> flavors; 335 nsresult rv = aTransferable->FlavorsTransferableCanImport(flavors); 336 if (NS_FAILED(rv)) return NS_ERROR_FAILURE; 337 338 // if this drag originated within Mozilla we should just use the cached data from 339 // when the drag started if possible 340 if (mDataItems) { 341 nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(mDataItems, aItemIndex); 342 if (currentTransferable) { 343 for (uint32_t i = 0; i < flavors.Length(); i++) { 344 nsCString& flavorStr = flavors[i]; 345 346 nsCOMPtr<nsISupports> dataSupports; 347 rv = currentTransferable->GetTransferData(flavorStr.get(), getter_AddRefs(dataSupports)); 348 if (NS_SUCCEEDED(rv)) { 349 aTransferable->SetTransferData(flavorStr.get(), dataSupports); 350 return NS_OK; // maybe try to fill in more types? Is there a point? 351 } 352 } 353 } 354 } 355 356 // now check the actual clipboard for data 357 for (uint32_t i = 0; i < flavors.Length(); i++) { 358 nsCString& flavorStr = flavors[i]; 359 360 MOZ_LOG(sCocoaLog, LogLevel::Info, 361 ("nsDragService::GetData: looking for clipboard data of type %s\n", flavorStr.get())); 362 363 NSArray* droppedItems = [globalDragPboard pasteboardItems]; 364 if (!droppedItems) { 365 continue; 366 } 367 368 uint32_t itemCount = [droppedItems count]; 369 if (aItemIndex >= itemCount) { 370 continue; 371 } 372 373 NSPasteboardItem* item = [droppedItems objectAtIndex:aItemIndex]; 374 if (!item) { 375 continue; 376 } 377 378 if (flavorStr.EqualsLiteral(kFileMime)) { 379 NSString* filePath = GetFilePath(item); 380 if (!filePath) continue; 381 382 unsigned int stringLength = [filePath length]; 383 unsigned int dataLength = (stringLength + 1) * sizeof(char16_t); // in bytes 384 char16_t* clipboardDataPtr = (char16_t*)malloc(dataLength); 385 if (!clipboardDataPtr) return NS_ERROR_OUT_OF_MEMORY; 386 [filePath getCharacters:reinterpret_cast<unichar*>(clipboardDataPtr)]; 387 clipboardDataPtr[stringLength] = 0; // null terminate 388 389 nsCOMPtr<nsIFile> file; 390 rv = NS_NewLocalFile(nsDependentString(clipboardDataPtr), true, getter_AddRefs(file)); 391 free(clipboardDataPtr); 392 if (NS_FAILED(rv)) continue; 393 394 aTransferable->SetTransferData(flavorStr.get(), file); 395 396 break; 397 } else if (flavorStr.EqualsLiteral(kCustomTypesMime)) { 398 NSString* availableType = 399 [item availableTypeFromArray:[NSArray arrayWithObject:kMozCustomTypesPboardType]]; 400 if (!availableType || !IsValidType(availableType, false)) { 401 continue; 402 } 403 NSData* pasteboardData = [item dataForType:availableType]; 404 if (!pasteboardData) { 405 continue; 406 } 407 408 unsigned int dataLength = [pasteboardData length]; 409 void* clipboardDataPtr = malloc(dataLength); 410 if (!clipboardDataPtr) { 411 return NS_ERROR_OUT_OF_MEMORY; 412 } 413 [pasteboardData getBytes:clipboardDataPtr]; 414 415 nsCOMPtr<nsISupports> genericDataWrapper; 416 nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtr, dataLength, 417 getter_AddRefs(genericDataWrapper)); 418 419 aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper); 420 free(clipboardDataPtr); 421 break; 422 } 423 424 NSString* pString = nil; 425 if (flavorStr.EqualsLiteral(kUnicodeMime)) { 426 pString = GetStringForType(item, [UTIHelper stringFromPboardType:NSPasteboardTypeString]); 427 } else if (flavorStr.EqualsLiteral(kHTMLMime)) { 428 pString = GetStringForType(item, [UTIHelper stringFromPboardType:NSPasteboardTypeHTML]); 429 } else if (flavorStr.EqualsLiteral(kURLMime)) { 430 pString = GetStringForType(item, [UTIHelper stringFromPboardType:kPublicUrlPboardType]); 431 if (pString) { 432 NSString* title = GetTitleForURL(item); 433 if (!title) { 434 title = pString; 435 } 436 pString = [NSString stringWithFormat:@"%@\n%@", pString, title]; 437 } 438 } else if (flavorStr.EqualsLiteral(kURLDataMime)) { 439 pString = GetStringForType(item, [UTIHelper stringFromPboardType:kPublicUrlPboardType]); 440 } else if (flavorStr.EqualsLiteral(kURLDescriptionMime)) { 441 pString = GetTitleForURL(item); 442 } else if (flavorStr.EqualsLiteral(kRTFMime)) { 443 pString = GetStringForType(item, [UTIHelper stringFromPboardType:NSPasteboardTypeRTF]); 444 } 445 if (pString) { 446 NSData* stringData; 447 if (flavorStr.EqualsLiteral(kRTFMime)) { 448 stringData = [pString dataUsingEncoding:NSASCIIStringEncoding]; 449 } else { 450 stringData = [pString dataUsingEncoding:NSUnicodeStringEncoding]; 451 } 452 unsigned int dataLength = [stringData length]; 453 void* clipboardDataPtr = malloc(dataLength); 454 if (!clipboardDataPtr) return NS_ERROR_OUT_OF_MEMORY; 455 [stringData getBytes:clipboardDataPtr]; 456 457 // The DOM only wants LF, so convert from MacOS line endings to DOM line endings. 458 int32_t signedDataLength = dataLength; 459 nsLinebreakHelpers::ConvertPlatformToDOMLinebreaks(flavorStr, &clipboardDataPtr, 460 &signedDataLength); 461 dataLength = signedDataLength; 462 463 // skip BOM (Byte Order Mark to distinguish little or big endian) 464 char16_t* clipboardDataPtrNoBOM = (char16_t*)clipboardDataPtr; 465 if ((dataLength > 2) && 466 ((clipboardDataPtrNoBOM[0] == 0xFEFF) || (clipboardDataPtrNoBOM[0] == 0xFFFE))) { 467 dataLength -= sizeof(char16_t); 468 clipboardDataPtrNoBOM += 1; 469 } 470 471 nsCOMPtr<nsISupports> genericDataWrapper; 472 nsPrimitiveHelpers::CreatePrimitiveForData(flavorStr, clipboardDataPtrNoBOM, dataLength, 473 getter_AddRefs(genericDataWrapper)); 474 aTransferable->SetTransferData(flavorStr.get(), genericDataWrapper); 475 free(clipboardDataPtr); 476 break; 477 } 478 479 // We have never supported this on Mac OS X, we should someday. Normally dragging images 480 // in is accomplished with a file path drag instead of the image data itself. 481 /* 482 if (flavorStr.EqualsLiteral(kPNGImageMime) || flavorStr.EqualsLiteral(kJPEGImageMime) || 483 flavorStr.EqualsLiteral(kJPGImageMime) || flavorStr.EqualsLiteral(kGIFImageMime)) { 484 485 } 486 */ 487 } 488 return NS_OK; 489 490 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 491} 492 493NS_IMETHODIMP 494nsDragService::IsDataFlavorSupported(const char* aDataFlavor, bool* _retval) { 495 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 496 497 *_retval = false; 498 499 if (!globalDragPboard) return NS_ERROR_FAILURE; 500 501 nsDependentCString dataFlavor(aDataFlavor); 502 503 // first see if we have data for this in our cached transferable 504 if (mDataItems) { 505 uint32_t dataItemsCount; 506 mDataItems->GetLength(&dataItemsCount); 507 for (unsigned int i = 0; i < dataItemsCount; i++) { 508 nsCOMPtr<nsITransferable> currentTransferable = do_QueryElementAt(mDataItems, i); 509 if (!currentTransferable) continue; 510 511 nsTArray<nsCString> flavors; 512 nsresult rv = currentTransferable->FlavorsTransferableCanImport(flavors); 513 if (NS_FAILED(rv)) continue; 514 515 for (uint32_t j = 0; j < flavors.Length(); j++) { 516 if (dataFlavor.Equals(flavors[j])) { 517 *_retval = true; 518 return NS_OK; 519 } 520 } 521 } 522 } 523 524 const NSString* type = nil; 525 bool allowFileURL = false; 526 if (dataFlavor.EqualsLiteral(kFileMime)) { 527 type = [UTIHelper stringFromPboardType:(NSString*)kUTTypeFileURL]; 528 allowFileURL = true; 529 } else if (dataFlavor.EqualsLiteral(kUnicodeMime)) { 530 type = [UTIHelper stringFromPboardType:NSPasteboardTypeString]; 531 } else if (dataFlavor.EqualsLiteral(kHTMLMime)) { 532 type = [UTIHelper stringFromPboardType:NSPasteboardTypeHTML]; 533 } else if (dataFlavor.EqualsLiteral(kURLMime) || dataFlavor.EqualsLiteral(kURLDataMime)) { 534 type = [UTIHelper stringFromPboardType:kPublicUrlPboardType]; 535 } else if (dataFlavor.EqualsLiteral(kURLDescriptionMime)) { 536 type = [UTIHelper stringFromPboardType:kPublicUrlNamePboardType]; 537 } else if (dataFlavor.EqualsLiteral(kRTFMime)) { 538 type = [UTIHelper stringFromPboardType:NSPasteboardTypeRTF]; 539 } else if (dataFlavor.EqualsLiteral(kCustomTypesMime)) { 540 type = [UTIHelper stringFromPboardType:kMozCustomTypesPboardType]; 541 } 542 543 NSString* availableType = 544 [globalDragPboard availableTypeFromArray:[NSArray arrayWithObjects:(id)type, nil]]; 545 if (availableType && IsValidType(availableType, allowFileURL)) { 546 *_retval = true; 547 } 548 549 return NS_OK; 550 551 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 552} 553 554NS_IMETHODIMP 555nsDragService::GetNumDropItems(uint32_t* aNumItems) { 556 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 557 558 *aNumItems = 0; 559 560 // first check to see if we have a number of items cached 561 if (mDataItems) { 562 mDataItems->GetLength(aNumItems); 563 return NS_OK; 564 } 565 566 NSArray* droppedItems = [globalDragPboard pasteboardItems]; 567 if (droppedItems) { 568 *aNumItems = [droppedItems count]; 569 } 570 571 return NS_OK; 572 573 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 574} 575 576NS_IMETHODIMP 577nsDragService::UpdateDragImage(nsINode* aImage, int32_t aImageX, int32_t aImageY) { 578 nsBaseDragService::UpdateDragImage(aImage, aImageX, aImageY); 579 mDragImageChanged = true; 580 return NS_OK; 581} 582 583void nsDragService::DragMovedWithView(NSDraggingSession* aSession, NSPoint aPoint) { 584 aPoint.y = nsCocoaUtils::FlippedScreenY(aPoint.y); 585 586 // XXX It feels like we should be using the backing scale factor at aPoint 587 // rather than the initial drag view, but I've seen no ill effects of this. 588 CGFloat scaleFactor = nsCocoaUtils::GetBackingScaleFactor(mNativeDragView); 589 LayoutDeviceIntPoint devPoint = nsCocoaUtils::CocoaPointsToDevPixels(aPoint, scaleFactor); 590 591 // If the image has changed, call enumerateDraggingItemsWithOptions to get 592 // the item being dragged and update its image. 593 if (mDragImageChanged && mNativeDragView) { 594 mDragImageChanged = false; 595 596 nsPresContext* pc = nullptr; 597 nsCOMPtr<nsIContent> content = do_QueryInterface(mImage); 598 if (content) { 599 pc = content->OwnerDoc()->GetPresContext(); 600 } 601 602 if (pc) { 603 void (^changeImageBlock)(NSDraggingItem*, NSInteger, BOOL*) = 604 ^(NSDraggingItem* draggingItem, NSInteger idx, BOOL* stop) { 605 // We never add more than one item right now, but check just in case. 606 if (idx > 0) { 607 return; 608 } 609 610 nsPoint pt = LayoutDevicePixel::ToAppUnits( 611 devPoint, pc->DeviceContext()->AppUnitsPerDevPixelAtUnitFullZoom()); 612 CSSIntPoint screenPoint = CSSIntPoint(nsPresContext::AppUnitsToIntCSSPixels(pt.x), 613 nsPresContext::AppUnitsToIntCSSPixels(pt.y)); 614 615 // Create a new image; if one isn't returned don't change the current one. 616 LayoutDeviceIntRect newRect; 617 NSImage* image = ConstructDragImage(mSourceNode, Nothing(), screenPoint, &newRect); 618 if (image) { 619 NSRect draggingRect = nsCocoaUtils::GeckoRectToCocoaRectDevPix(newRect, scaleFactor); 620 [draggingItem setDraggingFrame:draggingRect contents:image]; 621 } 622 }; 623 624 [aSession enumerateDraggingItemsWithOptions:NSDraggingItemEnumerationConcurrent 625 forView:nil 626 classes:[NSArray arrayWithObject:[NSPasteboardItem class]] 627 searchOptions:@{} 628 usingBlock:changeImageBlock]; 629 } 630 } 631 632 DragMoved(devPoint.x, devPoint.y); 633} 634 635NS_IMETHODIMP 636nsDragService::EndDragSession(bool aDoneDrag, uint32_t aKeyModifiers) { 637 NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NSRESULT; 638 639 if (mNativeDragView) { 640 [mNativeDragView release]; 641 mNativeDragView = nil; 642 } 643 if (mNativeDragEvent) { 644 [mNativeDragEvent release]; 645 mNativeDragEvent = nil; 646 } 647 648 mUserCancelled = gUserCancelledDrag; 649 650 nsresult rv = nsBaseDragService::EndDragSession(aDoneDrag, aKeyModifiers); 651 mDataItems = nullptr; 652 return rv; 653 654 NS_OBJC_END_TRY_ABORT_BLOCK_NSRESULT; 655} 656