Bug 677279 - Send momentum scroll events to the location of the last non-momentum scroll event. r=smichaud
authorMarkus Stange <mstange@themasta.com>
Thu, 18 Aug 2011 00:30:52 +0200
changeset 75442 7646eade98425467e7494db3b84c5c8391662cb3
parent 75441 4e2365ec6145d5bc246c0d07f210921be2b03641
child 75443 35e465c9d470b54d0c7dd7111423bd453ac9b734
push id2
push userbsmedberg@mozilla.com
push dateFri, 19 Aug 2011 14:38:13 +0000
reviewerssmichaud
bugs677279
milestone9.0a1
Bug 677279 - Send momentum scroll events to the location of the last non-momentum scroll event. r=smichaud
widget/src/cocoa/nsChildView.h
widget/src/cocoa/nsChildView.mm
widget/src/cocoa/nsCocoaUtils.h
widget/src/cocoa/nsCocoaUtils.mm
--- a/widget/src/cocoa/nsChildView.h
+++ b/widget/src/cocoa/nsChildView.h
@@ -352,29 +352,31 @@ typedef NSInteger NSEventGestureAxis;
 #endif
 @end
 
 class ChildViewMouseTracker {
 
 public:
 
   static void MouseMoved(NSEvent* aEvent);
+  static void MouseScrolled(NSEvent* aEvent);
   static void OnDestroyView(ChildView* aView);
   static void OnDestroyWindow(NSWindow* aWindow);
   static BOOL WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
                                  ChildView* aView, BOOL isClickThrough = NO);
   static void MouseExitedWindow(NSEvent* aEvent);
   static void MouseEnteredWindow(NSEvent* aEvent);
   static void ReEvaluateMouseEnterState(NSEvent* aEvent = nil);
   static void ResendLastMouseMoveEvent();
   static ChildView* ViewForEvent(NSEvent* aEvent);
 
   static ChildView* sLastMouseEventView;
   static NSEvent* sLastMouseMoveEvent;
   static NSWindow* sWindowUnderMouse;
+  static NSPoint sLastScrollEventScreenLocation;
 };
 
 //-------------------------------------------------------------------------
 //
 // nsChildView
 //
 //-------------------------------------------------------------------------
 
--- a/widget/src/cocoa/nsChildView.mm
+++ b/widget/src/cocoa/nsChildView.mm
@@ -123,16 +123,17 @@ extern PRBool gConsumeRollupEvent;
 
 PRBool gChildViewMethodsSwizzled = PR_FALSE;
 
 extern nsISupportsArray *gDraggedTransferables;
 
 ChildView* ChildViewMouseTracker::sLastMouseEventView = nil;
 NSEvent* ChildViewMouseTracker::sLastMouseMoveEvent = nil;
 NSWindow* ChildViewMouseTracker::sWindowUnderMouse = nil;
+NSPoint ChildViewMouseTracker::sLastScrollEventScreenLocation = NSZeroPoint;
 
 #ifdef INVALIDATE_DEBUGGING
 static void blinkRect(Rect* r);
 static void blinkRgn(RgnHandle rgn);
 #endif
 
 nsIRollupListener * gRollupListener = nsnull;
 nsIMenuRollup     * gMenuRollup = nsnull;
@@ -3686,18 +3687,17 @@ NSEvent* gLastDragMouseDownEvent = nil;
   }
 
   BOOL hasPixels = (scrollDeltaPixels != 0);
 
   if (!hasPixels && scrollDelta == 0)
     // No sense in firing off a Gecko event.
      return;
 
-  BOOL isMomentumScroll = [theEvent respondsToSelector:@selector(_scrollPhase)] &&
-                          [theEvent _scrollPhase] != 0;
+  BOOL isMomentumScroll = nsCocoaUtils::IsMomentumScrollEvent(theEvent);
 
   if (scrollDelta != 0) {
     // Send the line scroll event.
     nsMouseScrollEvent geckoEvent(PR_TRUE, NS_MOUSE_SCROLL, nsnull);
     [self convertCocoaMouseEvent:theEvent toGeckoEvent:&geckoEvent];
     geckoEvent.scrollFlags |= inAxis;
 
     if (hasPixels)
@@ -3809,36 +3809,29 @@ NSEvent* gLastDragMouseDownEvent = nil;
 #endif // #ifdef __LP64__
   }
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 -(void)scrollWheel:(NSEvent*)theEvent
 {
-  NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
-
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
 
+  ChildViewMouseTracker::MouseScrolled(theEvent);
+
   if ([self maybeRollup:theEvent])
     return;
 
-  if (!mGeckoChild)
-    return;
-
   // It's possible for a single NSScrollWheel event to carry both useful
   // deltaX and deltaY, for example, when the "wheel" is a trackpad.
   // NSMouseScrollEvent can only carry one axis at a time, so the system
   // event will be split into two Gecko events if necessary.
   [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsVertical];
-  if (!mGeckoChild)
-    return;
   [self scrollWheel:theEvent forAxis:nsMouseScrollEvent::kIsHorizontal];
-
-  NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 -(NSMenu*)menuForEvent:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if (!mGeckoChild || [self isPluginView])
     return nil;
@@ -4962,16 +4955,25 @@ ChildViewMouseTracker::MouseMoved(NSEven
   MouseEnteredWindow(aEvent);
   [sLastMouseEventView handleMouseMoved:aEvent];
   if (sLastMouseMoveEvent != aEvent) {
     [sLastMouseMoveEvent release];
     sLastMouseMoveEvent = [aEvent retain];
   }
 }
 
+void
+ChildViewMouseTracker::MouseScrolled(NSEvent* aEvent)
+{
+  if (!nsCocoaUtils::IsMomentumScrollEvent(aEvent)) {
+    // Store the position so we can pin future momentum scroll events.
+    sLastScrollEventScreenLocation = nsCocoaUtils::ScreenLocationForEvent(aEvent);
+  }
+}
+
 ChildView*
 ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
 {
   NSWindow* window = sWindowUnderMouse;
   if (!window)
     return nil;
 
   NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
--- a/widget/src/cocoa/nsCocoaUtils.h
+++ b/widget/src/cocoa/nsCocoaUtils.h
@@ -136,16 +136,18 @@ class nsCocoaUtils
   static BOOL IsEventOverWindow(NSEvent* anEvent, NSWindow* aWindow);
 
   // Events are set up so that their coordinates refer to the window to which they
   // were originally sent. If we reroute the event somewhere else, we'll have
   // to get the window coordinates this way. Do not call this unless the window
   // the event was originally targeted at is still alive!
   static NSPoint EventLocationForWindow(NSEvent* anEvent, NSWindow* aWindow);
 
+  static BOOL IsMomentumScrollEvent(NSEvent* aEvent);
+
   // Hides the Menu bar and the Dock. Multiple hide/show requests can be nested.
   static void HideOSChromeOnScreen(PRBool aShouldHide, NSScreen* aScreen);
 
   static nsIWidget* GetHiddenWindowWidget();
 
   static void PrepareForNativeAppModalDialog();
   static void CleanUpAfterNativeAppModalDialog();
 
--- a/widget/src/cocoa/nsCocoaUtils.mm
+++ b/widget/src/cocoa/nsCocoaUtils.mm
@@ -35,16 +35,17 @@
  * and other provisions required by the GPL or the LGPL. If you do not delete
  * the provisions above, a recipient may use your version of this file under
  * the terms of any one of the MPL, the GPL or the LGPL.
  *
  * ***** END LICENSE BLOCK ***** */
 
 #include "gfxImageSurface.h"
 #include "nsCocoaUtils.h"
+#include "nsChildView.h"
 #include "nsMenuBarX.h"
 #include "nsCocoaWindow.h"
 #include "nsCOMPtr.h"
 #include "nsIInterfaceRequestorUtils.h"
 #include "nsIAppShellService.h"
 #include "nsIXULWindow.h"
 #include "nsIBaseWindow.h"
 #include "nsIServiceManager.h"
@@ -100,16 +101,21 @@ nsIntRect nsCocoaUtils::CocoaRectToGecko
 NSPoint nsCocoaUtils::ScreenLocationForEvent(NSEvent* anEvent)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   // Don't trust mouse locations of mouse move events, see bug 443178.
   if (!anEvent || [anEvent type] == NSMouseMoved)
     return [NSEvent mouseLocation];
 
+  // Pin momentum scroll events to the location of the last user-controlled
+  // scroll event.
+  if (IsMomentumScrollEvent(anEvent))
+    return ChildViewMouseTracker::sLastScrollEventScreenLocation;
+
   return [[anEvent window] convertBaseToScreen:[anEvent locationInWindow]];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
 }
 
 BOOL nsCocoaUtils::IsEventOverWindow(NSEvent* anEvent, NSWindow* aWindow)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
@@ -123,16 +129,23 @@ NSPoint nsCocoaUtils::EventLocationForWi
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_RETURN;
 
   return [aWindow convertScreenToBase:ScreenLocationForEvent(anEvent)];
 
   NS_OBJC_END_TRY_ABORT_BLOCK_RETURN(NSMakePoint(0.0, 0.0));
 }
 
+BOOL nsCocoaUtils::IsMomentumScrollEvent(NSEvent* aEvent)
+{
+  return [aEvent type] == NSScrollWheel &&
+         [aEvent respondsToSelector:@selector(_scrollPhase)] &&
+         [aEvent _scrollPhase] != 0;
+}
+
 void nsCocoaUtils::HideOSChromeOnScreen(PRBool aShouldHide, NSScreen* aScreen)
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
   // Keep track of how many hiding requests have been made, so that they can
   // be nested.
   static int sMenuBarHiddenCount = 0, sDockHiddenCount = 0;