Bug 944938 - Add a 10.6 compatibility wrapper for hasPreciseScrollingDeltas and scrollingDeltaX/Y. r=smichaud
authorMarkus Stange <mstange@themasta.com>
Sat, 07 Jun 2014 00:49:59 +0200
changeset 206575 cde19559da0f6db9869a8586a55fe6d09cd248db
parent 206574 7eda6c9105c25dfa11e296ba6f130a691842c93b
child 206576 2dd8b0d9fc4456a700f80c6a52898c28b87568f1
push id3741
push userasasaki@mozilla.com
push dateMon, 21 Jul 2014 20:25:18 +0000
treeherdermozilla-beta@4d6f46f5af68 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerssmichaud
bugs944938
milestone32.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 944938 - Add a 10.6 compatibility wrapper for hasPreciseScrollingDeltas and scrollingDeltaX/Y. r=smichaud
widget/cocoa/nsChildView.h
widget/cocoa/nsChildView.mm
widget/cocoa/nsCocoaUtils.h
widget/cocoa/nsCocoaUtils.mm
--- a/widget/cocoa/nsChildView.h
+++ b/widget/cocoa/nsChildView.h
@@ -136,28 +136,16 @@ class APZCTreeManager;
 // disappear, or to reappear (say if the window's style changes).  If
 // 'redisplay' is true, the entire titlebar (the window's top 22 pixels) is
 // marked as needing redisplay.  This method has been present in the same
 // format since at least OS X 10.5.
 - (void)_tileTitlebarAndRedisplay:(BOOL)redisplay;
 
 @end
 
-// Support for pixel scroll deltas, not part of NSEvent.h
-// See http://lists.apple.com/archives/cocoa-dev/2007/Feb/msg00050.html
-@interface NSEvent (DeviceDelta)
-// Leopard and SnowLeopard
-- (CGFloat)deviceDeltaX;
-- (CGFloat)deviceDeltaY;
-// Lion and above
-- (CGFloat)scrollingDeltaX;
-- (CGFloat)scrollingDeltaY;
-- (BOOL)hasPreciseScrollingDeltas;
-@end
-
 #if !defined(MAC_OS_X_VERSION_10_6) || \
 MAC_OS_X_VERSION_MAX_ALLOWED < MAC_OS_X_VERSION_10_6
 @interface NSEvent (SnowLeopardEventFeatures)
 + (NSUInteger)pressedMouseButtons;
 + (NSUInteger)modifierFlags;
 @end
 #endif
 
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -5119,29 +5119,24 @@ static int32_t RoundUp(double aDouble)
   }
 
   WidgetWheelEvent wheelEvent(true, NS_WHEEL_WHEEL, mGeckoChild);
   [self convertCocoaMouseWheelEvent:theEvent toGeckoEvent:&wheelEvent];
 
   wheelEvent.lineOrPageDeltaX = RoundUp(-[theEvent deltaX]);
   wheelEvent.lineOrPageDeltaY = RoundUp(-[theEvent deltaY]);
 
+  // wheelEvent.deltaMode was set by convertCocoaMouseWheelEvent:toGeckoEvent:
+  // and depends on whether the current scrolling device supports pixel deltas.
   if (wheelEvent.deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
-    // Some scrolling devices supports pixel scrolling, e.g. a Macbook
-    // touchpad or a Mighty Mouse. On those devices, [theEvent deviceDeltaX/Y]
-    // contains the amount of pixels to scroll. Since Lion this has changed
-    // to [theEvent scrollingDeltaX/Y].
     double scale = mGeckoChild->BackingScaleFactor();
-    if ([theEvent respondsToSelector:@selector(scrollingDeltaX)]) {
-      wheelEvent.deltaX = -[theEvent scrollingDeltaX] * scale;
-      wheelEvent.deltaY = -[theEvent scrollingDeltaY] * scale;
-    } else {
-      wheelEvent.deltaX = -[theEvent deviceDeltaX] * scale;
-      wheelEvent.deltaY = -[theEvent deviceDeltaY] * scale;
-    }
+    CGFloat pixelDeltaX = 0, pixelDeltaY = 0;
+    nsCocoaUtils::GetScrollingDeltas(theEvent, &pixelDeltaX, &pixelDeltaY);
+    wheelEvent.deltaX = -pixelDeltaX * scale;
+    wheelEvent.deltaY = -pixelDeltaY * scale;
   } else {
     wheelEvent.deltaX = -[theEvent deltaX];
     wheelEvent.deltaY = -[theEvent deltaY];
   }
 
   // TODO: We should not set deltaZ for now because we're not sure if we should
   //       revert the sign.
   // wheelEvent.deltaZ = [theEvent deltaZ];
@@ -5231,32 +5226,22 @@ static int32_t RoundUp(double aDouble)
 
   NS_OBJC_END_TRY_ABORT_BLOCK_NIL;
 }
 
 - (void) convertCocoaMouseWheelEvent:(NSEvent*)aMouseEvent
                         toGeckoEvent:(WidgetWheelEvent*)outWheelEvent
 {
   [self convertCocoaMouseEvent:aMouseEvent toGeckoEvent:outWheelEvent];
-  outWheelEvent->deltaMode =
-    Preferences::GetBool("mousewheel.enable_pixel_scrolling", true) ?
-      nsIDOMWheelEvent::DOM_DELTA_PIXEL : nsIDOMWheelEvent::DOM_DELTA_LINE;
-
-  // Calling deviceDeltaX or deviceDeltaY on theEvent will trigger a Cocoa
-  // assertion and an Objective-C NSInternalInconsistencyException if the
-  // underlying "Carbon" event doesn't contain pixel scrolling information.
-  // For these events, carbonEventKind is kEventMouseWheelMoved instead of
-  // kEventMouseScroll.
-  if (outWheelEvent->deltaMode == nsIDOMWheelEvent::DOM_DELTA_PIXEL) {
-    EventRef theCarbonEvent = [aMouseEvent _eventRef];
-    UInt32 carbonEventKind = theCarbonEvent ? ::GetEventKind(theCarbonEvent) : 0;
-    if (carbonEventKind != kEventMouseScroll) {
-      outWheelEvent->deltaMode = nsIDOMWheelEvent::DOM_DELTA_LINE;
-    }
-  }
+
+  bool usePreciseDeltas = nsCocoaUtils::HasPreciseScrollingDeltas(aMouseEvent) &&
+    Preferences::GetBool("mousewheel.enable_pixel_scrolling", true);
+
+  outWheelEvent->deltaMode = usePreciseDeltas ? nsIDOMWheelEvent::DOM_DELTA_PIXEL
+                                              : nsIDOMWheelEvent::DOM_DELTA_LINE;
   outWheelEvent->isMomentum = nsCocoaUtils::IsMomentumScrollEvent(aMouseEvent);
 }
 
 - (void) convertCocoaMouseEvent:(NSEvent*)aMouseEvent
                    toGeckoEvent:(WidgetInputEvent*)outGeckoEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
--- a/widget/cocoa/nsCocoaUtils.h
+++ b/widget/cocoa/nsCocoaUtils.h
@@ -228,21 +228,25 @@ public:
   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);
 
-  // Compatibility wrappers for the -[NSEvent phase] and -[NSEvent momentumPhase]
-  // APIs that became availaible starting with the 10.7 SDK.
+  // Compatibility wrappers for the -[NSEvent phase], -[NSEvent momentumPhase],
+  // -[NSEvent hasPreciseScrollingDeltas] and -[NSEvent scrollingDeltaX/Y] APIs
+  // that became availaible starting with the 10.7 SDK.
+  // All of these can be removed once we drop support for 10.6.
   static NSEventPhase EventPhase(NSEvent* aEvent);
   static NSEventPhase EventMomentumPhase(NSEvent* aEvent);
   static BOOL IsMomentumScrollEvent(NSEvent* aEvent);
+  static BOOL HasPreciseScrollingDeltas(NSEvent* aEvent);
+  static void GetScrollingDeltas(NSEvent* aEvent, CGFloat* aOutDeltaX, CGFloat* aOutDeltaY);
 
   // Hides the Menu bar and the Dock. Multiple hide/show requests can be nested.
   static void HideOSChromeOnScreen(bool aShouldHide, NSScreen* aScreen);
 
   static nsIWidget* GetHiddenWindowWidget();
 
   static void PrepareForNativeAppModalDialog();
   static void CleanUpAfterNativeAppModalDialog();
--- a/widget/cocoa/nsCocoaUtils.mm
+++ b/widget/cocoa/nsCocoaUtils.mm
@@ -170,16 +170,69 @@ NSEventPhase nsCocoaUtils::EventMomentum
 }
 
 BOOL nsCocoaUtils::IsMomentumScrollEvent(NSEvent* aEvent)
 {
   return [aEvent type] == NSScrollWheel &&
     EventMomentumPhase(aEvent) != NSEventPhaseNone;
 }
 
+@interface NSEvent (HasPreciseScrollingDeltas)
+// 10.7 and above
+- (BOOL)hasPreciseScrollingDeltas;
+// For 10.6 and below, see the comment in nsChildView.h about _eventRef
+- (EventRef)_eventRef;
+@end
+
+BOOL nsCocoaUtils::HasPreciseScrollingDeltas(NSEvent* aEvent)
+{
+  if ([aEvent respondsToSelector:@selector(hasPreciseScrollingDeltas)]) {
+    return [aEvent hasPreciseScrollingDeltas];
+  }
+
+  // For events that don't contain pixel scrolling information, the event
+  // kind of their underlaying carbon event is kEventMouseWheelMoved instead
+  // of kEventMouseScroll.
+  EventRef carbonEvent = [aEvent _eventRef];
+  return carbonEvent && ::GetEventKind(carbonEvent) == kEventMouseScroll;
+}
+
+@interface NSEvent (ScrollingDeltas)
+// 10.6 and below
+- (CGFloat)deviceDeltaX;
+- (CGFloat)deviceDeltaY;
+// 10.7 and above
+- (CGFloat)scrollingDeltaX;
+- (CGFloat)scrollingDeltaY;
+@end
+
+void nsCocoaUtils::GetScrollingDeltas(NSEvent* aEvent, CGFloat* aOutDeltaX, CGFloat* aOutDeltaY)
+{
+  if ([aEvent respondsToSelector:@selector(scrollingDeltaX)]) {
+    *aOutDeltaX = [aEvent scrollingDeltaX];
+    *aOutDeltaY = [aEvent scrollingDeltaY];
+    return;
+  }
+  if ([aEvent respondsToSelector:@selector(deviceDeltaX)] &&
+      HasPreciseScrollingDeltas(aEvent)) {
+    // Calling deviceDeltaX/Y on those events that do not contain pixel
+    // scrolling information triggers a Cocoa assertion and an
+    // Objective-C NSInternalInconsistencyException.
+    *aOutDeltaX = [aEvent deviceDeltaX];
+    *aOutDeltaY = [aEvent deviceDeltaY];
+    return;
+  }
+
+  // This is only hit pre-10.7 when we are called on a scroll event that does
+  // not contain pixel scrolling information.
+  CGFloat lineDeltaPixels = 12;
+  *aOutDeltaX = [aEvent deltaX] * lineDeltaPixels;
+  *aOutDeltaY = [aEvent deltaY] * lineDeltaPixels;
+}
+
 void nsCocoaUtils::HideOSChromeOnScreen(bool 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;