Bug 1323983 part 1 - Ensure to release the pointer even if the document has been detached. r=smaug draft
authorXidorn Quan <me@upsuper.org>
Wed, 21 Dec 2016 12:02:24 +1100
changeset 451804 47159f623c46549b48c786b28b5c00c246288076
parent 450886 c4b4ce977fa19c7cfd9811225ae459855ed5b6d1
child 451805 4bc50c6f6723114c417471e27b4d5d35bb0e0c75
push id39301
push userxquan@mozilla.com
push dateWed, 21 Dec 2016 01:03:21 +0000
reviewerssmaug
bugs1323983
milestone53.0a1
Bug 1323983 part 1 - Ensure to release the pointer even if the document has been detached. r=smaug MozReview-Commit-ID: FY5lDQBYl6U
dom/base/nsDocument.cpp
dom/events/EventStateManager.cpp
dom/events/EventStateManager.h
--- a/dom/base/nsDocument.cpp
+++ b/dom/base/nsDocument.cpp
@@ -11698,16 +11698,23 @@ nsDocument::SetPointerLock(Element* aEle
       do_QueryReferent(EventStateManager::sPointerLockedDoc);
     MOZ_ASSERT(pointerLockedDoc == this);
   }
 #endif
 
   nsIPresShell* shell = GetShell();
   if (!shell) {
     NS_WARNING("SetPointerLock(): No PresShell");
+    if (!aElement) {
+      // If we are unlocking pointer lock, but for some reason the doc
+      // has already detached from the presshell, just ask the event
+      // state manager to release the pointer.
+      EventStateManager::SetPointerLock(nullptr, nullptr);
+      return true;
+    }
     return false;
   }
   nsPresContext* presContext = shell->GetPresContext();
   if (!presContext) {
     NS_WARNING("SetPointerLock(): Unable to get PresContext");
     return false;
   }
 
@@ -11723,17 +11730,17 @@ nsDocument::SetPointerLock(Element* aEle
       return false;
     }
   }
 
   // Hide the cursor and set pointer lock for future mouse events
   RefPtr<EventStateManager> esm = presContext->EventStateManager();
   esm->SetCursor(aCursorStyle, nullptr, false,
                  0.0f, 0.0f, widget, true);
-  esm->SetPointerLock(widget, aElement);
+  EventStateManager::SetPointerLock(widget, aElement);
 
   return true;
 }
 
 void
 nsDocument::UnlockPointer(nsIDocument* aDoc)
 {
   if (!EventStateManager::sIsPointerLocked) {
--- a/dom/events/EventStateManager.cpp
+++ b/dom/events/EventStateManager.cpp
@@ -263,16 +263,17 @@ static uint32_t sESMInstanceCount = 0;
 static bool sPointerEventEnabled = false;
 
 uint64_t EventStateManager::sUserInputCounter = 0;
 int32_t EventStateManager::sUserInputEventDepth = 0;
 bool EventStateManager::sNormalLMouseEventInProcess = false;
 EventStateManager* EventStateManager::sActiveESM = nullptr;
 nsIDocument* EventStateManager::sMouseOverDocument = nullptr;
 nsWeakFrame EventStateManager::sLastDragOverFrame = nullptr;
+LayoutDeviceIntPoint EventStateManager::sPreLockPoint = LayoutDeviceIntPoint(0, 0);
 LayoutDeviceIntPoint EventStateManager::sLastRefPoint = kInvalidRefPoint;
 CSSIntPoint EventStateManager::sLastScreenPoint = CSSIntPoint(0, 0);
 LayoutDeviceIntPoint EventStateManager::sSynthCenteringPoint = kInvalidRefPoint;
 CSSIntPoint EventStateManager::sLastClientPoint = CSSIntPoint(0, 0);
 bool EventStateManager::sIsPointerLocked = false;
 // Reference to the pointer locked element.
 nsWeakPtr EventStateManager::sPointerLockedElement;
 // Reference to the document which requested pointer lock.
@@ -285,17 +286,16 @@ EventStateManager::WheelPrefs*
   EventStateManager::WheelPrefs::sInstance = nullptr;
 bool EventStateManager::WheelPrefs::sWheelEventsEnabledOnPlugins = true;
 EventStateManager::DeltaAccumulator*
   EventStateManager::DeltaAccumulator::sInstance = nullptr;
 
 EventStateManager::EventStateManager()
   : mLockCursor(0)
   , mLastFrameConsumedSetCursor(false)
-  , mPreLockPoint(0,0)
   , mCurrentTarget(nullptr)
     // init d&d gesture state machine variables
   , mGestureDownPoint(0,0)
   , mPresContext(nullptr)
   , mLClickCount(0)
   , mMClickCount(0)
   , mRClickCount(0)
   , m_haveShutdown(false)
@@ -4348,17 +4348,17 @@ EventStateManager::GetWrapperByEventID(W
   if (!mPointersEnterLeaveHelper.Get(pointer->pointerId, getter_AddRefs(helper))) {
     helper = new OverOutElementsWrapper();
     mPointersEnterLeaveHelper.Put(pointer->pointerId, helper);
   }
 
   return helper;
 }
 
-void
+/* static */ void
 EventStateManager::SetPointerLock(nsIWidget* aWidget,
                                   nsIContent* aElement)
 {
   // NOTE: aElement will be nullptr when unlocking.
   sIsPointerLocked = !!aElement;
 
   // Reset mouse wheel transaction
   WheelTransaction::EndTransaction();
@@ -4366,17 +4366,17 @@ EventStateManager::SetPointerLock(nsIWid
   // Deal with DnD events
   nsCOMPtr<nsIDragService> dragService =
     do_GetService("@mozilla.org/widget/dragservice;1");
 
   if (sIsPointerLocked) {
     MOZ_ASSERT(aWidget, "Locking pointer requires a widget");
 
     // Store the last known ref point so we can reposition the pointer after unlock.
-    mPreLockPoint = sLastRefPoint;
+    sPreLockPoint = sLastRefPoint;
 
     // Fire a synthetic mouse move to ensure event state is updated. We first
     // set the mouse to the center of the window, so that the mouse event
     // doesn't report any movement.
     sLastRefPoint = GetWindowClientRectCenter(aWidget);
     aWidget->SynthesizeNativeMouseMove(
       sLastRefPoint + aWidget->WidgetToScreenOffset(), nullptr);
 
@@ -4384,23 +4384,23 @@ EventStateManager::SetPointerLock(nsIWid
     if (dragService) {
       dragService->Suppress();
     }
   } else {
     // Unlocking, so return pointer to the original position by firing a
     // synthetic mouse event. We first reset sLastRefPoint to its
     // pre-pointerlock position, so that the synthetic mouse event reports
     // no movement.
-    sLastRefPoint = mPreLockPoint;
+    sLastRefPoint = sPreLockPoint;
     // Reset SynthCenteringPoint to invalid so that next time we start
     // locking pointer, it has its initial value.
     sSynthCenteringPoint = kInvalidRefPoint;
     if (aWidget) {
       aWidget->SynthesizeNativeMouseMove(
-        mPreLockPoint + aWidget->WidgetToScreenOffset(), nullptr);
+        sPreLockPoint + aWidget->WidgetToScreenOffset(), nullptr);
     }
 
     // Unsuppress DnD
     if (dragService) {
       dragService->Unsuppress();
     }
   }
 }
--- a/dom/events/EventStateManager.h
+++ b/dom/events/EventStateManager.h
@@ -916,17 +916,17 @@ private:
                                       nsIContent* aClosure);
 
   int32_t     mLockCursor;
   bool mLastFrameConsumedSetCursor;
 
   // Last mouse event mRefPoint (the offset from the widget's origin in
   // device pixels) when mouse was locked, used to restore mouse position
   // after unlocking.
-  LayoutDeviceIntPoint mPreLockPoint;
+  static LayoutDeviceIntPoint sPreLockPoint;
 
   // Stores the mRefPoint 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 LayoutDeviceIntPoint sSynthCenteringPoint;
 
@@ -1010,17 +1010,17 @@ public:
   // Functions used for click hold context menus
   nsCOMPtr<nsITimer> mClickHoldTimer;
   void CreateClickHoldTimer(nsPresContext* aPresContext,
                             nsIFrame* aDownFrame,
                             WidgetGUIEvent* aMouseDownEvent);
   void KillClickHoldTimer();
   void FireContextClick();
 
-  void SetPointerLock(nsIWidget* aWidget, nsIContent* aElement) ;
+  static void SetPointerLock(nsIWidget* aWidget, nsIContent* aElement) ;
   static void sClickHoldCallback ( nsITimer* aTimer, void* aESM ) ;
 };
 
 /**
  * This class is used while processing real user input. During this time, popups
  * are allowed. For mousedown events, mouse capturing is also permitted.
  */
 class AutoHandlingUserInputStatePusher