Bug 1316251 Part1: Separate pointer event implementation from PresShell. r=masayuki draft
authorStone Shih <sshih@mozilla.com>
Wed, 06 Sep 2017 15:47:49 +0800
changeset 670214 ba744a736434bac7ae09c095298d0e93c524d869
parent 669596 7e962631ba4298bcefa571008661983d77c3e652
child 670215 72c000a84a2722a2c7d6311685b37c63a4064110
push id81560
push usersshih@mozilla.com
push dateTue, 26 Sep 2017 02:39:44 +0000
reviewersmasayuki
bugs1316251
milestone58.0a1
Bug 1316251 Part1: Separate pointer event implementation from PresShell. r=masayuki This patch moves pointer event related implementations to a new class. MozReview-Commit-ID: FdBYl7vYC5I
dom/base/Element.h
dom/events/PointerEventHandler.cpp
dom/events/PointerEventHandler.h
dom/events/moz.build
layout/base/PresShell.cpp
layout/base/PresShell.h
layout/base/nsIPresShell.h
layout/build/nsLayoutStatics.cpp
--- a/dom/base/Element.h
+++ b/dom/base/Element.h
@@ -34,16 +34,17 @@
 #include "mozilla/dom/DOMRect.h"
 #include "nsAttrValue.h"
 #include "mozilla/EventForwards.h"
 #include "mozilla/dom/BindingDeclarations.h"
 #include "mozilla/dom/DOMTokenListSupportedTokens.h"
 #include "mozilla/dom/WindowBinding.h"
 #include "mozilla/dom/ElementBinding.h"
 #include "mozilla/dom/Nullable.h"
+#include "mozilla/dom/PointerEventHandler.h"
 #include "mozilla/UniquePtr.h"
 #include "Units.h"
 #include "DOMIntersectionObserver.h"
 
 class mozAutoDocUpdate;
 class nsIFrame;
 class nsIDOMMozNamedAttrMap;
 class nsIMozBrowserFrame;
@@ -973,44 +974,44 @@ public:
 
   void InsertAdjacentText(const nsAString& aWhere,
                           const nsAString& aData,
                           ErrorResult& aError);
 
   void SetPointerCapture(int32_t aPointerId, ErrorResult& aError)
   {
     bool activeState = false;
-    if (!nsIPresShell::GetPointerInfo(aPointerId, activeState)) {
+    if (!PointerEventHandler::GetPointerInfo(aPointerId, activeState)) {
       aError.Throw(NS_ERROR_DOM_INVALID_POINTER_ERR);
       return;
     }
     if (!IsInUncomposedDoc()) {
       aError.Throw(NS_ERROR_DOM_INVALID_STATE_ERR);
       return;
     }
     if (!activeState) {
       return;
     }
-    nsIPresShell::SetPointerCapturingContent(aPointerId, this);
+    PointerEventHandler::SetPointerCaptureById(aPointerId, this);
   }
   void ReleasePointerCapture(int32_t aPointerId, ErrorResult& aError)
   {
     bool activeState = false;
-    if (!nsIPresShell::GetPointerInfo(aPointerId, activeState)) {
+    if (!PointerEventHandler::GetPointerInfo(aPointerId, activeState)) {
       aError.Throw(NS_ERROR_DOM_INVALID_POINTER_ERR);
       return;
     }
     if (HasPointerCapture(aPointerId)) {
-      nsIPresShell::ReleasePointerCapturingContent(aPointerId);
+      PointerEventHandler::ReleasePointerCaptureById(aPointerId);
     }
   }
   bool HasPointerCapture(long aPointerId)
   {
-    nsIPresShell::PointerCaptureInfo* pointerCaptureInfo =
-      nsIPresShell::GetPointerCaptureInfo(aPointerId);
+    PointerCaptureInfo* pointerCaptureInfo =
+      PointerEventHandler::GetPointerCaptureInfo(aPointerId);
     if (pointerCaptureInfo && pointerCaptureInfo->mPendingContent == this) {
       return true;
     }
     return false;
   }
   void SetCapture(bool aRetargetToElement)
   {
     // If there is already an active capture, ignore this request. This would
new file mode 100644
--- /dev/null
+++ b/dom/events/PointerEventHandler.cpp
@@ -0,0 +1,479 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "PointerEventHandler.h"
+#include "nsIFrame.h"
+#include "PointerEvent.h"
+#include "mozilla/PresShell.h"
+
+namespace mozilla {
+
+using namespace dom;
+
+static bool sPointerEventEnabled = true;
+static bool sPointerEventImplicitCapture = false;
+
+class PointerInfo final
+{
+public:
+  uint16_t mPointerType;
+  bool mActiveState;
+  bool mPrimaryState;
+  bool mPreventMouseEventByContent;
+  explicit PointerInfo(bool aActiveState, uint16_t aPointerType,
+                       bool aPrimaryState)
+    : mPointerType(aPointerType)
+    , mActiveState(aActiveState)
+    , mPrimaryState(aPrimaryState)
+    , mPreventMouseEventByContent(false)
+  {
+  }
+};
+
+// Keeps a map between pointerId and element that currently capturing pointer
+// with such pointerId. If pointerId is absent in this map then nobody is
+// capturing it. Additionally keep information about pending capturing content.
+static nsClassHashtable<nsUint32HashKey,
+                        PointerCaptureInfo>* sPointerCaptureList;
+
+// Keeps information about pointers such as pointerId, activeState, pointerType,
+// primaryState
+static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
+
+/* static */ void
+PointerEventHandler::Initialize()
+{
+  static bool addedPointerEventEnabled = false;
+  if (!addedPointerEventEnabled) {
+    Preferences::AddBoolVarCache(&sPointerEventEnabled,
+                                 "dom.w3c_pointer_events.enabled", true);
+    addedPointerEventEnabled = true;
+  }
+  static bool addedPointerEventImplicitCapture = false;
+  if (!addedPointerEventImplicitCapture) {
+    Preferences::AddBoolVarCache(&sPointerEventImplicitCapture,
+                                 "dom.w3c_pointer_events.implicit_capture",
+                                 true);
+    addedPointerEventImplicitCapture = true;
+  }
+}
+
+/* static */ void
+PointerEventHandler::InitializeStatics()
+{
+  MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
+  sPointerCaptureList =
+    new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
+  sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
+}
+
+/* static */ void
+PointerEventHandler::ReleaseStatics()
+{
+  MOZ_ASSERT(sPointerCaptureList, "ReleaseStatics called without Initialize!");
+  delete sPointerCaptureList;
+  sPointerCaptureList = nullptr;
+  delete sActivePointersIds;
+  sActivePointersIds = nullptr;
+}
+
+/* static */ bool
+PointerEventHandler::IsPointerEventEnabled()
+{
+  return sPointerEventEnabled;
+}
+
+/* static */ bool
+PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled()
+{
+  return sPointerEventEnabled && sPointerEventImplicitCapture;
+}
+
+/* static */ void
+PointerEventHandler::UpdateActivePointerState(WidgetGUIEvent* aEvent)
+{
+  if (!IsPointerEventEnabled()) {
+    return;
+  }
+  switch (aEvent->mMessage) {
+  case eMouseEnterIntoWidget:
+    // In this case we have to know information about available mouse pointers
+    if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
+      sActivePointersIds->Put(mouseEvent->pointerId,
+                              new PointerInfo(false, mouseEvent->inputSource,
+                                              true));
+    }
+    break;
+  case ePointerDown:
+    // In this case we switch pointer to active state
+    if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
+      sActivePointersIds->Put(pointerEvent->pointerId,
+                              new PointerInfo(true, pointerEvent->inputSource,
+                                              pointerEvent->mIsPrimary));
+    }
+    break;
+  case ePointerUp:
+    // In this case we remove information about pointer or turn off active state
+    if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
+      if(pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
+        sActivePointersIds->Put(pointerEvent->pointerId,
+                                new PointerInfo(false,
+                                                pointerEvent->inputSource,
+                                                pointerEvent->mIsPrimary));
+      } else {
+        sActivePointersIds->Remove(pointerEvent->pointerId);
+      }
+    }
+    break;
+  case eMouseExitFromWidget:
+    // In this case we have to remove information about disappeared mouse
+    // pointers
+    if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
+      sActivePointersIds->Remove(mouseEvent->pointerId);
+    }
+    break;
+  default:
+    break;
+  }
+}
+
+/* static */ void
+PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId,
+                                           nsIContent* aContent)
+{
+  MOZ_ASSERT(aContent);
+  if (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == GetPointerType(aPointerId)) {
+    nsIPresShell::SetCapturingContent(aContent, CAPTURE_PREVENTDRAG);
+  }
+
+  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
+  if (pointerCaptureInfo) {
+    pointerCaptureInfo->mPendingContent = aContent;
+  } else {
+    sPointerCaptureList->Put(aPointerId, new PointerCaptureInfo(aContent));
+  }
+}
+
+/* static */ PointerCaptureInfo*
+PointerEventHandler::GetPointerCaptureInfo(uint32_t aPointerId)
+{
+  PointerCaptureInfo* pointerCaptureInfo = nullptr;
+  sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
+  return pointerCaptureInfo;
+}
+
+/* static */ void
+PointerEventHandler::ReleasePointerCaptureById(uint32_t aPointerId)
+{
+  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
+  if (pointerCaptureInfo && pointerCaptureInfo->mPendingContent) {
+    if (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == GetPointerType(aPointerId)) {
+      nsIPresShell::SetCapturingContent(nullptr, CAPTURE_PREVENTDRAG);
+    }
+    pointerCaptureInfo->mPendingContent = nullptr;
+  }
+}
+
+/* static */ bool
+PointerEventHandler::GetPointerInfo(uint32_t aPointerId, bool& aActiveState)
+{
+  PointerInfo* pointerInfo = nullptr;
+  if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
+    aActiveState = pointerInfo->mActiveState;
+    return true;
+  }
+  return false;
+}
+
+/* static */ void
+PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent)
+{
+  // Handle pending pointer capture before any pointer events except
+  // gotpointercapture / lostpointercapture.
+  if (!aEvent) {
+    return;
+  }
+  MOZ_ASSERT(IsPointerEventEnabled());
+  MOZ_ASSERT(aEvent->mClass == ePointerEventClass);
+
+  PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId);
+
+  if (captureInfo &&
+      captureInfo->mPendingContent != captureInfo->mOverrideContent) {
+    // cache captureInfo->mPendingContent since it may be changed in the pointer
+    // event listener
+    nsIContent* pendingContent = captureInfo->mPendingContent.get();
+    if (captureInfo->mOverrideContent) {
+      DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
+                                           captureInfo->mOverrideContent);
+    }
+    if (pendingContent) {
+      DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
+                                           pendingContent);
+    }
+
+    captureInfo->mOverrideContent = pendingContent;
+    if (captureInfo->Empty()) {
+      sPointerCaptureList->Remove(aEvent->pointerId);
+    }
+  }
+}
+
+/* static */ nsIContent*
+PointerEventHandler::GetPointerCapturingContent(uint32_t aPointerId)
+{
+  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
+  if (pointerCaptureInfo) {
+    return pointerCaptureInfo->mOverrideContent;
+  }
+  return nullptr;
+}
+
+/* static */ void
+PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent)
+{
+  // We should check that aChild does not contain pointer capturing elements.
+  // If it does we should release the pointer capture for the elements.
+  for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
+    PointerCaptureInfo* data = iter.UserData();
+    if (data && data->mPendingContent &&
+        nsContentUtils::ContentIsDescendantOf(data->mPendingContent,
+                                              aContent)) {
+      ReleasePointerCaptureById(iter.Key());
+    }
+  }
+}
+
+/* static */ void
+PointerEventHandler::PreHandlePointerEventsPreventDefault(
+                       WidgetPointerEvent* aPointerEvent,
+                       WidgetGUIEvent* aMouseOrTouchEvent)
+{
+  if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
+    return;
+  }
+  PointerInfo* pointerInfo = nullptr;
+  if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
+      !pointerInfo) {
+    // The PointerInfo for active pointer should be added for normal cases. But
+    // in some cases, we may receive mouse events before adding PointerInfo in
+    // sActivePointersIds. (e.g. receive mousemove before eMouseEnterIntoWidget
+    // or change preference 'dom.w3c_pointer_events.enabled' from off to on).
+    // In these cases, we could ignore them because they are not the events
+    // between a DefaultPrevented pointerdown and the corresponding pointerup.
+    return;
+  }
+  if (!pointerInfo->mPreventMouseEventByContent) {
+    return;
+  }
+  aMouseOrTouchEvent->PreventDefault(false);
+  if (aPointerEvent->mMessage == ePointerUp) {
+    pointerInfo->mPreventMouseEventByContent = false;
+  }
+}
+
+/* static */ void
+PointerEventHandler::PostHandlePointerEventsPreventDefault(
+                       WidgetPointerEvent* aPointerEvent,
+                       WidgetGUIEvent* aMouseOrTouchEvent)
+{
+  if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
+      !aPointerEvent->DefaultPreventedByContent()) {
+    return;
+  }
+  PointerInfo* pointerInfo = nullptr;
+  if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
+      !pointerInfo) {
+    // We already added the PointerInfo for active pointer when
+    // PresShell::HandleEvent handling pointerdown event.
+#ifdef DEBUG
+    MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
+#endif // #ifdef DEBUG
+    return;
+  }
+  // PreventDefault only applied for active pointers.
+  if (!pointerInfo->mActiveState) {
+    return;
+  }
+  aMouseOrTouchEvent->PreventDefault(false);
+  pointerInfo->mPreventMouseEventByContent = true;
+}
+
+/* static */ nsresult
+PointerEventHandler::DispatchPointerFromMouseOrTouch(
+                       PresShell* aShell,
+                       nsIFrame* aFrame,
+                       WidgetGUIEvent* aEvent,
+                       bool aDontRetargetEvents,
+                       nsEventStatus* aStatus,
+                       nsIContent** aTargetContent)
+{
+  EventMessage pointerMessage = eVoidEvent;
+  if (aEvent->mClass == eMouseEventClass) {
+    WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
+    // 1. If it is not mouse then it is likely will come as touch event
+    // 2. We don't synthesize pointer events for those events that are not
+    //    dispatched to DOM.
+    if (!mouseEvent->convertToPointer ||
+        !aEvent->IsAllowedToDispatchDOMEvent()) {
+      return NS_OK;
+    }
+    int16_t button = mouseEvent->button;
+    switch (mouseEvent->mMessage) {
+    case eMouseMove:
+      button = WidgetMouseEvent::eNoButton;
+      pointerMessage = ePointerMove;
+      break;
+    case eMouseUp:
+      pointerMessage = mouseEvent->buttons ? ePointerMove : ePointerUp;
+      break;
+    case eMouseDown:
+      pointerMessage =
+        mouseEvent->buttons & ~nsContentUtils::GetButtonsFlagForButton(button) ?
+        ePointerMove : ePointerDown;
+      break;
+    default:
+      return NS_OK;
+    }
+
+    WidgetPointerEvent event(*mouseEvent);
+    event.pointerId = mouseEvent->pointerId;
+    event.inputSource = mouseEvent->inputSource;
+    event.mMessage = pointerMessage;
+    event.button = button;
+    event.buttons = mouseEvent->buttons;
+    event.pressure = event.buttons ?
+                     mouseEvent->pressure ? mouseEvent->pressure : 0.5f :
+                     0.0f;
+    event.convertToPointer = mouseEvent->convertToPointer = false;
+    PreHandlePointerEventsPreventDefault(&event, aEvent);
+    RefPtr<PresShell> shell(aShell);
+    shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
+                       aTargetContent);
+    PostHandlePointerEventsPreventDefault(&event, aEvent);
+  } else if (aEvent->mClass == eTouchEventClass) {
+    WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
+    int16_t button = WidgetMouseEvent::eLeftButton;
+    int16_t buttons = WidgetMouseEvent::eLeftButtonFlag;
+    // loop over all touches and dispatch pointer events on each touch
+    // copy the event
+    switch (touchEvent->mMessage) {
+    case eTouchMove:
+      pointerMessage = ePointerMove;
+      button = WidgetMouseEvent::eNoButton;
+      break;
+    case eTouchEnd:
+      pointerMessage = ePointerUp;
+      buttons = WidgetMouseEvent::eNoButtonFlag;
+      break;
+    case eTouchStart:
+      pointerMessage = ePointerDown;
+      break;
+    case eTouchCancel:
+    case eTouchPointerCancel:
+      pointerMessage = ePointerCancel;
+      break;
+    default:
+      return NS_OK;
+    }
+
+    RefPtr<PresShell> shell(aShell);
+    for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
+      Touch* touch = touchEvent->mTouches[i];
+      if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
+        continue;
+      }
+
+      WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
+                               touchEvent->mWidget);
+      event.mIsPrimary = i == 0;
+      event.pointerId = touch->Identifier();
+      event.mRefPoint = touch->mRefPoint;
+      event.mModifiers = touchEvent->mModifiers;
+      event.mWidth = touch->RadiusX(CallerType::System);
+      event.mHeight = touch->RadiusY(CallerType::System);
+      event.tiltX = touch->tiltX;
+      event.tiltY = touch->tiltY;
+      event.mTime = touchEvent->mTime;
+      event.mTimeStamp = touchEvent->mTimeStamp;
+      event.mFlags = touchEvent->mFlags;
+      event.button = button;
+      event.buttons = buttons;
+      event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
+      event.convertToPointer = touch->convertToPointer = false;
+      PreHandlePointerEventsPreventDefault(&event, aEvent);
+      shell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
+                         aTargetContent);
+      PostHandlePointerEventsPreventDefault(&event, aEvent);
+    }
+  }
+  return NS_OK;
+}
+
+/* static */ uint16_t
+PointerEventHandler::GetPointerType(uint32_t aPointerId)
+{
+  PointerInfo* pointerInfo = nullptr;
+  if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
+    return pointerInfo->mPointerType;
+  }
+  return nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
+}
+
+/* static */ bool
+PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId)
+{
+  PointerInfo* pointerInfo = nullptr;
+  if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
+    return pointerInfo->mPrimaryState;
+  }
+  return false;
+}
+
+/* static */ void
+PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
+                       bool aIsGotCapture,
+                       const WidgetPointerEvent* aPointerEvent,
+                       nsIContent* aCaptureTarget)
+{
+  nsIDocument* targetDoc = aCaptureTarget->OwnerDoc();
+  nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell();
+  if (NS_WARN_IF(!shell)) {
+    return;
+  }
+
+  if (!aIsGotCapture && !aCaptureTarget->IsInUncomposedDoc()) {
+    // If the capturing element was removed from the DOM tree, fire
+    // ePointerLostCapture at the document.
+    PointerEventInit init;
+    init.mPointerId = aPointerEvent->pointerId;
+    init.mBubbles = true;
+    init.mComposed = true;
+    ConvertPointerTypeToString(aPointerEvent->inputSource, init.mPointerType);
+    init.mIsPrimary = aPointerEvent->mIsPrimary;
+    RefPtr<PointerEvent> event;
+    event = PointerEvent::Constructor(aCaptureTarget,
+                                      NS_LITERAL_STRING("lostpointercapture"),
+                                      init);
+    bool dummy;
+    targetDoc->DispatchEvent(event->InternalDOMEvent(), &dummy);
+    return;
+  }
+  nsEventStatus status = nsEventStatus_eIgnore;
+  WidgetPointerEvent localEvent(aPointerEvent->IsTrusted(),
+                                aIsGotCapture ? ePointerGotCapture :
+                                                ePointerLostCapture,
+                                aPointerEvent->mWidget);
+  localEvent.AssignPointerEventData(*aPointerEvent, true);
+  nsresult rv = shell->HandleEventWithTarget(
+                         &localEvent,
+                         aCaptureTarget->GetPrimaryFrame(),
+                         aCaptureTarget, &status);
+  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
+                       "DispatchGotOrLostPointerCaptureEvent failed");
+}
+
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/dom/events/PointerEventHandler.h
@@ -0,0 +1,144 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set ts=8 sts=2 et sw=2 tw=80: */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_PointerEventHandler_h
+#define mozilla_PointerEventHandler_h
+
+#include "mozilla/EventForwards.h"
+
+class nsIFrame;
+class nsIContent;
+
+namespace mozilla {
+
+class PresShell;
+
+class PointerCaptureInfo final
+{
+public:
+  nsCOMPtr<nsIContent> mPendingContent;
+  nsCOMPtr<nsIContent> mOverrideContent;
+
+  explicit PointerCaptureInfo(nsIContent* aPendingContent)
+    : mPendingContent(aPendingContent)
+  {
+    MOZ_COUNT_CTOR(PointerCaptureInfo);
+  }
+
+  ~PointerCaptureInfo()
+  {
+    MOZ_COUNT_DTOR(PointerCaptureInfo);
+  }
+
+  bool Empty()
+  {
+    return !(mPendingContent || mOverrideContent);
+  }
+};
+
+class PointerEventHandler final
+{
+public:
+  // Called in PresShell::Initialize to initialize pointer event related
+  // preferences.
+  static void Initialize();
+
+  // Called in nsLayoutStatics::Initialize/Shutdown to initialize pointer event
+  // related static variables.
+  static void InitializeStatics();
+  static void ReleaseStatics();
+
+  // Return the preference value of pointer event enabled.
+  static bool IsPointerEventEnabled();
+
+  // Return the preference value of implicit capture.
+  static bool IsPointerEventImplicitCaptureForTouchEnabled();
+
+  // Called in ESM::PreHandleEvent to update current active pointers in a hash
+  // table.
+  static void UpdateActivePointerState(WidgetGUIEvent* aEvent);
+
+  // Got/release pointer capture of the specified pointer by the content.
+  static void SetPointerCaptureById(uint32_t aPointerId, nsIContent* aContent);
+  static void ReleasePointerCaptureById(uint32_t aPointerId);
+
+  // Get the pointer captured info of the specified pointer.
+  static PointerCaptureInfo* GetPointerCaptureInfo(uint32_t aPointerId);
+
+  // GetPointerInfo returns true if pointer with aPointerId is situated in
+  // device, false otherwise.
+  // aActiveState is additional information, which shows state of pointer like
+  // button state for mouse.
+  static bool GetPointerInfo(uint32_t aPointerId, bool& aActiveState);
+
+  // CheckPointerCaptureState checks cases, when got/lostpointercapture events
+  // should be fired.
+  static void CheckPointerCaptureState(WidgetPointerEvent* aEvent);
+
+  static nsIContent* GetPointerCapturingContent(uint32_t aPointerId);
+
+  // Release pointer capture if captured by the specified content or it's
+  // descendant. This is called to handle the case that the pointer capturing
+  // content or it's parent is removed from the document.
+  static void ReleaseIfCaptureByDescendant(nsIContent* aContent);
+
+  /*
+   * This function handles the case when content had called preventDefault on
+   * the active pointer. In that case we have to prevent firing subsequent mouse
+   * to content. We check the flag PointerInfo::mPreventMouseEventByContent and
+   * call PreventDefault(false) to stop default behaviors and stop firing mouse
+   * events to content and chrome.
+   *
+   * note: mouse transition events are excluded
+   * note: we have to clean mPreventMouseEventByContent on pointerup for those
+   *       devices support hover
+   * note: we don't suppress firing mouse events to chrome and system group
+   *       handlers because they may implement default behaviors
+   */
+  static void PreHandlePointerEventsPreventDefault(
+                WidgetPointerEvent* aPointerEvent,
+                WidgetGUIEvent* aMouseOrTouchEvent);
+
+  /*
+   * This function handles the preventDefault behavior of pointerdown. When user
+   * preventDefault on pointerdown, We have to mark the active pointer to
+   * prevent sebsequent mouse events (except mouse transition events) and
+   * default behaviors.
+   *
+   * We add mPreventMouseEventByContent flag in PointerInfo to represent the
+   * active pointer won't firing compatible mouse events. It's set to true when
+   * content preventDefault on pointerdown
+   */
+  static void PostHandlePointerEventsPreventDefault(
+                WidgetPointerEvent* aPointerEvent,
+                WidgetGUIEvent* aMouseOrTouchEvent);
+
+  static nsresult DispatchPointerFromMouseOrTouch(PresShell* aShell,
+                                                  nsIFrame* aFrame,
+                                                  WidgetGUIEvent* aEvent,
+                                                  bool aDontRetargetEvents,
+                                                  nsEventStatus* aStatus,
+                                                  nsIContent** aTargetContent);
+
+private:
+  // GetPointerType returns pointer type like mouse, pen or touch for pointer
+  // event with pointerId. The return value must be one of
+  // nsIDOMMouseEvent::MOZ_SOURCE_*
+  static uint16_t GetPointerType(uint32_t aPointerId);
+
+  // GetPointerPrimaryState returns state of attribute isPrimary for pointer
+  // event with pointerId
+  static bool GetPointerPrimaryState(uint32_t aPointerId);
+
+  static void DispatchGotOrLostPointerCaptureEvent(
+                bool aIsGotCapture,
+                const WidgetPointerEvent* aPointerEvent,
+                nsIContent* aCaptureTarget);
+};
+
+} // namespace mozilla
+
+#endif // mozilla_PointerEventHandler_h
--- a/dom/events/moz.build
+++ b/dom/events/moz.build
@@ -57,16 +57,17 @@ EXPORTS.mozilla.dom += [
     'KeyboardEvent.h',
     'MessageEvent.h',
     'MouseEvent.h',
     'MouseScrollEvent.h',
     'MutationEvent.h',
     'NotifyPaintEvent.h',
     'PaintRequest.h',
     'PointerEvent.h',
+    'PointerEventHandler.h',
     'ScrollAreaEvent.h',
     'SimpleGestureEvent.h',
     'StorageEvent.h',
     'TextClause.h',
     'Touch.h',
     'TouchEvent.h',
     'TransitionEvent.h',
     'UIEvent.h',
@@ -106,16 +107,17 @@ UNIFIED_SOURCES += [
     'KeyboardEvent.cpp',
     'MessageEvent.cpp',
     'MouseEvent.cpp',
     'MouseScrollEvent.cpp',
     'MutationEvent.cpp',
     'NotifyPaintEvent.cpp',
     'PaintRequest.cpp',
     'PointerEvent.cpp',
+    'PointerEventHandler.cpp',
     'ScrollAreaEvent.cpp',
     'SimpleGestureEvent.cpp',
     'StorageEvent.cpp',
     'TextClause.cpp',
     'TextComposition.cpp',
     'Touch.cpp',
     'TouchEvent.cpp',
     'TransitionEvent.cpp',
--- a/layout/base/PresShell.cpp
+++ b/layout/base/PresShell.cpp
@@ -36,17 +36,17 @@
 #include "gfxContext.h"
 #include "gfxPrefs.h"
 #include "gfxUserFontSet.h"
 #include "nsPresContext.h"
 #include "nsIContent.h"
 #include "nsIContentIterator.h"
 #include "mozilla/dom/Element.h"
 #include "mozilla/dom/Event.h" // for Event::GetEventPopupControlState()
-#include "mozilla/dom/PointerEvent.h"
+#include "mozilla/dom/PointerEventHandler.h"
 #include "nsIDocument.h"
 #include "nsAnimationManager.h"
 #include "nsNameSpaceManager.h"  // for Pref-related rule management (bugs 22963,20760,31816)
 #include "nsFrame.h"
 #include "FrameLayerBuilder.h"
 #include "nsViewManager.h"
 #include "nsView.h"
 #include "nsCRTGlue.h"
@@ -210,27 +210,16 @@ using namespace mozilla::layout;
 using PaintFrameFlags = nsLayoutUtils::PaintFrameFlags;
 typedef FrameMetrics::ViewID ViewID;
 
 CapturingContentInfo nsIPresShell::gCaptureInfo =
   { false /* mAllowed */, false /* mPointerLock */, false /* mRetargetToElement */,
     false /* mPreventDrag */ };
 nsIContent* nsIPresShell::gKeyDownTarget;
 
-// Keeps a map between pointerId and element that currently capturing pointer
-// with such pointerId. If pointerId is absent in this map then nobody is
-// capturing it. Additionally keep information about pending capturing content.
-static nsClassHashtable<nsUint32HashKey,
-                        nsIPresShell::PointerCaptureInfo>* sPointerCaptureList;
-
-// Keeps information about pointers such as pointerId, activeState, pointerType,
-// primaryState
-static nsClassHashtable<nsUint32HashKey,
-                        nsIPresShell::PointerInfo>* sActivePointersIds;
-
 // RangePaintInfo is used to paint ranges to offscreen buffers
 struct RangePaintInfo {
   RefPtr<nsRange> mRange;
   nsDisplayListBuilder mBuilder;
   nsDisplayList mList;
 
   // offset of builder's reference frame to the root frame
   nsPoint mRootOffset;
@@ -707,18 +696,16 @@ nsIPresShell::FrameSelection()
   RefPtr<nsFrameSelection> ret = mSelection;
   return ret.forget();
 }
 
 //----------------------------------------------------------------------
 
 static bool sSynthMouseMove = true;
 static uint32_t sNextPresShellId;
-static bool sPointerEventEnabled = true;
-static bool sPointerEventImplicitCapture = false;
 static bool sAccessibleCaretEnabled = false;
 static bool sAccessibleCaretOnTouch = false;
 
 /* static */ bool
 PresShell::AccessibleCaretEnabled(nsIDocShell* aDocShell)
 {
   static bool initialized = false;
   if (!initialized) {
@@ -837,29 +824,17 @@ PresShell::PresShell()
   mScrollPositionClampingScrollPortSizeSet = false;
 
   static bool addedSynthMouseMove = false;
   if (!addedSynthMouseMove) {
     Preferences::AddBoolVarCache(&sSynthMouseMove,
                                  "layout.reflow.synthMouseMove", true);
     addedSynthMouseMove = true;
   }
-  static bool addedPointerEventEnabled = false;
-  if (!addedPointerEventEnabled) {
-    Preferences::AddBoolVarCache(&sPointerEventEnabled,
-                                 "dom.w3c_pointer_events.enabled", true);
-    addedPointerEventEnabled = true;
-  }
-  static bool addedPointerEventImplicitCapture = false;
-  if (!addedPointerEventImplicitCapture) {
-    Preferences::AddBoolVarCache(&sPointerEventImplicitCapture,
-                                 "dom.w3c_pointer_events.implicit_capture",
-                                 true);
-    addedPointerEventImplicitCapture = true;
-  }
+  PointerEventHandler::Initialize();
   mPaintingIsFrozen = false;
   mHasCSSBackgroundColor = true;
   mIsLastChromeOnlyEscapeKeyConsumed = false;
   mHasReceivedPaintMessage = false;
 }
 
 NS_IMPL_ISUPPORTS(PresShell, nsIPresShell, nsIDocumentObserver,
                   nsISelectionController,
@@ -4476,25 +4451,17 @@ PresShell::ContentRemoved(nsIDocument *a
 
   // After removing aChild from tree we should save information about live ancestor
   if (mPointerEventTarget) {
     if (nsContentUtils::ContentIsDescendantOf(mPointerEventTarget, aChild)) {
       mPointerEventTarget = aMaybeContainer;
     }
   }
 
-  // We should check that aChild does not contain pointer capturing elements.
-  // If it does we should release the pointer capture for the elements.
-  for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
-    nsIPresShell::PointerCaptureInfo* data = iter.UserData();
-    if (data && data->mPendingContent &&
-        nsContentUtils::ContentIsDescendantOf(data->mPendingContent, aChild)) {
-      nsIPresShell::ReleasePointerCapturingContent(iter.Key());
-    }
-  }
+  PointerEventHandler::ReleaseIfCaptureByDescendant(aChild);
 
   mFrameConstructor->ContentRemoved(aMaybeContainer, aChild, oldNextSibling,
                                     nsCSSFrameConstructor::REMOVE_CONTENT);
 
   if (aChild->NodeType() == nsIDOMNode::DOCUMENT_TYPE_NODE) {
     MOZ_ASSERT(container == aDocument);
     NotifyFontSizeInflationEnabledIsDirty();
   }
@@ -6494,168 +6461,16 @@ nsIPresShell::SetCapturingContent(nsICon
     // CAPTURE_POINTERLOCK is the same as CAPTURE_RETARGETTOELEMENT & CAPTURE_IGNOREALLOWED
     gCaptureInfo.mRetargetToElement = ((aFlags & CAPTURE_RETARGETTOELEMENT) != 0) ||
                                       ((aFlags & CAPTURE_POINTERLOCK) != 0);
     gCaptureInfo.mPreventDrag = (aFlags & CAPTURE_PREVENTDRAG) != 0;
     gCaptureInfo.mPointerLock = (aFlags & CAPTURE_POINTERLOCK) != 0;
   }
 }
 
-/* static */ void
-nsIPresShell::SetPointerCapturingContent(uint32_t aPointerId,
-                                         nsIContent* aContent)
-{
-  MOZ_ASSERT(aContent != nullptr);
-
-  if (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == GetPointerType(aPointerId)) {
-    SetCapturingContent(aContent, CAPTURE_PREVENTDRAG);
-  }
-
-  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
-  if (pointerCaptureInfo) {
-    pointerCaptureInfo->mPendingContent = aContent;
-  } else {
-    sPointerCaptureList->Put(aPointerId, new PointerCaptureInfo(aContent));
-  }
-}
-
-/* static */ nsIPresShell::PointerCaptureInfo*
-nsIPresShell::GetPointerCaptureInfo(uint32_t aPointerId)
-{
-  PointerCaptureInfo* pointerCaptureInfo = nullptr;
-  sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
-  return pointerCaptureInfo;
-}
-
-/* static */ void
-nsIPresShell::ReleasePointerCapturingContent(uint32_t aPointerId)
-{
-  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
-  if (pointerCaptureInfo && pointerCaptureInfo->mPendingContent) {
-    if (nsIDOMMouseEvent::MOZ_SOURCE_MOUSE == GetPointerType(aPointerId)) {
-      SetCapturingContent(nullptr, CAPTURE_PREVENTDRAG);
-    }
-    pointerCaptureInfo->mPendingContent = nullptr;
-  }
-}
-
-/* static */ nsIContent*
-nsIPresShell::GetPointerCapturingContent(uint32_t aPointerId)
-{
-  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
-  if (pointerCaptureInfo) {
-    return pointerCaptureInfo->mOverrideContent;
-  }
-  return nullptr;
-}
-
-/* static */ void
-nsIPresShell::CheckPointerCaptureState(const WidgetPointerEvent* aPointerEvent)
-{
-  PointerCaptureInfo* captureInfo =
-    GetPointerCaptureInfo(aPointerEvent->pointerId);
-
-  if (captureInfo &&
-      captureInfo->mPendingContent != captureInfo->mOverrideContent) {
-    // cache captureInfo->mPendingContent since it may be changed in the pointer
-    // event listener
-    nsIContent* pendingContent = captureInfo->mPendingContent.get();
-    if (captureInfo->mOverrideContent) {
-      DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false,
-                                           aPointerEvent,
-                                           captureInfo->mOverrideContent);
-    }
-    if (pendingContent) {
-      DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true,
-                                           aPointerEvent, pendingContent);
-    }
-
-    captureInfo->mOverrideContent = pendingContent;
-    if (captureInfo->Empty()) {
-      sPointerCaptureList->Remove(aPointerEvent->pointerId);
-    }
-  }
-}
-
-/* static */ uint16_t
-nsIPresShell::GetPointerType(uint32_t aPointerId)
-{
-  PointerInfo* pointerInfo = nullptr;
-  if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
-    return pointerInfo->mPointerType;
-  }
-  return nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN;
-}
-
-/* static */ bool
-nsIPresShell::GetPointerPrimaryState(uint32_t aPointerId)
-{
-  PointerInfo* pointerInfo = nullptr;
-  if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
-    return pointerInfo->mPrimaryState;
-  }
-  return false;
-}
-
-/* static */ bool
-nsIPresShell::GetPointerInfo(uint32_t aPointerId, bool& aActiveState)
-{
-  PointerInfo* pointerInfo = nullptr;
-  if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
-    aActiveState = pointerInfo->mActiveState;
-    return true;
-  }
-  return false;
-}
-
-void
-PresShell::UpdateActivePointerState(WidgetGUIEvent* aEvent)
-{
-  switch (aEvent->mMessage) {
-  case eMouseEnterIntoWidget:
-    // In this case we have to know information about available mouse pointers
-    if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
-      sActivePointersIds->Put(mouseEvent->pointerId,
-                              new PointerInfo(false, mouseEvent->inputSource,
-                                              true));
-    }
-    break;
-  case ePointerDown:
-    // In this case we switch pointer to active state
-    if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
-      sActivePointersIds->Put(pointerEvent->pointerId,
-                              new PointerInfo(true, pointerEvent->inputSource,
-                                              pointerEvent->mIsPrimary));
-    }
-    break;
-  case ePointerUp:
-    // In this case we remove information about pointer or turn off active state
-    if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
-      if(pointerEvent->inputSource != nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
-        sActivePointersIds->Put(pointerEvent->pointerId,
-                                new PointerInfo(false,
-                                                pointerEvent->inputSource,
-                                                pointerEvent->mIsPrimary));
-      } else {
-        sActivePointersIds->Remove(pointerEvent->pointerId);
-      }
-    }
-    break;
-  case eMouseExitFromWidget:
-    // In this case we have to remove information about disappeared mouse
-    // pointers
-    if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
-      sActivePointersIds->Remove(mouseEvent->pointerId);
-    }
-    break;
-  default:
-    break;
-  }
-}
-
 nsIContent*
 PresShell::GetCurrentEventContent()
 {
   if (mCurrentEventContent &&
       mCurrentEventContent->GetComposedDoc() != mDocument) {
     mCurrentEventContent = nullptr;
     mCurrentEventFrame = nullptr;
   }
@@ -6919,200 +6734,16 @@ FlushThrottledStyles(nsIDocument *aDocum
       presContext->RestyleManager()->UpdateOnlyAnimationStyles();
     }
   }
 
   aDocument->EnumerateSubDocuments(FlushThrottledStyles, nullptr);
   return true;
 }
 
-/*
- * This function handles the preventDefault behavior of pointerdown. When user
- * preventDefault on pointerdown, We have to mark the active pointer to prevent
- * sebsequent mouse events (except mouse transition events) and default
- * behaviors.
- *
- * We add mPreventMouseEventByContent flag in PointerInfo to represent the
- * active pointer won't firing compatible mouse events. It's set to true when
- * content preventDefault on pointerdown
- */
-static void
-PostHandlePointerEventsPreventDefault(WidgetPointerEvent* aPointerEvent,
-                                      WidgetGUIEvent* aMouseOrTouchEvent)
-{
-  if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
-      !aPointerEvent->DefaultPreventedByContent()) {
-    return;
-  }
-  nsIPresShell::PointerInfo* pointerInfo = nullptr;
-  if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
-      !pointerInfo) {
-    // We already added the PointerInfo for active pointer when
-    // PresShell::HandleEvent handling pointerdown event.
-#ifdef DEBUG
-    MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
-#endif // #ifdef DEBUG
-    return;
-  }
-  // PreventDefault only applied for active pointers.
-  if (!pointerInfo->mActiveState) {
-    return;
-  }
-  aMouseOrTouchEvent->PreventDefault(false);
-  pointerInfo->mPreventMouseEventByContent = true;
-}
-
-/*
- * This function handles the case when content had called preventDefault on the
- * active pointer. In that case we have to prevent firing subsequent mouse
- * to content. We check the flag PointerInfo::mPreventMouseEventByContent and
- * call PreventDefault(false) to stop default behaviors and stop firing mouse
- * events to content and chrome.
- *
- * note: mouse transition events are excluded
- * note: we have to clean mPreventMouseEventByContent on pointerup for those
- *       devices support hover
- * note: we don't suppress firing mouse events to chrome and system group
- *       handlers because they may implement default behaviors
- */
-static void
-PreHandlePointerEventsPreventDefault(WidgetPointerEvent* aPointerEvent,
-                                     WidgetGUIEvent* aMouseOrTouchEvent)
-{
-  if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
-    return;
-  }
-  nsIPresShell::PointerInfo* pointerInfo = nullptr;
-  if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
-      !pointerInfo) {
-    // The PointerInfo for active pointer should be added for normal cases. But
-    // in some cases, we may receive mouse events before adding PointerInfo in
-    // sActivePointersIds. (e.g. receive mousemove before eMouseEnterIntoWidget
-    // or change preference 'dom.w3c_pointer_events.enabled' from off to on).
-    // In these cases, we could ignore them because they are not the events
-    // between a DefaultPrevented pointerdown and the corresponding pointerup.
-    return;
-  }
-  if (!pointerInfo->mPreventMouseEventByContent) {
-    return;
-  }
-  aMouseOrTouchEvent->PreventDefault(false);
-  if (aPointerEvent->mMessage == ePointerUp) {
-    pointerInfo->mPreventMouseEventByContent = false;
-  }
-}
-
-static nsresult
-DispatchPointerFromMouseOrTouch(PresShell* aShell,
-                                nsIFrame* aFrame,
-                                WidgetGUIEvent* aEvent,
-                                bool aDontRetargetEvents,
-                                nsEventStatus* aStatus,
-                                nsIContent** aTargetContent)
-{
-  EventMessage pointerMessage = eVoidEvent;
-  if (aEvent->mClass == eMouseEventClass) {
-    WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
-    // 1. If it is not mouse then it is likely will come as touch event
-    // 2. We don't synthesize pointer events for those events that are not
-    //    dispatched to DOM.
-    if (!mouseEvent->convertToPointer ||
-        !aEvent->IsAllowedToDispatchDOMEvent()) {
-      return NS_OK;
-    }
-    int16_t button = mouseEvent->button;
-    switch (mouseEvent->mMessage) {
-    case eMouseMove:
-      button = WidgetMouseEvent::eNoButton;
-      pointerMessage = ePointerMove;
-      break;
-    case eMouseUp:
-      pointerMessage = mouseEvent->buttons ? ePointerMove : ePointerUp;
-      break;
-    case eMouseDown:
-      pointerMessage =
-        mouseEvent->buttons & ~nsContentUtils::GetButtonsFlagForButton(button) ?
-        ePointerMove : ePointerDown;
-      break;
-    default:
-      return NS_OK;
-    }
-
-    WidgetPointerEvent event(*mouseEvent);
-    event.pointerId = mouseEvent->pointerId;
-    event.inputSource = mouseEvent->inputSource;
-    event.mMessage = pointerMessage;
-    event.button = button;
-    event.buttons = mouseEvent->buttons;
-    event.pressure = event.buttons ?
-                     mouseEvent->pressure ? mouseEvent->pressure : 0.5f :
-                     0.0f;
-    event.convertToPointer = mouseEvent->convertToPointer = false;
-    PreHandlePointerEventsPreventDefault(&event, aEvent);
-    aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
-                        aTargetContent);
-    PostHandlePointerEventsPreventDefault(&event, aEvent);
-  } else if (aEvent->mClass == eTouchEventClass) {
-    WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
-    int16_t button = WidgetMouseEvent::eLeftButton;
-    int16_t buttons = WidgetMouseEvent::eLeftButtonFlag;
-    // loop over all touches and dispatch pointer events on each touch
-    // copy the event
-    switch (touchEvent->mMessage) {
-    case eTouchMove:
-      pointerMessage = ePointerMove;
-      button = WidgetMouseEvent::eNoButton;
-      break;
-    case eTouchEnd:
-      pointerMessage = ePointerUp;
-      buttons = WidgetMouseEvent::eNoButtonFlag;
-      break;
-    case eTouchStart:
-      pointerMessage = ePointerDown;
-      break;
-    case eTouchCancel:
-    case eTouchPointerCancel:
-      pointerMessage = ePointerCancel;
-      break;
-    default:
-      return NS_OK;
-    }
-
-    for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
-      mozilla::dom::Touch* touch = touchEvent->mTouches[i];
-      if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
-        continue;
-      }
-
-      WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
-                               touchEvent->mWidget);
-      event.mIsPrimary = i == 0;
-      event.pointerId = touch->Identifier();
-      event.mRefPoint = touch->mRefPoint;
-      event.mModifiers = touchEvent->mModifiers;
-      event.mWidth = touch->RadiusX(CallerType::System);
-      event.mHeight = touch->RadiusY(CallerType::System);
-      event.tiltX = touch->tiltX;
-      event.tiltY = touch->tiltY;
-      event.mTime = touchEvent->mTime;
-      event.mTimeStamp = touchEvent->mTimeStamp;
-      event.mFlags = touchEvent->mFlags;
-      event.button = button;
-      event.buttons = buttons;
-      event.inputSource = nsIDOMMouseEvent::MOZ_SOURCE_TOUCH;
-      event.convertToPointer = touch->convertToPointer = false;
-      PreHandlePointerEventsPreventDefault(&event, aEvent);
-      aShell->HandleEvent(aFrame, &event, aDontRetargetEvents, aStatus,
-                          aTargetContent);
-      PostHandlePointerEventsPreventDefault(&event, aEvent);
-    }
-  }
-  return NS_OK;
-}
-
 bool
 PresShell::CanDispatchEvent(const WidgetGUIEvent* aEvent) const
 {
   bool rv =
     mPresContext && !mHaveShutDown && nsContentUtils::IsSafeToRunScript();
   if (aEvent) {
     rv &= (aEvent && aEvent->mWidget && !aEvent->mWidget->Destroyed());
   }
@@ -7149,22 +6780,22 @@ PresShell::HandleEvent(nsIFrame* aFrame,
   // Update the latest focus sequence number with this new sequence number
   if (mAPZFocusSequenceNumber < aEvent->mFocusSequenceNumber) {
     mAPZFocusSequenceNumber = aEvent->mFocusSequenceNumber;
 
     // Schedule an empty transaction to transmit this focus update
     aFrame->SchedulePaint(nsIFrame::PAINT_COMPOSITE_ONLY);
   }
 
-  if (sPointerEventEnabled) {
+  if (PointerEventHandler::IsPointerEventEnabled()) {
     AutoWeakFrame weakFrame(aFrame);
     nsCOMPtr<nsIContent> targetContent;
-    DispatchPointerFromMouseOrTouch(this, aFrame, aEvent, aDontRetargetEvents,
-                                    aEventStatus,
-                                    getter_AddRefs(targetContent));
+    PointerEventHandler::DispatchPointerFromMouseOrTouch(
+                           this, aFrame, aEvent, aDontRetargetEvents,
+                           aEventStatus, getter_AddRefs(targetContent));
     if (!weakFrame.IsAlive()) {
       if (targetContent) {
         aFrame = targetContent->GetPrimaryFrame();
         if (!aFrame) {
           PushCurrentEventInfo(aFrame, targetContent);
           nsresult rv = HandleEventInternal(aEvent, aEventStatus, true);
           PopCurrentEventInfo();
           return rv;
@@ -7204,19 +6835,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
         // If the event is consumed, cancel APZC panning by setting
         // mMultipleActionsPrevented.
         aEvent->mFlags.mMultipleActionsPrevented = true;
         return NS_OK;
       }
     }
   }
 
-  if (sPointerEventEnabled) {
-    UpdateActivePointerState(aEvent);
-  }
+  PointerEventHandler::UpdateActivePointerState(aEvent);
 
   if (!nsContentUtils::IsSafeToRunScript() &&
       aEvent->IsAllowedToDispatchDOMEvent()) {
     if (aEvent->mClass == eCompositionEventClass) {
       IMEStateManager::OnCompositionEventDiscarded(
         aEvent->AsCompositionEvent());
     }
 #ifdef DEBUG
@@ -7546,53 +7175,53 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       NS_ASSERTION(capturingContent->GetComposedDoc() == GetDocument(),
                    "Unexpected document");
       nsIFrame* capturingFrame = capturingContent->GetPrimaryFrame();
       if (capturingFrame) {
         frame = capturingFrame;
       }
     }
 
-    if (aEvent->mClass == ePointerEventClass) {
-      if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
-        // Try to keep frame for following check, because
-        // frame can be damaged during CheckPointerCaptureState.
-        AutoWeakFrame frameKeeper(frame);
-        // Handle pending pointer capture before any pointer events except
-        // gotpointercapture / lostpointercapture.
-        CheckPointerCaptureState(pointerEvent);
-        // Prevent application crashes, in case damaged frame.
-        if (!frameKeeper.IsAlive()) {
-          frame = nullptr;
+    if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
+      // Try to keep frame for following check, because
+      // frame can be damaged during CheckPointerCaptureState.
+      AutoWeakFrame frameKeeper(frame);
+      // Handle pending pointer capture before any pointer events except
+      // gotpointercapture / lostpointercapture.
+      PointerEventHandler::CheckPointerCaptureState(pointerEvent);
+      // Prevent application crashes, in case damaged frame.
+      if (!frameKeeper.IsAlive()) {
+        frame = nullptr;
+      }
+      // Implicit pointer capture for touch
+      if (frame &&
+          PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() &&
+          pointerEvent->mMessage == ePointerDown &&
+          pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
+        nsCOMPtr<nsIContent> targetContent;
+        frame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
+        while (targetContent && !targetContent->IsElement()) {
+          targetContent = targetContent->GetParent();
         }
-        // Implicit pointer capture for touch
-        if (frame && sPointerEventImplicitCapture &&
-            pointerEvent->mMessage == ePointerDown &&
-            pointerEvent->inputSource == nsIDOMMouseEvent::MOZ_SOURCE_TOUCH) {
-          nsCOMPtr<nsIContent> targetContent;
-          frame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
-          while (targetContent && !targetContent->IsElement()) {
-            targetContent = targetContent->GetParent();
-          }
-          if (targetContent) {
-            SetPointerCapturingContent(pointerEvent->pointerId, targetContent);
-          }
+        if (targetContent) {
+          PointerEventHandler::SetPointerCaptureById(pointerEvent->pointerId,
+                                                     targetContent);
         }
       }
     }
 
     // Mouse events should be fired to the same target as their mapped pointer
     // events
     if ((aEvent->mClass == ePointerEventClass ||
          aEvent->mClass == eMouseEventClass) &&
         aEvent->mMessage != ePointerDown && aEvent->mMessage != eMouseDown) {
       if (WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent()) {
         uint32_t pointerId = mouseEvent->pointerId;
         nsIContent* pointerCapturingContent =
-          GetPointerCapturingContent(pointerId);
+          PointerEventHandler::GetPointerCapturingContent(pointerId);
 
         if (pointerCapturingContent) {
           if (nsIFrame* capturingFrame = pointerCapturingContent->GetPrimaryFrame()) {
             frame = capturingFrame;
           }
         }
       }
     }
@@ -7685,18 +7314,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
           }
         }
       }
     }
 
     // Before HandlePositionedEvent we should save mPointerEventTarget in some
     // cases
     AutoWeakFrame weakFrame;
-    if (sPointerEventEnabled && aTargetContent &&
-        ePointerEventClass == aEvent->mClass) {
+    if (aTargetContent && ePointerEventClass == aEvent->mClass) {
       weakFrame = frame;
       shell->mPointerEventTarget = frame->GetContent();
       MOZ_ASSERT(!frame->GetContent() ||
                  shell->GetDocument() == frame->GetContent()->OwnerDoc());
     }
 
     // Prevent deletion until we're done with event handling (bug 336582) and
     // swap mPointerEventTarget to *aTargetContent
@@ -7710,18 +7338,17 @@ PresShell::HandleEvent(nsIFrame* aFrame,
       // now ask the subshell to dispatch it normally.
       rv = shell->HandlePositionedEvent(frame, aEvent, aEventStatus);
     } else {
       rv = HandlePositionedEvent(frame, aEvent, aEventStatus);
     }
 
     // After HandlePositionedEvent we should reestablish
     // content (which still live in tree) in some cases
-    if (sPointerEventEnabled && aTargetContent &&
-        ePointerEventClass == aEvent->mClass) {
+    if (aTargetContent && ePointerEventClass == aEvent->mClass) {
       if (!weakFrame.IsAlive()) {
         shell->mPointerEventTarget.swap(*aTargetContent);
       }
     }
 
     return rv;
   }
 
@@ -8157,18 +7784,18 @@ PresShell::HandleEventInternal(WidgetEve
 
       if (aEvent->mMessage == ePointerUp ||
           aEvent->mMessage == ePointerCancel) {
         // Implicitly releasing capture for given pointer.
         // ePointerLostCapture should be send after ePointerUp or
         // ePointerCancel.
         WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
         MOZ_ASSERT(pointerEvent);
-        nsIPresShell::ReleasePointerCapturingContent(pointerEvent->pointerId);
-        nsIPresShell::CheckPointerCaptureState(pointerEvent);
+        PointerEventHandler::ReleasePointerCaptureById(pointerEvent->pointerId);
+        PointerEventHandler::CheckPointerCaptureState(pointerEvent);
       }
 
       // 3. Give event to event manager for post event state changes and
       //    generation of synthetic events.
       if (!mIsDestroying && NS_SUCCEEDED(rv)) {
         rv = manager->PostHandleEvent(mPresContext, aEvent,
                                       GetCurrentEventFrame(), aStatus);
       }
@@ -8315,55 +7942,17 @@ PresShell::HandleEventInternal(WidgetEve
       sLastInputCreated = aEvent->mTimeStamp;
     }
     sLastInputProcessed = now;
   }
 
   return rv;
 }
 
-/* static */ void
-nsIPresShell::DispatchGotOrLostPointerCaptureEvent(
-                bool aIsGotCapture,
-                const WidgetPointerEvent* aPointerEvent,
-                nsIContent* aCaptureTarget)
-{
-  nsIDocument* targetDoc = aCaptureTarget->OwnerDoc();
-  nsCOMPtr<nsIPresShell> shell = targetDoc->GetShell();
-  NS_ENSURE_TRUE_VOID(shell);
-
-  if (!aIsGotCapture && !aCaptureTarget->IsInUncomposedDoc()) {
-    // If the capturing element was removed from the DOM tree, fire
-    // ePointerLostCapture at the document.
-    PointerEventInit init;
-    init.mPointerId = aPointerEvent->pointerId;
-    init.mBubbles = true;
-    init.mComposed = true;
-    ConvertPointerTypeToString(aPointerEvent->inputSource, init.mPointerType);
-    init.mIsPrimary = aPointerEvent->mIsPrimary;
-    RefPtr<mozilla::dom::PointerEvent> event;
-    event = PointerEvent::Constructor(aCaptureTarget,
-                                      NS_LITERAL_STRING("lostpointercapture"),
-                                      init);
-    bool dummy;
-    targetDoc->DispatchEvent(event->InternalDOMEvent(), &dummy);
-    return;
-  }
-  nsEventStatus status = nsEventStatus_eIgnore;
-  WidgetPointerEvent localEvent(aPointerEvent->IsTrusted(),
-                                aIsGotCapture ? ePointerGotCapture :
-                                                ePointerLostCapture,
-                                aPointerEvent->mWidget);
-  localEvent.AssignPointerEventData(*aPointerEvent, true);
-  nsresult rv = shell->HandleEventWithTarget(
-                         &localEvent,
-                         aCaptureTarget->GetPrimaryFrame(),
-                         aCaptureTarget, &status);
-  NS_ENSURE_SUCCESS_VOID(rv);
-}
+
 
 nsresult
 PresShell::DispatchEventToDOM(WidgetEvent* aEvent,
                               nsEventStatus* aStatus,
                               nsPresShellEventCB* aEventCB)
 {
   nsresult rv = NS_OK;
   nsCOMPtr<nsINode> eventTarget = mCurrentEventContent.get();
@@ -10882,33 +10471,16 @@ nsIPresShell::IsAccessibilityActive()
 
 nsAccessibilityService*
 nsIPresShell::AccService()
 {
   return GetAccService();
 }
 #endif
 
-void nsIPresShell::InitializeStatics()
-{
-  MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
-  sPointerCaptureList =
-    new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
-  sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
-}
-
-void nsIPresShell::ReleaseStatics()
-{
-  MOZ_ASSERT(sPointerCaptureList, "ReleaseStatics called without Initialize!");
-  delete sPointerCaptureList;
-  sPointerCaptureList = nullptr;
-  delete sActivePointersIds;
-  sActivePointersIds = nullptr;
-}
-
 // Asks our docshell whether we're active.
 void PresShell::QueryIsActive()
 {
   nsCOMPtr<nsISupports> container = mPresContext->GetContainerWeak();
   if (mDocument) {
     nsIDocument* displayDoc = mDocument->GetDisplayDocument();
     if (displayDoc) {
       // Ok, we're an external resource document -- we need to use our display
--- a/layout/base/PresShell.h
+++ b/layout/base/PresShell.h
@@ -729,19 +729,16 @@ protected:
   virtual void SysColorChanged() override { mPresContext->SysColorChanged(); }
   virtual void ThemeChanged() override { mPresContext->ThemeChanged(); }
   virtual void BackingScaleFactorChanged() override { mPresContext->UIResolutionChanged(); }
   virtual nsIDocument* GetPrimaryContentDocument() override;
 
   virtual void PausePainting() override;
   virtual void ResumePainting() override;
 
-  void UpdateActivePointerState(mozilla::WidgetGUIEvent* aEvent);
-
-
   //////////////////////////////////////////////////////////////////////////////
   // Approximate frame visibility tracking implementation.
   //////////////////////////////////////////////////////////////////////////////
 
   void UpdateApproximateFrameVisibility();
   void DoUpdateApproximateFrameVisibility(bool aRemoveOnly);
 
   void ClearApproximatelyVisibleFramesList(const Maybe<mozilla::OnNonvisible>& aNonvisibleAction
--- a/layout/base/nsIPresShell.h
+++ b/layout/base/nsIPresShell.h
@@ -1243,86 +1243,16 @@ public:
   bool IsActive()
   {
     return mIsActive;
   }
 
   // mouse capturing
   static CapturingContentInfo gCaptureInfo;
 
-  class PointerCaptureInfo final
-  {
-  public:
-    nsCOMPtr<nsIContent> mPendingContent;
-    nsCOMPtr<nsIContent> mOverrideContent;
-
-    explicit PointerCaptureInfo(nsIContent* aPendingContent)
-      : mPendingContent(aPendingContent)
-    {
-      MOZ_COUNT_CTOR(PointerCaptureInfo);
-    }
-
-    ~PointerCaptureInfo()
-    {
-      MOZ_COUNT_DTOR(PointerCaptureInfo);
-    }
-
-    bool Empty()
-    {
-      return !(mPendingContent || mOverrideContent);
-    }
-  };
-
-  class PointerInfo final
-  {
-  public:
-    uint16_t mPointerType;
-    bool mActiveState;
-    bool mPrimaryState;
-    bool mPreventMouseEventByContent;
-    explicit PointerInfo(bool aActiveState, uint16_t aPointerType,
-                         bool aPrimaryState)
-      : mPointerType(aPointerType)
-      , mActiveState(aActiveState)
-      , mPrimaryState(aPrimaryState)
-      , mPreventMouseEventByContent(false)
-    {
-    }
-  };
-
-  static void DispatchGotOrLostPointerCaptureEvent(
-                bool aIsGotCapture,
-                const mozilla::WidgetPointerEvent* aPointerEvent,
-                nsIContent* aCaptureTarget);
-
-  static PointerCaptureInfo* GetPointerCaptureInfo(uint32_t aPointerId);
-  static void SetPointerCapturingContent(uint32_t aPointerId,
-                                         nsIContent* aContent);
-  static void ReleasePointerCapturingContent(uint32_t aPointerId);
-  static nsIContent* GetPointerCapturingContent(uint32_t aPointerId);
-
-  // CheckPointerCaptureState checks cases, when got/lostpointercapture events
-  // should be fired.
-  static void CheckPointerCaptureState(
-                const mozilla::WidgetPointerEvent* aPointerEvent);
-
-  // GetPointerInfo returns true if pointer with aPointerId is situated in
-  // device, false otherwise.
-  // aActiveState is additional information, which shows state of pointer like
-  // button state for mouse.
-  static bool GetPointerInfo(uint32_t aPointerId, bool& aActiveState);
-
-  // GetPointerType returns pointer type like mouse, pen or touch for pointer
-  // event with pointerId
-  static uint16_t GetPointerType(uint32_t aPointerId);
-
-  // GetPointerPrimaryState returns state of attribute isPrimary for pointer
-  // event with pointerId
-  static bool GetPointerPrimaryState(uint32_t aPointerId);
-
   /**
    * When capturing content is set, it traps all mouse events and retargets
    * them at this content node. If capturing is not allowed
    * (gCaptureInfo.mAllowed is false), then capturing is not set. However, if
    * the CAPTURE_IGNOREALLOWED flag is set, the allowed state is ignored and
    * capturing is set regardless. To disable capture, pass null for the value
    * of aContent.
    *
@@ -1665,22 +1595,16 @@ public:
   bool AddRefreshObserver(nsARefreshObserver* aObserver,
                           mozilla::FlushType aFlushType);
   bool RemoveRefreshObserver(nsARefreshObserver* aObserver,
                              mozilla::FlushType aFlushType);
 
   virtual bool AddPostRefreshObserver(nsAPostRefreshObserver* aObserver);
   virtual bool RemovePostRefreshObserver(nsAPostRefreshObserver* aObserver);
 
-  /**
-   * Initialize and shut down static variables.
-   */
-  static void InitializeStatics();
-  static void ReleaseStatics();
-
   // If a frame in the subtree rooted at aFrame is capturing the mouse then
   // clears that capture.
   static void ClearMouseCapture(nsIFrame* aFrame);
 
   void SetScrollPositionClampingScrollPortSize(nscoord aWidth, nscoord aHeight);
   bool IsScrollPositionClampingScrollPortSizeSet() {
     return mScrollPositionClampingScrollPortSizeSet;
   }
--- a/layout/build/nsLayoutStatics.cpp
+++ b/layout/build/nsLayoutStatics.cpp
@@ -119,16 +119,17 @@
 #include "MediaDecoder.h"
 #include "MediaPrefs.h"
 #include "mozilla/ServoBindings.h"
 #include "mozilla/StaticPresData.h"
 #include "mozilla/StylePrefs.h"
 #include "mozilla/dom/WebIDLGlobalNameHash.h"
 #include "mozilla/dom/ipc/IPCBlobInputStreamStorage.h"
 #include "mozilla/dom/U2FTokenManager.h"
+#include "mozilla/dom/PointerEventHandler.h"
 
 using namespace mozilla;
 using namespace mozilla::net;
 using namespace mozilla::dom;
 using namespace mozilla::dom::ipc;
 
 nsrefcnt nsLayoutStatics::sLayoutStaticRefcnt = 0;
 
@@ -254,17 +255,17 @@ nsLayoutStatics::Initialize()
   AsyncLatencyLogger::InitializeStatics();
   MediaManager::StartupInit();
   CubebUtils::InitLibrary();
 
   nsContentSink::InitializeStatics();
   nsHtml5Module::InitializeStatics();
   mozilla::dom::FallbackEncoding::Initialize();
   nsLayoutUtils::Initialize();
-  nsIPresShell::InitializeStatics();
+  PointerEventHandler::InitializeStatics();
   TouchManager::InitializeStatics();
   ContentPrincipal::InitializeStatics();
 
   nsCORSListenerProxy::Startup();
 
   nsWindowMemoryReporter::Init();
 
   SVGElementFactory::Init();
@@ -395,17 +396,17 @@ nsLayoutStatics::Shutdown()
   WebAudioUtils::Shutdown();
 
 #ifdef MOZ_WEBSPEECH
   nsSynthVoiceRegistry::Shutdown();
 #endif
 
   nsCORSListenerProxy::Shutdown();
 
-  nsIPresShell::ReleaseStatics();
+  PointerEventHandler::ReleaseStatics();
 
   TouchManager::ReleaseStatics();
 
   nsTreeSanitizer::ReleaseStatics();
 
   nsHtml5Module::ReleaseStatics();
 
   mozilla::dom::FallbackEncoding::Shutdown();