Bug 917385 - Move precise/imprecise input tracking down to widget thus making it more reliable with apz enabled. r=tabraldes, mbrubeck
authorJim Mathies <jmathies@mozilla.com>
Wed, 16 Oct 2013 12:14:55 -0500
changeset 164807 96905a5e44e6877e9afb5054e58d8ac25c31f051
parent 164806 8254d15a537dbaca36aa08d5718ed1428a8d5a79
child 164808 ef27681c50f7163f6f63ed3dff3332e0a56990e3
push id3066
push userakeybl@mozilla.com
push dateMon, 09 Dec 2013 19:58:46 +0000
treeherdermozilla-beta@a31a0dce83aa [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerstabraldes, mbrubeck
bugs917385
milestone27.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 917385 - Move precise/imprecise input tracking down to widget thus making it more reliable with apz enabled. r=tabraldes, mbrubeck
browser/metro/base/content/input.js
widget/windows/winrt/MetroInput.cpp
widget/windows/winrt/MetroInput.h
--- a/browser/metro/base/content/input.js
+++ b/browser/metro/base/content/input.js
@@ -1032,71 +1032,47 @@ var GestureModule = {
 };
 
 /**
  * Helper to track when the user is using a precise pointing device (pen/mouse)
  * versus an imprecise one (touch).
  */
 var InputSourceHelper = {
   isPrecise: false,
-  touchIsActive: false,
 
   init: function ish_init() {
-    window.addEventListener("mousemove", this, true);
-    window.addEventListener("mousedown", this, true);
-    window.addEventListener("touchstart", this, true);
-    window.addEventListener("touchend", this, true);
-    window.addEventListener("touchcancel", this, true);
+    Services.obs.addObserver(this, "metro_precise_input", false);
+    Services.obs.addObserver(this, "metro_imprecise_input", false);
   },
 
   _precise: function () {
     if (!this.isPrecise) {
       this.isPrecise = true;
       this._fire("MozPrecisePointer");
     }
   },
 
   _imprecise: function () {
     if (this.isPrecise) {
       this.isPrecise = false;
       this._fire("MozImprecisePointer");
     }
   },
 
-  handleEvent: function ish_handleEvent(aEvent) {
-    switch(aEvent.type) {
-      case "touchstart":
-        this._imprecise();
-        this.touchIsActive = true;
-        break;
-      case "touchend":
-      case "touchcancel":
-        this.touchIsActive = false;
+  observe: function BrowserUI_observe(aSubject, aTopic, aData) {
+    switch (aTopic) {
+      case "metro_precise_input":
+        this._precise();
         break;
-      default:
-        // Ignore mouse movement when touch is active. Prevents both mouse scrollbars
-        // and touch scrollbars from displaying at the same time. Also works around
-        // odd win8 bug involving an erant mousemove event after a touch sequence
-        // starts (bug 896017).
-        if (this.touchIsActive) {
-          return;
-        }
-
-        switch (aEvent.mozInputSource) {
-          case Ci.nsIDOMMouseEvent.MOZ_SOURCE_MOUSE:
-          case Ci.nsIDOMMouseEvent.MOZ_SOURCE_PEN:
-          case Ci.nsIDOMMouseEvent.MOZ_SOURCE_ERASER:
-          case Ci.nsIDOMMouseEvent.MOZ_SOURCE_CURSOR:
-            this._precise();
-            break;
-        }
+      case "metro_imprecise_input":
+        this._imprecise();
         break;
     }
   },
-  
+
   fireUpdate: function fireUpdate() {
     if (this.isPrecise) {
       this._fire("MozPrecisePointer");
     } else {
       this._fire("MozImprecisePointer");
     }
   },
 
--- a/widget/windows/winrt/MetroInput.cpp
+++ b/widget/windows/winrt/MetroInput.cpp
@@ -201,16 +201,17 @@ namespace {
 namespace mozilla {
 namespace widget {
 namespace winrt {
 
 MetroInput::MetroInput(MetroWidget* aWidget,
                        UI::Core::ICoreWindow* aWindow)
               : mWidget(aWidget),
                 mChromeHitTestCacheForTouch(false),
+                mCurrentInputLevel(LEVEL_IMPRECISE),
                 mWindow(aWindow)
 {
   LogFunction();
   NS_ASSERTION(aWidget, "Attempted to create MetroInput for null widget!");
   NS_ASSERTION(aWindow, "Attempted to create MetroInput for null window!");
 
   mTokenPointerPressed.value = 0;
   mTokenPointerReleased.value = 0;
@@ -235,16 +236,61 @@ MetroInput::MetroInput(MetroWidget* aWid
 }
 
 MetroInput::~MetroInput()
 {
   LogFunction();
   UnregisterInputEvents();
 }
 
+
+/**
+ * Tracks the current input level (precise/imprecise) and fires an observer
+ * when the mode changes.
+ */
+void
+MetroInput::UpdateInputLevel(InputPrecisionLevel aInputLevel)
+{
+  // ignore mouse input if we have active touch input.
+  if (aInputLevel == LEVEL_PRECISE && mTouches.Count() > 0) {
+    return;
+  }
+  if (mCurrentInputLevel != aInputLevel) {
+    mCurrentInputLevel = aInputLevel;
+    MetroUtils::FireObserver(mCurrentInputLevel == LEVEL_PRECISE ?
+                               "metro_precise_input" : "metro_imprecise_input");
+  }
+}
+
+/**
+ * Processes an IEdgeGestureEventArgs and returns the input source type
+ * for the event. Also updates input level via UpdateInputLevel.
+ */
+uint16_t
+MetroInput::ProcessInputTypeForGesture(UI::Input::IEdgeGestureEventArgs* aArgs)
+{
+  MOZ_ASSERT(aArgs);
+  UI::Input::EdgeGestureKind kind;
+  aArgs->get_Kind(&kind);
+  switch(kind) {
+    case UI::Input::EdgeGestureKind::EdgeGestureKind_Touch:
+      UpdateInputLevel(LEVEL_PRECISE);
+      return nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+    break;
+    case UI::Input::EdgeGestureKind::EdgeGestureKind_Keyboard:
+      return nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
+    break;
+    case UI::Input::EdgeGestureKind::EdgeGestureKind_Mouse:
+      UpdateInputLevel(LEVEL_IMPRECISE);
+      return nsIDOMMouseEvent::MOZ_SOURCE_MOUSE;
+    break;
+  }
+  return nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
+}
+
 /**
  * When the user swipes her/his finger in from the top of the screen,
  * we receive this event.
  *
  * @param sender the CoreDispatcher that fired this event
  * @param aArgs the event-specific args we use when processing this event
  * @returns S_OK
  */
@@ -258,18 +304,17 @@ MetroInput::OnEdgeGestureStarted(UI::Inp
   WidgetSimpleGestureEvent geckoEvent(true,
                                       NS_SIMPLE_GESTURE_EDGE_STARTED,
                                       mWidget.Get(),
                                       0,
                                       0.0);
   mModifierKeyState.Update();
   mModifierKeyState.InitInputEvent(geckoEvent);
   geckoEvent.time = ::GetMessageTime();
-
-  geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+  geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
 
   // Safe
   DispatchEventIgnoreStatus(&geckoEvent);
   return S_OK;
 }
 
 /**
  * This event can be received if the user swipes her/his finger back to
@@ -290,18 +335,17 @@ MetroInput::OnEdgeGestureCanceled(UI::In
   WidgetSimpleGestureEvent geckoEvent(true,
                                       NS_SIMPLE_GESTURE_EDGE_CANCELED,
                                       mWidget.Get(),
                                       0,
                                       0.0);
   mModifierKeyState.Update();
   mModifierKeyState.InitInputEvent(geckoEvent);
   geckoEvent.time = ::GetMessageTime();
-
-  geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+  geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
 
   // Safe
   DispatchEventIgnoreStatus(&geckoEvent);
   return S_OK;
 }
 
 /**
  * This event is received if the user presses ctrl+Z or lifts her/his
@@ -321,25 +365,17 @@ MetroInput::OnEdgeGestureCompleted(UI::I
   WidgetSimpleGestureEvent geckoEvent(true,
                                       NS_SIMPLE_GESTURE_EDGE_COMPLETED,
                                       mWidget.Get(),
                                       0,
                                       0.0);
   mModifierKeyState.Update();
   mModifierKeyState.InitInputEvent(geckoEvent);
   geckoEvent.time = ::GetMessageTime();
-
-  UI::Input::EdgeGestureKind value;
-  aArgs->get_Kind(&value);
-
-  if (value == UI::Input::EdgeGestureKind::EdgeGestureKind_Keyboard) {
-    geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_KEYBOARD;
-  } else {
-    geckoEvent.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
-  }
+  geckoEvent.inputSource = ProcessInputTypeForGesture(aArgs);
 
   // Safe
   DispatchEventIgnoreStatus(&geckoEvent);
   return S_OK;
 }
 
 /**
  * This helper function is used by our processing of PointerPressed,
@@ -386,16 +422,17 @@ MetroInput::OnPointerNonTouch(UI::Input:
       event->button = WidgetMouseEvent::buttonType::eMiddleButton;
       event->message = NS_MOUSE_BUTTON_UP;
       break;
     case UI::Input::PointerUpdateKind::PointerUpdateKind_RightButtonReleased:
       event->button = WidgetMouseEvent::buttonType::eRightButton;
       event->message = NS_MOUSE_BUTTON_UP;
       break;
   }
+  UpdateInputLevel(LEVEL_PRECISE);
   InitGeckoMouseEventFromPointerPoint(event, aPoint);
   DispatchAsyncEventIgnoreStatus(event);
 }
 
 void
 MetroInput::InitTouchEventTouchList(WidgetTouchEvent* aEvent)
 {
   MOZ_ASSERT(aEvent);
@@ -425,16 +462,18 @@ MetroInput::OnPointerPressed(UI::Core::I
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     OnPointerNonTouch(currentPoint.Get());
     mGestureRecognizer->ProcessDownEvent(currentPoint.Get());
     return S_OK;
   }
 
   // This is touch input.
+  UpdateInputLevel(LEVEL_IMPRECISE);
+
   // Create the new touch point and add it to our event.
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
   nsRefPtr<Touch> touch = CreateDOMTouch(currentPoint.Get());
   touch->mChanged = true;
   mTouches.Put(pointerId, touch);
 
   WidgetTouchEvent* touchEvent =
@@ -512,16 +551,18 @@ MetroInput::OnPointerMoved(UI::Core::ICo
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     OnPointerNonTouch(currentPoint.Get());
     AddPointerMoveDataToRecognizer(aArgs);
     return S_OK;
   }
 
   // This is touch input.
+  UpdateInputLevel(LEVEL_IMPRECISE);
+
   // Get the touch associated with this touch point.
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
   nsRefPtr<Touch> touch = mTouches.Get(pointerId);
 
   // Some old drivers cause us to receive a PointerMoved event for a touchId
   // after we've already received a PointerReleased event for that touchId.
   // To work around those busted drivers, we simply ignore TouchMoved events
@@ -609,16 +650,18 @@ MetroInput::OnPointerReleased(UI::Core::
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     OnPointerNonTouch(currentPoint.Get());
     mGestureRecognizer->ProcessUpEvent(currentPoint.Get());
     return S_OK;
   }
 
   // This is touch input.
+  UpdateInputLevel(LEVEL_IMPRECISE);
+
   // Get the touch associated with this touch point.
   uint32_t pointerId;
   currentPoint->get_PointerId(&pointerId);
   nsRefPtr<Touch> touch = mTouches.Get(pointerId);
 
   // Purge any pending moves for this pointer
   if (touch->mChanged) {
     WidgetTouchEvent* touchEvent =
@@ -743,19 +786,21 @@ MetroInput::OnPointerEntered(UI::Core::I
   device->get_PointerDeviceType(&deviceType);
 
   // We only dispatch mouseenter and mouseexit events for mouse and pen input.
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     WidgetMouseEvent* event =
       new WidgetMouseEvent(true, NS_MOUSE_ENTER, mWidget.Get(),
                            WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
+    UpdateInputLevel(LEVEL_PRECISE);
     InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get());
     DispatchAsyncEventIgnoreStatus(event);
   }
+  UpdateInputLevel(LEVEL_IMPRECISE);
   return S_OK;
 }
 
 // This event is raised when a precise pointer leaves the bounding box of
 // our window.  For touch input, this will be raised before the
 // PointerReleased event.
 HRESULT
 MetroInput::OnPointerExited(UI::Core::ICoreWindow* aSender,
@@ -774,19 +819,21 @@ MetroInput::OnPointerExited(UI::Core::IC
   device->get_PointerDeviceType(&deviceType);
 
   // We only dispatch mouseenter and mouseexit events for mouse and pen input.
   if (deviceType !=
           Devices::Input::PointerDeviceType::PointerDeviceType_Touch) {
     WidgetMouseEvent* event =
       new WidgetMouseEvent(true, NS_MOUSE_EXIT, mWidget.Get(),
                            WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal);
+    UpdateInputLevel(LEVEL_PRECISE);
     InitGeckoMouseEventFromPointerPoint(event, currentPoint.Get());
     DispatchAsyncEventIgnoreStatus(event);
   }
+  UpdateInputLevel(LEVEL_IMPRECISE);
   return S_OK;
 }
 
 /**
  * This helper function is called by our processing of "manipulation events".
  * Manipulation events are how Windows sends us information about swipes,
  * magnification gestures, and rotation gestures.
  *
--- a/widget/windows/winrt/MetroInput.h
+++ b/widget/windows/winrt/MetroInput.h
@@ -150,16 +150,24 @@ public:
 
 private:
   Microsoft::WRL::ComPtr<ICoreWindow> mWindow;
   Microsoft::WRL::ComPtr<MetroWidget> mWidget;
   Microsoft::WRL::ComPtr<IGestureRecognizer> mGestureRecognizer;
 
   ModifierKeyState mModifierKeyState;
 
+  // Tracking input level
+  enum InputPrecisionLevel {
+    LEVEL_PRECISE,
+    LEVEL_IMPRECISE
+  };
+  InputPrecisionLevel mCurrentInputLevel;
+  void UpdateInputLevel(InputPrecisionLevel aInputLevel);
+
   // Initialization/Uninitialization helpers
   void RegisterInputEvents();
   void UnregisterInputEvents();
 
   // Hit testing for chrome content
   bool mChromeHitTestCacheForTouch;
   bool HitTestChrome(const LayoutDeviceIntPoint& pt);
 
@@ -169,16 +177,17 @@ private:
   void OnPointerNonTouch(IPointerPoint* aPoint);
   void AddPointerMoveDataToRecognizer(IPointerEventArgs* aArgs);
   void InitGeckoMouseEventFromPointerPoint(WidgetMouseEvent* aEvent,
                                            IPointerPoint* aPoint);
   void ProcessManipulationDelta(ManipulationDelta const& aDelta,
                                 Point const& aPosition,
                                 uint32_t aMagEventType,
                                 uint32_t aRotEventType);
+  uint16_t ProcessInputTypeForGesture(IEdgeGestureEventArgs* aArgs);
 
   // The W3C spec states that "whether preventDefault has been called" should
   // be tracked on a per-touchpoint basis, but it also states that touchstart
   // and touchmove events can contain multiple changed points.  At the time of
   // this writing, W3C touch events are in the process of being abandoned in
   // favor of W3C pointer events, so it is unlikely that this ambiguity will
   // be resolved.  Additionally, nsPresShell tracks "whether preventDefault
   // has been called" on a per-touch-session basis.  We will follow a similar