Bug 782729 - Clarify pointer lock pointer-centering logic, and cancel centering mouse moves. r=smaug
authorChris Pearce <cpearce@mozilla.com>
Tue, 25 Sep 2012 11:44:03 +1200
changeset 108066 a88b3cd6f28d86b61aaa46a65d039afd2fa69948
parent 108065 1f87f13f8da46fa36b82fcf9d9c7ac998560b802
child 108067 cbd181aafd5ffadd602e5e0b586862d9b9cfec12
push id82
push usershu@rfrn.org
push dateFri, 05 Oct 2012 13:20:22 +0000
reviewerssmaug
bugs782729
milestone18.0a1
Bug 782729 - Clarify pointer lock pointer-centering logic, and cancel centering mouse moves. r=smaug
content/events/src/nsEventStateManager.cpp
content/events/src/nsEventStateManager.h
--- a/content/events/src/nsEventStateManager.cpp
+++ b/content/events/src/nsEventStateManager.cpp
@@ -117,16 +117,17 @@ static uint32_t sESMInstanceCount = 0;
 static int32_t sChromeAccessModifier = 0, sContentAccessModifier = 0;
 int32_t nsEventStateManager::sUserInputEventDepth = 0;
 bool nsEventStateManager::sNormalLMouseEventInProcess = false;
 nsEventStateManager* nsEventStateManager::sActiveESM = nullptr;
 nsIDocument* nsEventStateManager::sMouseOverDocument = nullptr;
 nsWeakFrame nsEventStateManager::sLastDragOverFrame = nullptr;
 nsIntPoint nsEventStateManager::sLastRefPoint = nsIntPoint(0,0);
 nsIntPoint nsEventStateManager::sLastScreenPoint = nsIntPoint(0,0);
+nsIntPoint nsEventStateManager::sSynthCenteringPoint = nsIntPoint(-1,-1);
 nsIntPoint nsEventStateManager::sLastClientPoint = nsIntPoint(0,0);
 bool nsEventStateManager::sIsPointerLocked = false;
 // Reference to the pointer locked element.
 nsWeakPtr nsEventStateManager::sPointerLockedElement;
 // Reference to the document which requested pointer lock.
 nsWeakPtr nsEventStateManager::sPointerLockedDoc;
 nsCOMPtr<nsIContent> nsEventStateManager::sDragOverContent = nullptr;
 
@@ -4127,29 +4128,46 @@ nsEventStateManager::GenerateMouseEnterE
     return;
 
   // Hold onto old target content through the event and reset after.
   nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
 
   switch(aEvent->message) {
   case NS_MOUSE_MOVE:
     {
+      // Mouse movement is reported on the MouseEvent.movement{X,Y} fields.
+      // Movement is calculated in nsDOMUIEvent::GetMovementPoint() as:
+      //   previous_mousemove_refPoint - current_mousemove_refPoint.
       if (sIsPointerLocked && aEvent->widget) {
-        // Perform mouse lock by recentering the mouse directly, and storing
-        // the refpoints so movement deltas can be calculated.
+        // The pointer is locked. If the pointer is not located at the center of
+        // the window, dispatch a synthetic mousemove to return the pointer there.
+        // Doing this between "real" pointer moves gives the impression that the
+        // (locked) pointer can continue moving and won't stop at the screen
+        // boundary. We cancel the synthetic event so that we don't end up
+        // dispatching the centering move event to content.
         nsIntPoint center = GetWindowInnerRectCenter(mDocument->GetWindow(),
                                                      aEvent->widget,
                                                      mPresContext);
         aEvent->lastRefPoint = center;
         if (aEvent->refPoint != center) {
-          // This mouse move doesn't finish at the center of the widget,
-          // dispatch a synthetic mouse move to return the mouse back to
-          // the center.
+          // Mouse move doesn't finish at the center of the window. Dispatch a
+          // synthetic native mouse event to move the pointer back to the center
+          // of the window, to faciliate more movement. But first, record that
+          // we've dispatched a synthetic mouse movement, so we can cancel it
+          // in the other branch here.
+          sSynthCenteringPoint = center;
           aEvent->widget->SynthesizeNativeMouseMove(
             center + aEvent->widget->WidgetToScreenOffset());
+        } else if (aEvent->refPoint == sSynthCenteringPoint) {
+          // This is the "synthetic native" event we dispatched to re-center the
+          // pointer. Cancel it so we don't expose the centering move to content.
+          aEvent->flags |= NS_EVENT_FLAG_STOP_DISPATCH;
+          // Clear sSynthCenteringPoint so we don't cancel other events
+          // targeted at the center.
+          sSynthCenteringPoint = nsIntPoint(-1,-1);
         }
       } else {
         aEvent->lastRefPoint = sLastRefPoint;
       }
 
       // Update the last known refPoint with the current refPoint.
       sLastRefPoint = aEvent->refPoint;
 
--- a/content/events/src/nsEventStateManager.h
+++ b/content/events/src/nsEventStateManager.h
@@ -692,16 +692,23 @@ private:
 
   int32_t     mLockCursor;
 
   // Last mouse event refPoint (the offset from the widget's origin in
   // device pixels) when mouse was locked, used to restore mouse position
   // after unlocking.
   nsIntPoint  mPreLockPoint;
 
+  // Stores the refPoint of the last synthetic mouse move we dispatched
+  // to re-center the mouse when we were pointer locked. If this is (-1,-1) it
+  // means we've not recently dispatched a centering event. We use this to
+  // detect when we receive the synth event, so we can cancel and not send it
+  // to content.
+  static nsIntPoint sSynthCenteringPoint;
+
   nsWeakFrame mCurrentTarget;
   nsCOMPtr<nsIContent> mCurrentTargetContent;
   nsWeakFrame mLastMouseOverFrame;
   nsCOMPtr<nsIContent> mLastMouseOverElement;
   static nsWeakFrame sLastDragOverFrame;
 
   // Stores the refPoint (the offset from the widget's origin in device
   // pixels) of the last mouse event.