Bug 944938 - Pass scroll events to the APZC tree manager on the async event thread. r=smichaud
authorMarkus Stange <mstange@themasta.com>
Sat, 07 Jun 2014 00:49:59 +0200
changeset 187354 2dd8b0d9fc4456a700f80c6a52898c28b87568f1
parent 187353 cde19559da0f6db9869a8586a55fe6d09cd248db
child 187355 ec4f84a6cc5a83b4d98485d4a01a3cad10d56152
push id26917
push userryanvm@gmail.com
push dateSat, 07 Jun 2014 18:13:47 +0000
treeherdermozilla-central@a2f0e0619332 [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 - Pass scroll events to the APZC tree manager on the async event thread. r=smichaud
layout/base/Units.h
widget/cocoa/nsChildView.mm
--- a/layout/base/Units.h
+++ b/layout/base/Units.h
@@ -196,16 +196,19 @@ struct LayerPixel {
  * On non-OMTC platforms this should be equivalent to LayerPixel units.
  * On OMTC platforms these may diverge from LayerPixel units temporarily,
  * while an asynchronous zoom is happening, but should eventually converge
  * back to LayerPixel units. Some variables (such as those representing
  * chrome UI element sizes) that are not subject to content zoom should
  * generally be represented in ScreenPixel units.
  */
 struct ScreenPixel {
+  static ScreenIntPoint FromUntyped(const nsIntPoint& aPoint) {
+    return ScreenIntPoint(aPoint.x, aPoint.y);
+  }
 };
 
 // Operators to apply ScaleFactors directly to Points, Rects, Sizes and Margins
 
 template<class src, class dst>
 gfx::PointTyped<dst> operator*(const gfx::PointTyped<src>& aPoint, const gfx::ScaleFactor<src, dst>& aScale) {
   return gfx::PointTyped<dst>(aPoint.x * aScale.scale,
                               aPoint.y * aScale.scale);
--- a/widget/cocoa/nsChildView.mm
+++ b/widget/cocoa/nsChildView.mm
@@ -87,16 +87,17 @@
 #include <dlfcn.h>
 
 #include <ApplicationServices/ApplicationServices.h>
 
 #include "GeckoProfiler.h"
 
 #include "nsIDOMWheelEvent.h"
 #include "mozilla/layers/APZCCallbackHelper.h"
+#include "InputData.h"
 
 using namespace mozilla;
 using namespace mozilla::layers;
 using namespace mozilla::gl;
 using namespace mozilla::widget;
 
 #undef DEBUG_UPDATE
 #undef INVALIDATE_DEBUGGING  // flash areas as they are invalidated
@@ -5089,16 +5090,21 @@ static int32_t RoundUp(double aDouble)
   }
   [self sendWheelStartOrStop:second forEvent:theEvent];
 }
 
 - (void)scrollWheel:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
 
+  if ([self apzctm]) {
+    // Disable main-thread scrolling completely when using APZC.
+    return;
+  }
+
   nsAutoRetainCocoaObject kungFuDeathGrip(self);
 
   ChildViewMouseTracker::MouseScrolled(theEvent);
 
   if ([self maybeRollup:theEvent]) {
     return;
   }
 
@@ -5168,16 +5174,107 @@ static int32_t RoundUp(double aDouble)
   }
 #endif // #ifdef __LP64__
 
   NS_OBJC_END_TRY_ABORT_BLOCK;
 }
 
 - (void)handleAsyncScrollEvent:(CGEventRef)cgEvent ofType:(CGEventType)type
 {
+  APZCTreeManager* apzctm = [self apzctm];
+  if (!apzctm) {
+    return;
+  }
+
+  CGPoint loc = CGEventGetLocation(cgEvent);
+  loc.y = nsCocoaUtils::FlippedScreenY(loc.y);
+  NSPoint locationInWindow = [[self window] convertScreenToBase:NSPointFromCGPoint(loc)];
+  ScreenIntPoint location = ScreenPixel::FromUntyped([self convertWindowCoordinates:locationInWindow]);
+
+  static NSTimeInterval sStartTime = [NSDate timeIntervalSinceReferenceDate];
+  static TimeStamp sStartTimeStamp = TimeStamp::Now();
+
+  if (type == kCGEventScrollWheel) {
+    NSEvent* event = [NSEvent eventWithCGEvent:cgEvent];
+    NSEventPhase phase = nsCocoaUtils::EventPhase(event);
+    NSEventPhase momentumPhase = nsCocoaUtils::EventMomentumPhase(event);
+    CGFloat pixelDeltaX = 0, pixelDeltaY = 0;
+    nsCocoaUtils::GetScrollingDeltas(event, &pixelDeltaX, &pixelDeltaY);
+    uint32_t eventTime = ([event timestamp] - sStartTime) * 1000;
+    TimeStamp eventTimeStamp = sStartTimeStamp +
+      TimeDuration::FromSeconds([event timestamp] - sStartTime);
+    NSPoint locationInWindowMoved = NSMakePoint(
+      locationInWindow.x + pixelDeltaX,
+      locationInWindow.y - pixelDeltaY);
+    ScreenIntPoint locationMoved = ScreenPixel::FromUntyped(
+      [self convertWindowCoordinates:locationInWindowMoved]);
+    ScreenPoint delta = ScreenPoint(locationMoved - location);
+    ScrollableLayerGuid guid;
+
+    // MayBegin and Cancelled are dispatched when the fingers start or stop
+    // touching the touchpad before any scrolling has occurred. These events
+    // can be used to control scrollbar visibility or interrupt scroll
+    // animations. They are only dispatched on 10.8 or later, and only by
+    // relatively modern devices.
+    if (phase == NSEventPhaseMayBegin) {
+      apzctm->ReceiveInputEvent(PanGestureInput(PanGestureInput::PANGESTURE_MAYSTART,
+                                                eventTime, eventTimeStamp, location,
+                                                ScreenPoint(0, 0), 0), &guid);
+      return;
+    }
+    if (phase == NSEventPhaseCancelled) {
+      apzctm->ReceiveInputEvent(PanGestureInput(PanGestureInput::PANGESTURE_CANCELLED,
+                                                eventTime, eventTimeStamp, location,
+                                                ScreenPoint(0, 0), 0), &guid);
+      return;
+    }
+
+    // Legacy scroll events are dispatched by devices that do not have a
+    // concept of a scroll gesture, for example by USB mice with
+    // traditional mouse wheels.
+    // For these kinds of scrolls, we want to surround every single scroll
+    // event with a PANGESTURE_START and a PANGESTURE_END event. The APZC
+    // needs to know that the real scroll gesture can end abruptly after any
+    // one of these events.
+    bool isLegacyScroll = (phase == NSEventPhaseNone &&
+      momentumPhase == NSEventPhaseNone && delta != ScreenPoint(0, 0));
+
+    if (phase == NSEventPhaseBegan || isLegacyScroll) {
+      apzctm->ReceiveInputEvent(PanGestureInput(PanGestureInput::PANGESTURE_START,
+                                                eventTime, eventTimeStamp, location,
+                                                ScreenPoint(0, 0), 0), &guid);
+    }
+    if (momentumPhase == NSEventPhaseNone && delta != ScreenPoint(0, 0)) {
+      apzctm->ReceiveInputEvent(PanGestureInput(PanGestureInput::PANGESTURE_PAN,
+                                                eventTime, eventTimeStamp, location,
+                                                delta, 0), &guid);
+    }
+    if (phase == NSEventPhaseEnded || isLegacyScroll) {
+      apzctm->ReceiveInputEvent(PanGestureInput(PanGestureInput::PANGESTURE_END,
+                                                eventTime, eventTimeStamp, location,
+                                                ScreenPoint(0, 0), 0), &guid);
+    }
+
+    // Any device that can dispatch momentum events supports all three momentum phases.
+    if (momentumPhase == NSEventPhaseBegan) {
+      apzctm->ReceiveInputEvent(PanGestureInput(PanGestureInput::PANGESTURE_MOMENTUMSTART,
+                                                eventTime, eventTimeStamp, location,
+                                                ScreenPoint(0, 0), 0), &guid);
+    }
+    if (momentumPhase == NSEventPhaseChanged && delta != ScreenPoint(0, 0)) {
+      apzctm->ReceiveInputEvent(PanGestureInput(PanGestureInput::PANGESTURE_MOMENTUMPAN,
+                                                eventTime, eventTimeStamp, location,
+                                                delta, 0), &guid);
+    }
+    if (momentumPhase == NSEventPhaseEnded) {
+      apzctm->ReceiveInputEvent(PanGestureInput(PanGestureInput::PANGESTURE_MOMENTUMEND,
+                                                eventTime, eventTimeStamp, location,
+                                                ScreenPoint(0, 0), 0), &guid);
+    }
+  }
 }
 
 -(NSMenu*)menuForEvent:(NSEvent*)theEvent
 {
   NS_OBJC_BEGIN_TRY_ABORT_BLOCK_NIL;
 
   if (!mGeckoChild || [self isPluginView])
     return nil;