Bug 1329997: Ensure that we don't initiate drag sessions if the system has already sent a mouseUp event. This prevents getting stuck in drag sessions. r=mstange a=gchang
authorStephen A Pohl <spohl.mozilla.bugs@gmail.com>
Wed, 15 Feb 2017 18:46:02 -0500
changeset 376252 9320a74084557f6584db59ecf64a0c187bf12741
parent 376251 b80dc6d41d1e466c04effa413e6ebb49b35ecdee
child 376253 9599f3612a604852c5daac4ba4bca8887c73cccc
push id6996
push userjlorenzo@mozilla.com
push dateMon, 06 Mar 2017 20:48:21 +0000
treeherdermozilla-beta@d89512dab048 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmstange, gchang
bugs1329997
milestone53.0a2
Bug 1329997: Ensure that we don't initiate drag sessions if the system has already sent a mouseUp event. This prevents getting stuck in drag sessions. r=mstange a=gchang
widget/cocoa/nsDragService.mm
--- a/widget/cocoa/nsDragService.mm
+++ b/widget/cocoa/nsDragService.mm
@@ -272,34 +272,39 @@ nsDragService::GetFilePath(NSPasteboardI
     }
   }
 
   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;
 
+  if (!gLastDragView) {
+    // gLastDragView is only set during -[ChildView mouseDragged:].
+    // InvokeDragSessionImpl is only called while Gecko processes a mouse move
+    // event. So if we get here with gLastDragView being null, that means that
+    // the mouse button has already been released, and mouseMoved is on the
+    // stack instead of mouseDragged. In that case we need to abort the drag
+    // because the OS won't know where to drop whatever's being dragged, and we
+    // might end up with a stuck drag & drop session.
+    return NS_ERROR_FAILURE;
+  }
+
   mDataItems = aTransferableArray;
 
   // 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;
 
   NSPasteboardItem* pbItem = [NSPasteboardItem new];
@@ -340,16 +345,19 @@ nsDragService::InvokeDragSessionImpl(nsI
   localDragRect.origin.x = draggingPoint.x;
   localDragRect.origin.y = draggingPoint.y - localDragRect.size.height;
 
   NSDraggingItem* dragItem =
     [[NSDraggingItem alloc] initWithPasteboardWriter:pbItem];
   [pbItem release];
   [dragItem setDraggingFrame:localDragRect contents:image];
 
+  nsBaseDragService::StartDragSession();
+  nsBaseDragService::OpenDragPopup();
+
   NSDraggingSession* draggingSession =
     [mNativeDragView beginDraggingSessionWithItems:
         [NSArray arrayWithObject:[dragItem autorelease]]
                                              event:mNativeDragEvent
                                             source:mNativeDragView];
   draggingSession.animatesToStartingPositionsOnCancelOrFail = YES;
 
   return NS_OK;