tor-browser

The Tor Browser
git clone https://git.dasho.dev/tor-browser.git
Log | Files | Refs | README | LICENSE

PointerEventHandler.cpp (62128B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "PointerEventHandler.h"
      8 
      9 #include "EventStateManager.h"
     10 #include "PointerEvent.h"
     11 #include "PointerLockManager.h"
     12 #include "mozilla/ConnectedAncestorTracker.h"
     13 #include "mozilla/PresShell.h"
     14 #include "mozilla/StaticPrefs_dom.h"
     15 #include "mozilla/StaticPrefs_ui.h"
     16 #include "mozilla/dom/BrowserChild.h"
     17 #include "mozilla/dom/BrowserParent.h"
     18 #include "mozilla/dom/Document.h"
     19 #include "mozilla/dom/DocumentInlines.h"
     20 #include "mozilla/dom/MouseEventBinding.h"
     21 #include "nsIContentInlines.h"
     22 #include "nsIFrame.h"
     23 #include "nsIWeakReferenceUtils.h"
     24 #include "nsRFPService.h"
     25 #include "nsUserCharacteristics.h"
     26 
     27 namespace mozilla {
     28 // MouseLocation logs the mouse location and when/where enqueued synthesized
     29 // mouse move is flushed.  If you don't need all mouse location recording at
     30 // eMouseMove, you can use MouseLocation:3,sync.  Then, it's logged once per
     31 // 50 times.  Otherwise, if you need to log all eMouseMove locations, you can
     32 // use MouseLocation:5,sync.
     33 // Note that this is actually available only on debug builds for saving the
     34 // runtime cost on opt builds.
     35 LazyLogModule gLogMouseLocation("MouseLocation");
     36 // PointerLocation logs all pointer locations and when/where enqueued
     37 // synthesized pointer move is flushed.  If you don't need all pointer location
     38 // recording at ePointerMove, you can use PointerLocation:3,sync.  Then, it's
     39 // logged once per 50 times.  Otherwise, if you need to log all ePointerMove
     40 // locations, you can use PointerLocation:5,sync.
     41 // Note that this is actually available only on debug builds for saving the
     42 // runtime cost on opt builds.
     43 LazyLogModule gLogPointerLocation("PointerLocation");
     44 // Log the updates of sActivePointersIds.
     45 LazyLogModule gLogActivePointers("ActivePointers");
     46 
     47 using namespace dom;
     48 
     49 Maybe<int32_t> PointerEventHandler::sSpoofedPointerId;
     50 StaticAutoPtr<PointerInfo> PointerEventHandler::sLastMouseInfo;
     51 StaticRefPtr<nsIWeakReference> PointerEventHandler::sLastMousePresShell;
     52 Maybe<uint32_t> PointerEventHandler::sLastPointerId;
     53 
     54 // Keeps a map between pointerId and element that currently capturing pointer
     55 // with such pointerId. If pointerId is absent in this map then nobody is
     56 // capturing it. Additionally keep information about pending capturing content.
     57 static nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>*
     58    sPointerCaptureList;
     59 
     60 // Keeps information about pointers such as pointerId, activeState, pointerType,
     61 // primaryState
     62 static nsClassHashtable<nsUint32HashKey, PointerInfo>* sActivePointersIds;
     63 
     64 const UniquePtr<PointerInfo>& PointerEventHandler::InsertOrUpdateActivePointer(
     65    uint32_t aPointerId, UniquePtr<PointerInfo>&& aNewPointerInfo,
     66    EventMessage aEventMessage, const char* aCallerName) {
     67  const bool logIt = [&]() {
     68    if (MOZ_LIKELY(!MOZ_LOG_TEST(gLogActivePointers, LogLevel::Info))) {
     69      return false;
     70    }
     71    const PointerInfo* prevPointerInfo = sActivePointersIds->Get(aPointerId);
     72    return !prevPointerInfo ||
     73           !prevPointerInfo->EqualsBasicPointerData(*aNewPointerInfo);
     74  }();
     75 
     76  const UniquePtr<PointerInfo>& pointerInfo =
     77      sActivePointersIds->InsertOrUpdate(
     78          aPointerId, std::forward<UniquePtr<PointerInfo>>(aNewPointerInfo));
     79  if (MOZ_UNLIKELY(logIt)) {
     80    MOZ_LOG(
     81        gLogActivePointers, LogLevel::Info,
     82        ("InsertOrUpdate: { pointerId=%u, active: %s, inputSource: %s, "
     83         "primary: %s, fromTouchEvent: %s, synthesizedForTests: %s }, %s in "
     84         "%s",
     85         aPointerId, pointerInfo->mIsActive ? "Yes" : "No",
     86         InputSourceToString(pointerInfo->mInputSource).get(),
     87         pointerInfo->mIsPrimary ? "Yes" : "No",
     88         pointerInfo->mFromTouchEvent ? "Yes" : "No",
     89         pointerInfo->mIsSynthesizedForTests ? "Yes" : "No",
     90         ToChar(aEventMessage), aCallerName));
     91  }
     92  return pointerInfo;
     93 }
     94 
     95 void PointerEventHandler::RemoveActivePointer(uint32_t aPointerId,
     96                                              EventMessage aEventMessage,
     97                                              const char* aCallerName) {
     98  MOZ_ASSERT_IF(sLastPointerId, *sLastPointerId != aPointerId);
     99 
    100  sActivePointersIds->Remove(aPointerId);
    101  MOZ_LOG(
    102      gLogActivePointers, LogLevel::Info,
    103      ("Remove: { pointerId=%u }, %s in %s, remaining %u pointers", aPointerId,
    104       ToChar(aEventMessage), aCallerName, sActivePointersIds->Count()));
    105 }
    106 
    107 // Keeps track of which BrowserParent requested pointer capture for a pointer
    108 // id.
    109 static nsTHashMap<nsUint32HashKey, BrowserParent*>*
    110    sPointerCaptureRemoteTargetTable = nullptr;
    111 
    112 // Keep the capturing element at dispatching the last pointer up event to
    113 // consider the following click, auxclick or contextmenu event target.
    114 static StaticRefPtr<nsIWeakReference>
    115    sPointerCapturingElementAtLastPointerUpEvent;
    116 
    117 /* static */
    118 void PointerEventHandler::InitializeStatics() {
    119  MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
    120  sPointerCaptureList =
    121      new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
    122  sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
    123  if (XRE_IsParentProcess()) {
    124    sPointerCaptureRemoteTargetTable =
    125        new nsTHashMap<nsUint32HashKey, BrowserParent*>;
    126  }
    127 }
    128 
    129 /* static */
    130 void PointerEventHandler::ReleaseStatics() {
    131  MOZ_ASSERT(sPointerCaptureList, "ReleaseStatics called without Initialize!");
    132  delete sPointerCaptureList;
    133  sPointerCaptureList = nullptr;
    134  delete sActivePointersIds;
    135  sActivePointersIds = nullptr;
    136  sPointerCapturingElementAtLastPointerUpEvent = nullptr;
    137  if (sPointerCaptureRemoteTargetTable) {
    138    MOZ_ASSERT(XRE_IsParentProcess());
    139    delete sPointerCaptureRemoteTargetTable;
    140    sPointerCaptureRemoteTargetTable = nullptr;
    141  }
    142  sLastMouseInfo = nullptr;
    143  sLastMousePresShell = nullptr;
    144 }
    145 
    146 /* static */
    147 bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
    148  return StaticPrefs::dom_w3c_pointer_events_implicit_capture();
    149 }
    150 
    151 /* static */
    152 bool PointerEventHandler::ShouldDispatchClickEventOnCapturingElement(
    153    const WidgetGUIEvent* aSourceEvent /* = nullptr */) {
    154  if (!StaticPrefs::
    155          dom_w3c_pointer_events_dispatch_click_on_pointer_capturing_element()) {
    156    return false;
    157  }
    158  if (!aSourceEvent ||
    159      !StaticPrefs::
    160          dom_w3c_pointer_events_dispatch_click_on_pointer_capturing_element_except_touch()) {
    161    return true;
    162  }
    163  MOZ_ASSERT(aSourceEvent->mMessage == eMouseUp ||
    164             aSourceEvent->mMessage == ePointerUp ||
    165             aSourceEvent->mMessage == eTouchEnd);
    166  // Pointer Events defines that `click` event's userEvent is the preceding
    167  // `pointerup`.  However, Chrome does not follow treat it as so when the
    168  // `click` is caused by a tap.  For the compatibility with Chrome, we should
    169  // stop comforming to the spec until Chrome conforms to that.
    170  if (aSourceEvent->mClass == eTouchEventClass) {
    171    return false;
    172  }
    173  const WidgetMouseEvent* const sourceMouseEvent = aSourceEvent->AsMouseEvent();
    174  return sourceMouseEvent &&
    175         sourceMouseEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH;
    176 }
    177 
    178 /* static */
    179 void PointerEventHandler::RecordPointerState(
    180    const nsPoint& aRefPoint, const WidgetMouseEvent& aMouseEvent) {
    181  MOZ_ASSERT_IF(aMouseEvent.mMessage == eMouseMove ||
    182                    aMouseEvent.mMessage == ePointerMove,
    183                aMouseEvent.IsReal());
    184 
    185  PointerInfo* pointerInfo = sActivePointersIds->Get(aMouseEvent.pointerId);
    186  if (!pointerInfo) {
    187    // If there is no pointer info (i.e., no last pointer state too) and the
    188    // input device is not stationary or the caller wants to clear the last
    189    // state, we need to do nothing.
    190    if (!aMouseEvent.InputSourceSupportsHover() ||
    191        aRefPoint == nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
    192      return;
    193    }
    194    // If there is no PointerInfo, we need to add an inactive PointeInfo to
    195    // store the state.
    196    pointerInfo = InsertOrUpdateActivePointer(
    197                      aMouseEvent.pointerId,
    198                      MakeUnique<PointerInfo>(
    199                          PointerInfo::Active::No, aMouseEvent.mInputSource,
    200                          PointerInfo::Primary::Yes,
    201                          PointerInfo::FromTouchEvent::No, nullptr, nullptr,
    202                          static_cast<PointerInfo::SynthesizeForTests>(
    203                              aMouseEvent.mFlags.mIsSynthesizedForTests)),
    204                      aMouseEvent.mMessage, __func__)
    205                      .get();
    206  }
    207  // If the input source is a stationary device and the point is defined, we may
    208  // need to dispatch synthesized ePointerMove at the pointer later.  So, in
    209  // that case, we should store the data.
    210  if (aMouseEvent.InputSourceSupportsHover() &&
    211      aRefPoint != nsPoint(NS_UNCONSTRAINEDSIZE, NS_UNCONSTRAINEDSIZE)) {
    212    pointerInfo->RecordLastState(aRefPoint, aMouseEvent);
    213 #ifdef DEBUG
    214    if (MOZ_LOG_TEST(gLogPointerLocation, LogLevel::Info)) {
    215      static uint32_t sFrequentMessageCount = 0;
    216      const bool isFrequentMessage = aMouseEvent.mMessage == ePointerMove;
    217      if (!isFrequentMessage ||
    218          MOZ_LOG_TEST(gLogPointerLocation, LogLevel::Verbose) ||
    219          !(sFrequentMessageCount % 50)) {
    220        MOZ_LOG(
    221            gLogPointerLocation,
    222            isFrequentMessage ? LogLevel::Debug : LogLevel::Info,
    223            ("got %s on widget:%p at {%d, %d} (pointerId=%u, source=%s)\n",
    224             ToChar(aMouseEvent.mMessage), aMouseEvent.mWidget.get(),
    225             sLastMouseInfo->mLastRefPointInRootDoc.x,
    226             sLastMouseInfo->mLastRefPointInRootDoc.y, aMouseEvent.pointerId,
    227             InputSourceToString(aMouseEvent.mInputSource).get()));
    228      }
    229      if (isFrequentMessage) {
    230        sFrequentMessageCount++;
    231      } else {
    232        // Let's log the next ePointerMove after the other messages.
    233        sFrequentMessageCount = 0;
    234      }
    235    }
    236 #endif  // #ifdef DEBUG
    237  }
    238  // Otherwise, i.e., if it's not a stationary device or the caller wants to
    239  // forget the point, we should clear the last position to abort to synthesize
    240  // ePointerMove.
    241  else {
    242    pointerInfo->ClearLastState();
    243    MOZ_LOG_DEBUG_ONLY(
    244        gLogPointerLocation, LogLevel::Info,
    245        ("got %s on widget:%p, pointer location is cleared (pointerId=%u, "
    246         "source=%s)\n",
    247         ToChar(aMouseEvent.mMessage), aMouseEvent.mWidget.get(),
    248         aMouseEvent.pointerId,
    249         InputSourceToString(aMouseEvent.mInputSource).get()));
    250  }
    251 }
    252 
    253 /* static */
    254 void PointerEventHandler::RecordMouseState(
    255    PresShell& aRootPresShell, const WidgetMouseEvent& aMouseEvent) {
    256  MOZ_ASSERT(aRootPresShell.IsRoot());
    257  if (!sLastMouseInfo) {
    258    sLastMouseInfo = new PointerInfo();
    259  }
    260  sLastMousePresShell = do_GetWeakReference(&aRootPresShell);
    261  sLastMouseInfo->mLastRefPointInRootDoc =
    262      aRootPresShell.GetEventLocation(aMouseEvent);
    263  sLastMouseInfo->mLastTargetGuid =
    264      layers::InputAPZContext::GetTargetLayerGuid();
    265  // FIXME: Don't trust the synthesized for tests flag of drag events.
    266  if (aMouseEvent.mClass != eDragEventClass) {
    267    sLastMouseInfo->mInputSource = aMouseEvent.mInputSource;
    268    sLastMouseInfo->mIsSynthesizedForTests =
    269        aMouseEvent.mFlags.mIsSynthesizedForTests;
    270  }
    271 #ifdef DEBUG
    272  if (MOZ_LOG_TEST(gLogMouseLocation, LogLevel::Info)) {
    273    static uint32_t sFrequentMessageCount = 0;
    274    const bool isFrequentMessage =
    275        aMouseEvent.mMessage == eMouseMove || aMouseEvent.mMessage == eDragOver;
    276    if (!isFrequentMessage ||
    277        MOZ_LOG_TEST(gLogMouseLocation, LogLevel::Verbose) ||
    278        !(sFrequentMessageCount % 50)) {
    279      MOZ_LOG(
    280          gLogMouseLocation,
    281          isFrequentMessage ? LogLevel::Debug : LogLevel::Info,
    282          ("[ps=%p]got %s on widget:%p at {%d, %d} (pointerId=%u, source=%s)\n",
    283           &aRootPresShell, ToChar(aMouseEvent.mMessage),
    284           aMouseEvent.mWidget.get(), sLastMouseInfo->mLastRefPointInRootDoc.x,
    285           sLastMouseInfo->mLastRefPointInRootDoc.y, aMouseEvent.pointerId,
    286           InputSourceToString(aMouseEvent.mInputSource).get()));
    287    }
    288    if (isFrequentMessage) {
    289      sFrequentMessageCount++;
    290    } else {
    291      // Let's log the next eMouseMove or eDragOver after the other
    292      // messages.
    293      sFrequentMessageCount = 0;
    294    }
    295  }
    296 #endif  // #ifdef DEBUG
    297 }
    298 
    299 /* static */
    300 void PointerEventHandler::ClearMouseState(PresShell& aRootPresShell,
    301                                          const WidgetMouseEvent& aMouseEvent) {
    302  MOZ_ASSERT(aRootPresShell.IsRoot());
    303  const RefPtr<PresShell> lastMousePresShell =
    304      do_QueryReferent(sLastMousePresShell);
    305  if (lastMousePresShell != &aRootPresShell) {
    306    return;
    307  }
    308  sLastMouseInfo->ClearLastState();
    309  sLastMouseInfo->mLastTargetGuid =
    310      layers::InputAPZContext::GetTargetLayerGuid();
    311  sLastMouseInfo->mInputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
    312  sLastMouseInfo->mIsSynthesizedForTests =
    313      aMouseEvent.mFlags.mIsSynthesizedForTests;
    314  MOZ_LOG_DEBUG_ONLY(gLogMouseLocation, LogLevel::Info,
    315                     ("[ps=%p]got %s on widget:%p, mouse location is cleared "
    316                      "(pointerId=%u, source=%s)\n",
    317                      &aRootPresShell, ToChar(aMouseEvent.mMessage),
    318                      aMouseEvent.mWidget.get(), aMouseEvent.pointerId,
    319                      InputSourceToString(aMouseEvent.mInputSource).get()));
    320 }
    321 
    322 /* static */
    323 LazyLogModule& PointerEventHandler::MouseLocationLogRef() {
    324  return gLogMouseLocation;
    325 }
    326 
    327 /* static */
    328 LazyLogModule& PointerEventHandler::PointerLocationLogRef() {
    329  return gLogPointerLocation;
    330 }
    331 
    332 /* static */
    333 void PointerEventHandler::UpdatePointerActiveState(WidgetMouseEvent* aEvent,
    334                                                   nsIContent* aTargetContent) {
    335  if (!aEvent) {
    336    return;
    337  }
    338  switch (aEvent->mMessage) {
    339    case eMouseEnterIntoWidget: {
    340      const PointerInfo* const pointerInfo = GetPointerInfo(aEvent->pointerId);
    341      if (aEvent->mFlags.mIsSynthesizedForTests) {
    342        if (pointerInfo && !pointerInfo->mIsSynthesizedForTests) {
    343          // Do not overwrite the PointerInfo which is set by user input with
    344          // synthesized pointer move.
    345          return;
    346        }
    347      }
    348 
    349      // Do not update the last pointerId with eMouseEnterIntoWidget because it
    350      // may be dispatched by widget when it receives a native event which is
    351      // not required, e.g., when the pointer is not moved actually.  Let's
    352      // update sLastPointerId with the following ePointerMove, etc which should
    353      // be dispatched immediately. Note that anyway EventStateManager does not
    354      // handle eMouseEnterIntoWidget directly, it expects that new event is
    355      // coming.
    356 
    357      // In this case we have to know information about available mouse pointers
    358      InsertOrUpdateActivePointer(
    359          aEvent->pointerId,
    360          MakeUnique<PointerInfo>(PointerInfo::Active::No, aEvent->mInputSource,
    361                                  PointerInfo::Primary::Yes,
    362                                  PointerInfo::FromTouchEvent::No, nullptr,
    363                                  pointerInfo,
    364                                  static_cast<PointerInfo::SynthesizeForTests>(
    365                                      aEvent->mFlags.mIsSynthesizedForTests)),
    366          aEvent->mMessage, __func__);
    367      MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId);
    368      break;
    369    }
    370    case ePointerMove: {
    371      if (aEvent->IsReal()) {
    372        UpdateLastPointerId(aEvent->pointerId, aEvent->mMessage);
    373      }
    374      // If the event is a synthesized mouse event, we should register the
    375      // pointerId for the test if the pointer is not there.
    376      if (!aEvent->mFlags.mIsSynthesizedForTests ||
    377          aEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_MOUSE) {
    378        return;
    379      }
    380      const PointerInfo* const pointerInfo = GetPointerInfo(aEvent->pointerId);
    381      if (pointerInfo) {
    382        return;
    383      }
    384      InsertOrUpdateActivePointer(
    385          aEvent->pointerId,
    386          MakeUnique<PointerInfo>(
    387              PointerInfo::Active::No, MouseEvent_Binding::MOZ_SOURCE_MOUSE,
    388              PointerInfo::Primary::Yes, PointerInfo::FromTouchEvent::No,
    389              nullptr, pointerInfo, PointerInfo::SynthesizeForTests::Yes),
    390          aEvent->mMessage, __func__);
    391      return;
    392    }
    393    case ePointerDown:
    394      UpdateLastPointerId(aEvent->pointerId, aEvent->mMessage);
    395      sPointerCapturingElementAtLastPointerUpEvent = nullptr;
    396      // In this case we switch pointer to active state
    397      if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
    398        // XXXedgar, test could possibly synthesize a mousedown event on a
    399        // coordinate outside the browser window and cause aTargetContent to be
    400        // nullptr, not sure if this also happens on real usage.
    401        InsertOrUpdateActivePointer(
    402            pointerEvent->pointerId,
    403            MakeUnique<PointerInfo>(
    404                PointerInfo::Active::Yes, *pointerEvent,
    405                aTargetContent ? aTargetContent->OwnerDoc() : nullptr,
    406                GetPointerInfo(aEvent->pointerId)),
    407            pointerEvent->mMessage, __func__);
    408        MaybeCacheSpoofedPointerID(pointerEvent->mInputSource,
    409                                   pointerEvent->pointerId);
    410      }
    411      break;
    412    case ePointerCancel:
    413      // pointercancel means a pointer is unlikely to continue to produce
    414      // pointer events. In that case, we should turn off active state or remove
    415      // the pointer from active pointers.
    416    case ePointerUp:
    417      // In this case we remove information about pointer or turn off active
    418      // state
    419      if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
    420        if (pointerEvent->mInputSource !=
    421            MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
    422          UpdateLastPointerId(aEvent->pointerId, aEvent->mMessage);
    423          InsertOrUpdateActivePointer(
    424              pointerEvent->pointerId,
    425              MakeUnique<PointerInfo>(PointerInfo::Active::No, *pointerEvent,
    426                                      nullptr,
    427                                      GetPointerInfo(aEvent->pointerId)),
    428              pointerEvent->mMessage, __func__);
    429        } else {
    430          MaybeForgetLastPointerId(aEvent->pointerId, aEvent->mMessage);
    431          // XXX If the PointerInfo is registered with same pointerId as actual
    432          // pointer and the event is synthesized for tests, we unregister the
    433          // pointer unexpectedly here.  However, it should be rare and
    434          // currently, we use only pointerId for the key.  Therefore, we cannot
    435          // do nothing without changing the key.
    436          RemoveActivePointer(aEvent->pointerId, aEvent->mMessage, __func__);
    437        }
    438      }
    439      break;
    440    case eMouseExitFromWidget:
    441      if (aEvent->mFlags.mIsSynthesizedForTests) {
    442        const PointerInfo* const pointerInfo =
    443            GetPointerInfo(aEvent->pointerId);
    444        if (pointerInfo && !pointerInfo->mIsSynthesizedForTests) {
    445          // Do not remove the PointerInfo which is set by user input with
    446          // synthesized pointer move.
    447          return;
    448        }
    449      }
    450      MaybeForgetLastPointerId(aEvent->pointerId, aEvent->mMessage);
    451      // In this case we have to remove information about disappeared mouse
    452      // pointers
    453      RemoveActivePointer(aEvent->pointerId, aEvent->mMessage, __func__);
    454      break;
    455    default:
    456      MOZ_ASSERT_UNREACHABLE("event has invalid type");
    457      break;
    458  }
    459 }
    460 
    461 /* static */
    462 void PointerEventHandler::RequestPointerCaptureById(uint32_t aPointerId,
    463                                                    Element* aElement) {
    464  SetPointerCaptureById(aPointerId, aElement);
    465 
    466  if (BrowserChild* browserChild =
    467          BrowserChild::GetFrom(aElement->OwnerDoc()->GetDocShell())) {
    468    browserChild->SendRequestPointerCapture(
    469        aPointerId,
    470        [aPointerId](bool aSuccess) {
    471          if (!aSuccess) {
    472            PointerEventHandler::ReleasePointerCaptureById(aPointerId);
    473          }
    474        },
    475        [](mozilla::ipc::ResponseRejectReason) {});
    476  }
    477 }
    478 
    479 /* static */
    480 void PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId,
    481                                                Element* aElement) {
    482  MOZ_ASSERT(aElement);
    483  sPointerCaptureList->WithEntryHandle(aPointerId, [&](auto&& entry) {
    484    if (entry) {
    485      entry.Data()->mPendingElement = aElement;
    486    } else {
    487      entry.Insert(MakeUnique<PointerCaptureInfo>(aElement));
    488    }
    489  });
    490 }
    491 
    492 /* static */
    493 PointerCaptureInfo* PointerEventHandler::GetPointerCaptureInfo(
    494    uint32_t aPointerId) {
    495  PointerCaptureInfo* pointerCaptureInfo = nullptr;
    496  sPointerCaptureList->Get(aPointerId, &pointerCaptureInfo);
    497  return pointerCaptureInfo;
    498 }
    499 
    500 /* static */
    501 void PointerEventHandler::ReleasePointerCaptureById(uint32_t aPointerId) {
    502  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
    503  if (pointerCaptureInfo) {
    504    if (Element* pendingElement = pointerCaptureInfo->mPendingElement) {
    505      if (BrowserChild* browserChild = BrowserChild::GetFrom(
    506              pendingElement->OwnerDoc()->GetDocShell())) {
    507        browserChild->SendReleasePointerCapture(aPointerId);
    508      }
    509    }
    510    pointerCaptureInfo->mPendingElement = nullptr;
    511  }
    512 }
    513 
    514 /* static */
    515 void PointerEventHandler::ReleaseAllPointerCapture() {
    516  for (const auto& entry : *sPointerCaptureList) {
    517    PointerCaptureInfo* data = entry.GetWeak();
    518    if (data && data->mPendingElement) {
    519      ReleasePointerCaptureById(entry.GetKey());
    520    }
    521  }
    522 }
    523 
    524 /* static */
    525 bool PointerEventHandler::SetPointerCaptureRemoteTarget(
    526    uint32_t aPointerId, dom::BrowserParent* aBrowserParent) {
    527  MOZ_ASSERT(XRE_IsParentProcess());
    528  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
    529  MOZ_ASSERT(aBrowserParent);
    530 
    531  if (PointerLockManager::GetLockedRemoteTarget()) {
    532    return false;
    533  }
    534 
    535  BrowserParent* currentRemoteTarget =
    536      PointerEventHandler::GetPointerCapturingRemoteTarget(aPointerId);
    537  if (currentRemoteTarget && currentRemoteTarget != aBrowserParent) {
    538    return false;
    539  }
    540 
    541  sPointerCaptureRemoteTargetTable->InsertOrUpdate(aPointerId, aBrowserParent);
    542  return true;
    543 }
    544 
    545 /* static */
    546 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
    547    BrowserParent* aBrowserParent) {
    548  MOZ_ASSERT(XRE_IsParentProcess());
    549  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
    550  MOZ_ASSERT(aBrowserParent);
    551 
    552  sPointerCaptureRemoteTargetTable->RemoveIf([aBrowserParent](
    553                                                 const auto& iter) {
    554    BrowserParent* browserParent = iter.Data();
    555    MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
    556 
    557    return aBrowserParent == browserParent;
    558  });
    559 }
    560 
    561 /* static */
    562 void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
    563    uint32_t aPointerId) {
    564  MOZ_ASSERT(XRE_IsParentProcess());
    565  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
    566 
    567  sPointerCaptureRemoteTargetTable->Remove(aPointerId);
    568 }
    569 
    570 /* static */
    571 BrowserParent* PointerEventHandler::GetPointerCapturingRemoteTarget(
    572    uint32_t aPointerId) {
    573  MOZ_ASSERT(XRE_IsParentProcess());
    574  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
    575 
    576  return sPointerCaptureRemoteTargetTable->Get(aPointerId);
    577 }
    578 
    579 /* static */
    580 void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() {
    581  MOZ_ASSERT(XRE_IsParentProcess());
    582  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
    583 
    584  for (auto iter = sPointerCaptureRemoteTargetTable->Iter(); !iter.Done();
    585       iter.Next()) {
    586    BrowserParent* browserParent = iter.Data();
    587    MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");
    588 
    589    (void)browserParent->SendReleaseAllPointerCapture();
    590    iter.Remove();
    591  }
    592 }
    593 
    594 /* static */
    595 const PointerInfo* PointerEventHandler::GetPointerInfo(uint32_t aPointerId) {
    596  return sActivePointersIds->Get(aPointerId);
    597 }
    598 
    599 /* static */
    600 const PointerInfo* PointerEventHandler::GetLastMouseInfo(
    601    const PresShell* aRootPresShell /* = nullptr */) {
    602  if (!sLastMousePresShell || !sLastMouseInfo) {
    603    return nullptr;
    604  }
    605  if (aRootPresShell) {
    606    const RefPtr<PresShell> lastMousePresShell =
    607        do_QueryReferent(sLastMousePresShell);
    608    if (lastMousePresShell != aRootPresShell) {
    609      return nullptr;
    610    }
    611  }
    612  return sLastMouseInfo;
    613 }
    614 
    615 /* static */
    616 void PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent* aEvent) {
    617  switch (aEvent->mClass) {
    618    case eMouseEventClass:
    619      ProcessPointerCaptureForMouse(aEvent->AsMouseEvent());
    620      break;
    621    case eTouchEventClass:
    622      ProcessPointerCaptureForTouch(aEvent->AsTouchEvent());
    623      break;
    624    default:
    625      break;
    626  }
    627 }
    628 
    629 /* static */
    630 void PointerEventHandler::ProcessPointerCaptureForMouse(
    631    WidgetMouseEvent* aEvent) {
    632  if (!ShouldGeneratePointerEventFromMouse(aEvent)) {
    633    return;
    634  }
    635 
    636  PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId);
    637  if (!info || info->mPendingElement == info->mOverrideElement) {
    638    return;
    639  }
    640  WidgetPointerEvent localEvent(*aEvent);
    641  InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent);
    642  CheckPointerCaptureState(&localEvent);
    643 }
    644 
    645 /* static */
    646 void PointerEventHandler::ProcessPointerCaptureForTouch(
    647    WidgetTouchEvent* aEvent) {
    648  if (!ShouldGeneratePointerEventFromTouch(aEvent)) {
    649    return;
    650  }
    651 
    652  for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
    653    Touch* touch = aEvent->mTouches[i];
    654    if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) {
    655      continue;
    656    }
    657    PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier());
    658    if (!info || info->mPendingElement == info->mOverrideElement) {
    659      continue;
    660    }
    661    WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
    662    InitPointerEventFromTouch(event, *aEvent, *touch);
    663    CheckPointerCaptureState(&event);
    664  }
    665 }
    666 
    667 /* static */
    668 void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) {
    669  // Handle pending pointer capture before any pointer events except
    670  // gotpointercapture / lostpointercapture.
    671  if (!aEvent) {
    672    return;
    673  }
    674  MOZ_ASSERT(aEvent->mClass == ePointerEventClass);
    675 
    676  PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId);
    677 
    678  if (!captureInfo ||
    679      captureInfo->mPendingElement == captureInfo->mOverrideElement) {
    680    return;
    681  }
    682 
    683  const RefPtr<Element> overrideElement = captureInfo->mOverrideElement;
    684  RefPtr<Element> pendingElement = captureInfo->mPendingElement;
    685 
    686  // Update captureInfo before dispatching event since sPointerCaptureList may
    687  // be changed in the pointer event listener.
    688  captureInfo->mOverrideElement = captureInfo->mPendingElement;
    689  if (captureInfo->Empty()) {
    690    sPointerCaptureList->Remove(aEvent->pointerId);
    691    captureInfo = nullptr;
    692  }
    693 
    694  if (overrideElement) {
    695    DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
    696                                         overrideElement);
    697    // A `lostpointercapture` event listener may have removed the new pointer
    698    // capture element from the tree.  Then, we shouldn't dispatch
    699    // `gotpointercapture` on the node.
    700    if (pendingElement && !pendingElement->IsInComposedDoc()) {
    701      // We won't dispatch `gotpointercapture`, so, we should never fire
    702      // `lostpointercapture` on it at processing the next pending pointer
    703      // capture.
    704      if ((captureInfo = GetPointerCaptureInfo(aEvent->pointerId)) &&
    705          captureInfo->mOverrideElement == pendingElement) {
    706        captureInfo->mOverrideElement = nullptr;
    707        if (captureInfo->Empty()) {
    708          sPointerCaptureList->Remove(aEvent->pointerId);
    709          captureInfo = nullptr;
    710        }
    711      }
    712      pendingElement = nullptr;
    713    } else {
    714      captureInfo = nullptr;  // Maybe destroyed
    715    }
    716  }
    717  if (pendingElement) {
    718    DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
    719                                         pendingElement);
    720    captureInfo = nullptr;  // Maybe destroyed
    721  }
    722 
    723  // If nobody captures the pointer and the pointer will not be removed, we need
    724  // to dispatch pointer boundary events if the pointer will keep hovering over
    725  // somewhere even after the pointer is up.
    726  // XXX Do we need to check whether there is new pending pointer capture
    727  // element? But if there is, what should we do?
    728  if (overrideElement && !pendingElement && aEvent->mWidget &&
    729      aEvent->mMessage != ePointerCancel &&
    730      (aEvent->mMessage != ePointerUp || aEvent->InputSourceSupportsHover())) {
    731    aEvent->mSynthesizeMoveAfterDispatch = true;
    732  }
    733 }
    734 
    735 /* static */
    736 void PointerEventHandler::SynthesizeMoveToDispatchBoundaryEvents(
    737    const WidgetMouseEvent* aEvent) {
    738  nsCOMPtr<nsIWidget> widget = aEvent->mWidget;
    739  if (NS_WARN_IF(!widget)) {
    740    return;
    741  }
    742  Maybe<WidgetMouseEvent> mouseMoveEvent;
    743  Maybe<WidgetPointerEvent> pointerMoveEvent;
    744  if (aEvent->mClass == eMouseEventClass) {
    745    mouseMoveEvent.emplace(true, eMouseMove, aEvent->mWidget,
    746                           WidgetMouseEvent::eSynthesized);
    747  } else if (aEvent->mClass == ePointerEventClass) {
    748    pointerMoveEvent.emplace(true, ePointerMove, aEvent->mWidget);
    749    pointerMoveEvent->mReason = WidgetMouseEvent::eSynthesized;
    750 
    751    const WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
    752    MOZ_ASSERT(pointerEvent);
    753    pointerMoveEvent->mIsPrimary = pointerEvent->mIsPrimary;
    754    pointerMoveEvent->mFromTouchEvent = pointerEvent->mFromTouchEvent;
    755    pointerMoveEvent->mWidth = pointerEvent->mWidth;
    756    pointerMoveEvent->mHeight = pointerEvent->mHeight;
    757  } else {
    758    MOZ_ASSERT_UNREACHABLE(
    759        "The event must be WidgetMouseEvent or WidgetPointerEvent");
    760  }
    761  WidgetMouseEvent& event =
    762      mouseMoveEvent ? mouseMoveEvent.ref() : pointerMoveEvent.ref();
    763  event.mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests;
    764  event.mIgnoreCapturingContent = true;
    765  event.mRefPoint = aEvent->mRefPoint;
    766  event.mInputSource = aEvent->mInputSource;
    767  event.mButtons = aEvent->mButtons;
    768  event.mModifiers = aEvent->mModifiers;
    769  event.convertToPointer = false;
    770  event.AssignPointerHelperData(*aEvent);
    771 
    772  // XXX If the pointer is already over a document in different process, we
    773  // cannot synthesize the pointermove/mousemove on the document since
    774  // dispatching events to the parent process is currently allowed only in
    775  // automation.
    776  widget->DispatchEvent(&event);
    777 }
    778 
    779 /* static */
    780 void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
    781                                                   WidgetEvent* aEvent) {
    782  MOZ_ASSERT(aEvent->mMessage == ePointerDown);
    783  if (!aFrame || !IsPointerEventImplicitCaptureForTouchEnabled()) {
    784    return;
    785  }
    786  WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
    787  NS_WARNING_ASSERTION(pointerEvent,
    788                       "Call ImplicitlyCapturePointer with non-pointer event");
    789  if (!pointerEvent->mFromTouchEvent) {
    790    // We only implicitly capture the pointer for touch device.
    791    return;
    792  }
    793  nsIContent* target = aFrame->GetContentForEvent(aEvent);
    794  while (target && !target->IsElement()) {
    795    target = target->GetParent();
    796  }
    797  if (NS_WARN_IF(!target)) {
    798    return;
    799  }
    800  RequestPointerCaptureById(pointerEvent->pointerId, target->AsElement());
    801 }
    802 
    803 /* static */
    804 void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent) {
    805  MOZ_ASSERT(aEvent);
    806  if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
    807    return;
    808  }
    809  WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
    810  ReleasePointerCaptureById(pointerEvent->pointerId);
    811  CheckPointerCaptureState(pointerEvent);
    812 }
    813 
    814 /* static */
    815 void PointerEventHandler::MaybeImplicitlyReleasePointerCapture(
    816    WidgetGUIEvent* aEvent) {
    817  MOZ_ASSERT(aEvent);
    818  const EventMessage pointerEventMessage =
    819      PointerEventHandler::ToPointerEventMessage(aEvent);
    820  if (pointerEventMessage != ePointerUp &&
    821      pointerEventMessage != ePointerCancel) {
    822    return;
    823  }
    824  PointerEventHandler::MaybeProcessPointerCapture(aEvent);
    825 }
    826 
    827 /* static */
    828 Element* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId) {
    829  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
    830  if (pointerCaptureInfo) {
    831    return pointerCaptureInfo->mOverrideElement;
    832  }
    833  return nullptr;
    834 }
    835 
    836 /* static */
    837 Element* PointerEventHandler::GetPendingPointerCapturingElement(
    838    uint32_t aPointerId) {
    839  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
    840  if (pointerCaptureInfo) {
    841    return pointerCaptureInfo->mPendingElement;
    842  }
    843  return nullptr;
    844 }
    845 
    846 /* static */
    847 Element* PointerEventHandler::GetPointerCapturingElement(
    848    const WidgetGUIEvent* aEvent) {
    849  return GetPointerCapturingElementInternal(CapturingState::Override, aEvent);
    850 }
    851 
    852 /* static */
    853 Element* PointerEventHandler::GetPendingPointerCapturingElement(
    854    const WidgetGUIEvent* aEvent) {
    855  return GetPointerCapturingElementInternal(CapturingState::Pending, aEvent);
    856 }
    857 
    858 /* static */
    859 Element* PointerEventHandler::GetPointerCapturingElementInternal(
    860    CapturingState aCapturingState, const WidgetGUIEvent* aEvent) {
    861  if ((aEvent->mClass != ePointerEventClass &&
    862       aEvent->mClass != eMouseEventClass) ||
    863      aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) {
    864    // Pointer capture should only be applied to all pointer events and mouse
    865    // events except ePointerDown and eMouseDown;
    866    return nullptr;
    867  }
    868 
    869  // PointerEventHandler may synthesize ePointerMove event before releasing the
    870  // mouse capture (it's done by a default handler of eMouseUp) after handling
    871  // ePointerUp.  Then, we need to dispatch pointer boundary events for the
    872  // element under the pointer to emulate a pointer move after a pointer
    873  // capture.  Therefore, we need to ignore the capturing element if the event
    874  // dispatcher requests it.
    875  if (aEvent->ShouldIgnoreCapturingContent()) {
    876    return nullptr;
    877  }
    878 
    879  const WidgetMouseEvent* const mouseEvent = aEvent->AsMouseEvent();
    880  if (!mouseEvent) {
    881    return nullptr;
    882  }
    883  return aCapturingState == CapturingState::Pending
    884             ? GetPendingPointerCapturingElement(mouseEvent->pointerId)
    885             : GetPointerCapturingElement(mouseEvent->pointerId);
    886 }
    887 
    888 /* static */
    889 RefPtr<Element>
    890 PointerEventHandler::GetPointerCapturingElementAtLastPointerUp() {
    891  return do_QueryReferent(sPointerCapturingElementAtLastPointerUpEvent);
    892 }
    893 
    894 void PointerEventHandler::ReleasePointerCapturingElementAtLastPointerUp() {
    895  sPointerCapturingElementAtLastPointerUpEvent = nullptr;
    896 }
    897 
    898 /* static */
    899 void PointerEventHandler::SetPointerCapturingElementAtLastPointerUp(
    900    nsWeakPtr&& aPointerCapturingElement) {
    901  sPointerCapturingElementAtLastPointerUpEvent =
    902      aPointerCapturingElement.forget();
    903 }
    904 
    905 /* static */
    906 void PointerEventHandler::ReleaseIfCaptureByDescendant(nsIContent* aContent) {
    907  MOZ_ASSERT(aContent);
    908  // We should check that aChild does not contain pointer capturing elements.
    909  // If it does we should release the pointer capture for the elements.
    910  if (!sPointerCaptureList->IsEmpty() && aContent->IsElement()) {
    911    for (const auto& entry : *sPointerCaptureList) {
    912      PointerCaptureInfo* data = entry.GetWeak();
    913      if (data && data->mPendingElement &&
    914          data->mPendingElement->IsInclusiveDescendantOf(aContent)) {
    915        ReleasePointerCaptureById(entry.GetKey());
    916      }
    917    }
    918  }
    919 }
    920 
    921 /* static */
    922 void PointerEventHandler::PreHandlePointerEventsPreventDefault(
    923    WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
    924  if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage == ePointerDown) {
    925    return;
    926  }
    927  PointerInfo* pointerInfo = nullptr;
    928  if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
    929      !pointerInfo) {
    930    // The PointerInfo for active pointer should be added for normal cases. But
    931    // in some cases, we may receive mouse events before adding PointerInfo in
    932    // sActivePointersIds. (e.g. receive mousemove before
    933    // eMouseEnterIntoWidget). In these cases, we could ignore them because they
    934    // are not the events between a DefaultPrevented pointerdown and the
    935    // corresponding pointerup.
    936    return;
    937  }
    938  if (!pointerInfo->mPreventMouseEventByContent) {
    939    return;
    940  }
    941  aMouseOrTouchEvent->PreventDefault(false);
    942  aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
    943  if (aPointerEvent->mMessage == ePointerUp) {
    944    pointerInfo->mPreventMouseEventByContent = false;
    945  }
    946 }
    947 
    948 /* static */
    949 void PointerEventHandler::PostHandlePointerEventsPreventDefault(
    950    WidgetPointerEvent* aPointerEvent, WidgetGUIEvent* aMouseOrTouchEvent) {
    951  if (!aPointerEvent->mIsPrimary || aPointerEvent->mMessage != ePointerDown ||
    952      !aPointerEvent->DefaultPreventedByContent()) {
    953    return;
    954  }
    955  PointerInfo* pointerInfo = nullptr;
    956  if (!sActivePointersIds->Get(aPointerEvent->pointerId, &pointerInfo) ||
    957      !pointerInfo) {
    958    // We already added the PointerInfo for active pointer when
    959    // PresShell::HandleEvent handling pointerdown event.
    960 #ifdef DEBUG
    961    MOZ_CRASH("Got ePointerDown w/o active pointer info!!");
    962 #endif  // #ifdef DEBUG
    963    return;
    964  }
    965  // PreventDefault only applied for active pointers.
    966  if (!pointerInfo->mIsActive) {
    967    return;
    968  }
    969  aMouseOrTouchEvent->PreventDefault(false);
    970  aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
    971  pointerInfo->mPreventMouseEventByContent = true;
    972 }
    973 
    974 /* static */
    975 void PointerEventHandler::InitPointerEventFromMouse(
    976    WidgetPointerEvent* aPointerEvent, const WidgetMouseEvent* aMouseEvent,
    977    EventMessage aMessage) {
    978  MOZ_ASSERT(aPointerEvent);
    979  MOZ_ASSERT(aMouseEvent);
    980  aPointerEvent->pointerId = aMouseEvent->pointerId;
    981  aPointerEvent->mInputSource = aMouseEvent->mInputSource;
    982  aPointerEvent->mMessage = aMessage;
    983  aPointerEvent->mButton = aMouseEvent->mMessage == eMouseMove
    984                               ? MouseButton::eNotPressed
    985                               : aMouseEvent->mButton;
    986 
    987  aPointerEvent->mButtons = aMouseEvent->mButtons;
    988  aPointerEvent->mPressure = aMouseEvent->ComputeMouseButtonPressure();
    989 }
    990 
    991 /* static */
    992 void PointerEventHandler::InitPointerEventFromTouch(
    993    WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent,
    994    const mozilla::dom::Touch& aTouch) {
    995  // Use mButton/mButtons only when mButton got a value (from pen input)
    996  int16_t button = aTouchEvent.mMessage == eTouchRawUpdate ||
    997                           aTouchEvent.mMessage == eTouchMove
    998                       ? MouseButton::eNotPressed
    999                   : aTouchEvent.mButton != MouseButton::eNotPressed
   1000                       ? aTouchEvent.mButton
   1001                       : MouseButton::ePrimary;
   1002  int16_t buttons = aTouchEvent.mMessage == eTouchEnd
   1003                        ? MouseButtonsFlag::eNoButtons
   1004                    : aTouchEvent.mButton != MouseButton::eNotPressed
   1005                        ? aTouchEvent.mButtons
   1006                        : MouseButtonsFlag::ePrimaryFlag;
   1007 
   1008  // XXX: This doesn't support multi pen scenario (bug 1904865)
   1009  if (aTouchEvent.mInputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
   1010    // Only the first touch would be the primary pointer.
   1011    aPointerEvent.mIsPrimary =
   1012        aTouchEvent.mMessage == eTouchStart
   1013            ? !HasActiveTouchPointer()
   1014            : GetPointerPrimaryState(aTouch.Identifier());
   1015  }
   1016  aPointerEvent.pointerId = aTouch.Identifier();
   1017  aPointerEvent.mRefPoint = aTouch.mRefPoint;
   1018  aPointerEvent.mModifiers = aTouchEvent.mModifiers;
   1019  aPointerEvent.mWidth = aTouch.RadiusX(CallerType::System);
   1020  aPointerEvent.mHeight = aTouch.RadiusY(CallerType::System);
   1021  aPointerEvent.tiltX = aTouch.tiltX;
   1022  aPointerEvent.tiltY = aTouch.tiltY;
   1023  aPointerEvent.twist = aTouch.twist;
   1024  aPointerEvent.mTimeStamp = aTouchEvent.mTimeStamp;
   1025  aPointerEvent.mFlags = aTouchEvent.mFlags;
   1026  aPointerEvent.mButton = button;
   1027  aPointerEvent.mButtons = buttons;
   1028  aPointerEvent.mInputSource = aTouchEvent.mInputSource;
   1029  aPointerEvent.mFromTouchEvent = true;
   1030  aPointerEvent.mPressure = aTouch.mForce;
   1031 }
   1032 
   1033 /* static */
   1034 void PointerEventHandler::InitCoalescedEventFromPointerEvent(
   1035    WidgetPointerEvent& aCoalescedEvent,
   1036    const WidgetPointerEvent& aSourceEvent) {
   1037  aCoalescedEvent.mFlags.mCancelable = false;
   1038  aCoalescedEvent.mFlags.mBubbles = false;
   1039 
   1040  aCoalescedEvent.mTimeStamp = aSourceEvent.mTimeStamp;
   1041  aCoalescedEvent.mRefPoint = aSourceEvent.mRefPoint;
   1042  aCoalescedEvent.mModifiers = aSourceEvent.mModifiers;
   1043 
   1044  // WidgetMouseEventBase
   1045  aCoalescedEvent.mButton = aSourceEvent.mButton;
   1046  aCoalescedEvent.mButtons = aSourceEvent.mButtons;
   1047  aCoalescedEvent.mPressure = aSourceEvent.mPressure;
   1048  aCoalescedEvent.mInputSource = aSourceEvent.mInputSource;
   1049 
   1050  // pointerId, tiltX, tiltY, twist, tangentialPressure and convertToPointer.
   1051  aCoalescedEvent.AssignPointerHelperData(aSourceEvent);
   1052 
   1053  // WidgetPointerEvent
   1054  aCoalescedEvent.mWidth = aSourceEvent.mWidth;
   1055  aCoalescedEvent.mHeight = aSourceEvent.mHeight;
   1056  aCoalescedEvent.mIsPrimary = aSourceEvent.mIsPrimary;
   1057  aCoalescedEvent.mFromTouchEvent = aSourceEvent.mFromTouchEvent;
   1058 }
   1059 
   1060 /* static */
   1061 EventMessage PointerEventHandler::ToPointerEventMessage(
   1062    const WidgetGUIEvent* aMouseOrTouchEvent) {
   1063  MOZ_ASSERT(aMouseOrTouchEvent);
   1064 
   1065  switch (aMouseOrTouchEvent->mMessage) {
   1066    case eMouseRawUpdate:
   1067    case eTouchRawUpdate:
   1068      return ePointerRawUpdate;
   1069    case eMouseMove:
   1070      return ePointerMove;
   1071    case eMouseUp:
   1072      return aMouseOrTouchEvent->AsMouseEvent()->mButtons ? ePointerMove
   1073                                                          : ePointerUp;
   1074    case eMouseDown: {
   1075      const WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent();
   1076      return mouseEvent->mButtons & ~nsContentUtils::GetButtonsFlagForButton(
   1077                                        mouseEvent->mButton)
   1078                 ? ePointerMove
   1079                 : ePointerDown;
   1080    }
   1081    case eTouchMove:
   1082      return ePointerMove;
   1083    case eTouchEnd:
   1084      return ePointerUp;
   1085    case eTouchStart:
   1086      return ePointerDown;
   1087    case eTouchCancel:
   1088    case eTouchPointerCancel:
   1089      return ePointerCancel;
   1090    default:
   1091      return eVoidEvent;
   1092  }
   1093 }
   1094 
   1095 /* static */
   1096 bool PointerEventHandler::NeedToDispatchPointerRawUpdate(
   1097    const Document* aDocument) {
   1098  const nsPIDOMWindowInner* const innerWindow =
   1099      aDocument ? aDocument->GetInnerWindow() : nullptr;
   1100  return innerWindow && innerWindow->HasPointerRawUpdateEventListeners() &&
   1101         innerWindow->IsSecureContext();
   1102 }
   1103 
   1104 /* static */
   1105 nsresult PointerEventHandler::DispatchPointerEventWithTarget(
   1106    EventMessage aPointerEventMessage,
   1107    const WidgetMouseEvent& aMouseOrPointerEvent,
   1108    const AutoWeakFrame& aTargetWeakFrame, nsIContent* aTargetContent,
   1109    nsEventStatus* aStatus /* = nullptr */) {
   1110  Maybe<WidgetPointerEvent> pointerEvent;
   1111  if (aMouseOrPointerEvent.mClass == ePointerEventClass) {
   1112    pointerEvent.emplace(aPointerEventMessage,
   1113                         *aMouseOrPointerEvent.AsPointerEvent());
   1114  } else {
   1115    pointerEvent.emplace(aMouseOrPointerEvent);
   1116    PointerEventHandler::InitPointerEventFromMouse(
   1117        pointerEvent.ptr(), &aMouseOrPointerEvent, ePointerCancel);
   1118  }
   1119  pointerEvent->convertToPointer = false;
   1120 
   1121  return DispatchPointerEventWithTarget(pointerEvent.ref(), aTargetWeakFrame,
   1122                                        aTargetContent, aStatus);
   1123 }
   1124 
   1125 /* static */
   1126 nsresult PointerEventHandler::DispatchPointerEventWithTarget(
   1127    EventMessage aPointerEventMessage, const WidgetTouchEvent& aTouchEvent,
   1128    size_t aTouchIndex, const AutoWeakFrame& aTargetWeakFrame,
   1129    nsIContent* aTargetContent, nsEventStatus* aStatus /* = nullptr */) {
   1130  WidgetPointerEvent pointerEvent(aTouchEvent.IsTrusted(), aPointerEventMessage,
   1131                                  aTouchEvent.mWidget);
   1132  PointerEventHandler::InitPointerEventFromTouch(
   1133      pointerEvent, aTouchEvent, *aTouchEvent.mTouches[aTouchIndex]);
   1134  pointerEvent.convertToPointer = false;
   1135 
   1136  return DispatchPointerEventWithTarget(pointerEvent, aTargetWeakFrame,
   1137                                        aTargetContent, aStatus);
   1138 }
   1139 
   1140 /* static */
   1141 nsresult PointerEventHandler::DispatchPointerEventWithTarget(
   1142    WidgetPointerEvent& aPointerEvent, const AutoWeakFrame& aTargetWeakFrame,
   1143    nsIContent* aTargetContent, nsEventStatus* aStatus /* = nullptr */) {
   1144  if (aStatus) {
   1145    *aStatus = nsEventStatus_eIgnore;
   1146  }
   1147 
   1148  AutoWeakFrame targetWeakFrame(aTargetWeakFrame);
   1149  nsCOMPtr<nsIContent> targetContent = aTargetContent;
   1150  if (targetWeakFrame) {
   1151    MOZ_ASSERT_IF(
   1152        targetContent,
   1153        targetContent == targetWeakFrame->GetContentForEvent(&aPointerEvent));
   1154    if (!targetContent) {
   1155      targetContent = targetWeakFrame->GetContentForEvent(&aPointerEvent);
   1156      if (NS_WARN_IF(!targetContent)) {
   1157        return NS_ERROR_FAILURE;
   1158      }
   1159    }
   1160  } else if (NS_WARN_IF(!targetContent)) {
   1161    return NS_ERROR_FAILURE;
   1162  }
   1163  const RefPtr<PresShell> presShell =
   1164      targetWeakFrame ? targetWeakFrame->PresShell()
   1165                      : targetContent->OwnerDoc()->GetPresShell();
   1166  if (NS_WARN_IF(!presShell)) {
   1167    return NS_ERROR_FAILURE;
   1168  }
   1169 
   1170  // If the event is not a gotpointercapture, lostpointercapture, click,
   1171  // auxclick or contextmenu event, run the process pending pointer capture
   1172  // steps for this PointerEvent.
   1173  switch (aPointerEvent.mMessage) {
   1174    case ePointerGotCapture:
   1175    case ePointerLostCapture:
   1176    case ePointerClick:
   1177    case ePointerAuxClick:
   1178    case eContextMenu:
   1179      break;
   1180    default: {
   1181      Maybe<AutoConnectedAncestorTracker> trackTargetContent;
   1182      if (targetContent->IsInComposedDoc()) {
   1183        trackTargetContent.emplace(*targetContent);
   1184      }
   1185      CheckPointerCaptureState(&aPointerEvent);
   1186      // If the event target was disconnected from the document, we should use
   1187      // its connected ancestor as the target of aPointerEvent.
   1188      if (trackTargetContent && trackTargetContent->ContentWasRemoved()) {
   1189        MOZ_ASSERT(!targetWeakFrame);
   1190        targetContent = trackTargetContent->GetConnectedContent();
   1191        if (NS_WARN_IF(!targetContent)) {
   1192          targetWeakFrame = nullptr;
   1193          // FIXME: If we lost the target content with dispatching
   1194          // ePointerGotCapture or ePointerLostCapture event, we may need to
   1195          // retarget the pointer event to the Document.
   1196          return NS_ERROR_FAILURE;
   1197        }
   1198      }
   1199      break;
   1200    }
   1201  }
   1202 
   1203  // The active document of the pointerId and pointer capture for the pointerId
   1204  // should be handled by EventStateManager::PreHandleEvent() immediately before
   1205  // dispatching the event to the DOM.
   1206 
   1207  // Pointer boundary events should be handled by
   1208  // EventStateManager::PreHandleEvent() too.
   1209 
   1210  nsEventStatus dummyStatus = nsEventStatus_eIgnore;
   1211  nsresult rv = presShell->HandleEventWithTarget(
   1212      &aPointerEvent, targetWeakFrame, targetContent,
   1213      aStatus ? aStatus : &dummyStatus);
   1214 
   1215  // EventStateManager must have store the last `pointerover` target at handling
   1216  // the pointer boundary events. So, we need to do nothing here.
   1217 
   1218  return rv;
   1219 }
   1220 
   1221 /* static */
   1222 void PointerEventHandler::DispatchPointerFromMouseOrTouch(
   1223    PresShell* aShell, nsIFrame* aEventTargetFrame,
   1224    nsIContent* aEventTargetContent, Element* aPointerCapturingElement,
   1225    WidgetGUIEvent* aMouseOrTouchEvent, bool aDontRetargetEvents,
   1226    nsEventStatus* aStatus,
   1227    nsIContent** aMouseOrTouchEventTarget /* = nullptr */) {
   1228  MOZ_ASSERT(aEventTargetFrame || aEventTargetContent);
   1229  MOZ_ASSERT(aMouseOrTouchEvent);
   1230 
   1231  nsWeakPtr pointerCapturingElementWeak =
   1232      do_GetWeakReference(aPointerCapturingElement);
   1233  EventMessage pointerMessage = eVoidEvent;
   1234  if (aMouseOrTouchEvent->mClass == eMouseEventClass) {
   1235    WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent();
   1236    // Don't dispatch pointer events caused by a mouse when simulating touch
   1237    // devices in RDM.
   1238    Document* doc = aShell->GetDocument();
   1239    if (!doc) {
   1240      return;
   1241    }
   1242 
   1243    BrowsingContext* bc = doc->GetBrowsingContext();
   1244    if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
   1245        bc->Top()->InRDMPane()) {
   1246      return;
   1247    }
   1248 
   1249    // If it is not mouse then it is likely will come as touch event.
   1250    if (!mouseEvent->convertToPointer) {
   1251      return;
   1252    }
   1253 
   1254    // Normal synthesized mouse move events are marked as "not convert to
   1255    // pointer" by PresShell::ProcessSynthMouseOrPointerMoveEvent().  However:
   1256    // 1. if the event is synthesized via nsIDOMWindowUtils, it's not marked as
   1257    // so because there is no synthesized pointer move dispatcher.  So, we need
   1258    // to dispatch synthesized pointer move from here.  This path may be used by
   1259    // mochitests which check the synthesized mouse/pointer boundary event
   1260    // behavior.
   1261    // 2. if the event comes from another process and our content will be moved
   1262    // underneath the mouse cursor.  In this case, we should handle preceding
   1263    // ePointerMove.
   1264    // FIXME: In the latter case, we may need to synthesize ePointerMove for the
   1265    // other pointers too.
   1266    if (mouseEvent->IsSynthesized()) {
   1267      if (!StaticPrefs::
   1268              dom_event_pointer_boundary_dispatch_when_layout_change() ||
   1269          !mouseEvent->InputSourceSupportsHover()) {
   1270        return;
   1271      }
   1272      // So, if the pointer is captured, we don't need to dispatch pointer
   1273      // boundary events since pointer boundary events should be fired before
   1274      // gotpointercapture.
   1275      PointerCaptureInfo* const captureInfo =
   1276          GetPointerCaptureInfo(mouseEvent->pointerId);
   1277      if (captureInfo && captureInfo->mOverrideElement) {
   1278        return;
   1279      }
   1280    }
   1281 
   1282    pointerMessage = PointerEventHandler::ToPointerEventMessage(mouseEvent);
   1283    if (pointerMessage == eVoidEvent) {
   1284      return;
   1285    }
   1286 #ifdef DEBUG
   1287    if (pointerMessage == ePointerRawUpdate) {
   1288      const nsIContent* const targetContent =
   1289          aEventTargetContent ? aEventTargetContent
   1290                              : aEventTargetFrame->GetContent();
   1291      NS_ASSERTION(targetContent, "Where do we want to try to dispatch?");
   1292      if (targetContent) {
   1293        NS_ASSERTION(
   1294            targetContent->IsInComposedDoc(),
   1295            nsPrintfCString("Do we want to dispatch ePointerRawUpdate onto "
   1296                            "disconnected content? (targetContent=%s)",
   1297                            ToString(*targetContent).c_str())
   1298                .get());
   1299        if (!NeedToDispatchPointerRawUpdate(targetContent->OwnerDoc())) {
   1300          NS_ASSERTION(
   1301              false,
   1302              nsPrintfCString(
   1303                  "Did we fail to retarget the document? (targetContent=%s)",
   1304                  ToString(*targetContent).c_str())
   1305                  .get());
   1306        }
   1307      }
   1308    }
   1309 #endif  // #ifdef DEBUG
   1310    WidgetPointerEvent event(*mouseEvent);
   1311    InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
   1312    event.convertToPointer = mouseEvent->convertToPointer = false;
   1313    RefPtr<PresShell> shell(aShell);
   1314    if (!aEventTargetFrame) {
   1315      shell = PresShell::GetShellForEventTarget(nullptr, aEventTargetContent);
   1316      if (!shell) {
   1317        return;
   1318      }
   1319    }
   1320    PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
   1321    // Dispatch pointer event to the same target which is found by the
   1322    // corresponding mouse event.
   1323    shell->HandleEventWithTarget(&event, aEventTargetFrame, aEventTargetContent,
   1324                                 aStatus, true, aMouseOrTouchEventTarget);
   1325    PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
   1326    // If pointer capture is released, we need to synthesize eMouseMove to
   1327    // dispatch mouse boundary events later.
   1328    mouseEvent->mSynthesizeMoveAfterDispatch |=
   1329        event.mSynthesizeMoveAfterDispatch;
   1330  } else if (aMouseOrTouchEvent->mClass == eTouchEventClass) {
   1331    WidgetTouchEvent* touchEvent = aMouseOrTouchEvent->AsTouchEvent();
   1332    // loop over all touches and dispatch pointer events on each touch
   1333    // copy the event
   1334    pointerMessage = PointerEventHandler::ToPointerEventMessage(touchEvent);
   1335    if (pointerMessage == eVoidEvent) {
   1336      return;
   1337    }
   1338    // If the touch is a single tap release, we will dispatch click or auxclick
   1339    // event later unless it's suppressed.  The event target should be the
   1340    // pointer capturing element right now, i.e., at dispatching ePointerUp.
   1341    // Although we cannot know whether the touch is a single tap here, we should
   1342    // store the last touch pointer capturing element.  If this is not a single
   1343    // tap end, the stored element will be ignored due to not dispatching click
   1344    // nor auxclick.
   1345    if (touchEvent->mMessage == eTouchEnd &&
   1346        touchEvent->mTouches.Length() == 1) {
   1347      MOZ_ASSERT(!pointerCapturingElementWeak);
   1348      pointerCapturingElementWeak = do_GetWeakReference(
   1349          GetPointerCapturingElement(touchEvent->mTouches[0]->Identifier()));
   1350    }
   1351    RefPtr<PresShell> shell(aShell);
   1352    for (uint32_t i = 0; i < touchEvent->mTouches.Length(); ++i) {
   1353      Touch* touch = touchEvent->mTouches[i];
   1354      if (!TouchManager::ShouldConvertTouchToPointer(touch, touchEvent)) {
   1355        continue;
   1356      }
   1357 
   1358      WidgetPointerEvent event(touchEvent->IsTrusted(), pointerMessage,
   1359                               touchEvent->mWidget);
   1360 
   1361      InitPointerEventFromTouch(event, *touchEvent, *touch);
   1362      event.convertToPointer = touch->convertToPointer = false;
   1363      event.mCoalescedWidgetEvents = touch->mCoalescedWidgetEvents;
   1364      if (aMouseOrTouchEvent->mMessage == eTouchStart) {
   1365        // We already did hit test for touchstart in PresShell. We should
   1366        // dispatch pointerdown to the same target as touchstart.
   1367        nsCOMPtr<nsIContent> content =
   1368            nsIContent::FromEventTargetOrNull(touch->mTarget);
   1369        if (!content) {
   1370          continue;
   1371        }
   1372 
   1373        nsIFrame* frame = content->GetPrimaryFrame();
   1374        shell = PresShell::GetShellForEventTarget(frame, content);
   1375        if (!shell) {
   1376          continue;
   1377        }
   1378 
   1379        PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
   1380        shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
   1381                                     aMouseOrTouchEventTarget);
   1382        PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
   1383      } else {
   1384        // We didn't hit test for other touch events. Spec doesn't mention that
   1385        // all pointer events should be dispatched to the same target as their
   1386        // corresponding touch events. Call PresShell::HandleEvent so that we do
   1387        // hit test for pointer events.
   1388        // FIXME: If aDontRetargetEvents is false and the event is fired on
   1389        // different document, we cannot track the pointer event target when
   1390        // it's removed from the tree.
   1391        PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
   1392        shell->HandleEvent(aEventTargetFrame, &event, aDontRetargetEvents,
   1393                           aStatus);
   1394        PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
   1395      }
   1396    }
   1397  }
   1398  // If we dispatched an ePointerUp event while an element capturing the
   1399  // pointer, we should keep storing it to consider click, auxclick and
   1400  // contextmenu event target later.
   1401  if (!aShell->IsDestroying() && pointerMessage == ePointerUp &&
   1402      pointerCapturingElementWeak) {
   1403    SetPointerCapturingElementAtLastPointerUp(
   1404        std::move(pointerCapturingElementWeak));
   1405  }
   1406 }
   1407 
   1408 /* static */
   1409 void PointerEventHandler::NotifyDestroyPresContext(
   1410    nsPresContext* aPresContext) {
   1411  // Clean up pointer capture info
   1412  for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
   1413    PointerCaptureInfo* data = iter.UserData();
   1414    MOZ_ASSERT(data, "how could we have a null PointerCaptureInfo here?");
   1415    if (data->mPendingElement &&
   1416        data->mPendingElement->GetPresContext(Element::eForComposedDoc) ==
   1417            aPresContext) {
   1418      data->mPendingElement = nullptr;
   1419    }
   1420    if (data->mOverrideElement &&
   1421        data->mOverrideElement->GetPresContext(Element::eForComposedDoc) ==
   1422            aPresContext) {
   1423      data->mOverrideElement = nullptr;
   1424    }
   1425    if (data->Empty()) {
   1426      iter.Remove();
   1427    }
   1428  }
   1429  if (const RefPtr<Element> capturingElementAtLastPointerUp =
   1430          GetPointerCapturingElementAtLastPointerUp()) {
   1431    // The pointer capturing element may belong to different document from the
   1432    // destroying nsPresContext. Check whether the composed document's
   1433    // nsPresContext is the destroying one or not.
   1434    if (capturingElementAtLastPointerUp->GetPresContext(
   1435            Element::eForComposedDoc) == aPresContext) {
   1436      ReleasePointerCapturingElementAtLastPointerUp();
   1437    }
   1438  }
   1439  // Clean up active pointer info
   1440  for (auto iter = sActivePointersIds->Iter(); !iter.Done(); iter.Next()) {
   1441    PointerInfo* data = iter.UserData();
   1442    MOZ_ASSERT(data, "how could we have a null PointerInfo here?");
   1443    if (data->mActiveDocument &&
   1444        data->mActiveDocument->GetPresContext() == aPresContext) {
   1445      iter.Remove();
   1446    }
   1447  }
   1448 }
   1449 
   1450 bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
   1451  // We shouldn't start a drag session if the event is synthesized one because
   1452  // aEvent doesn't have enough information for initializing the ePointerCancel.
   1453  if (aEvent.IsSynthesized()) {
   1454    return false;
   1455  }
   1456  // And we should not start with raw update events, which should be used only
   1457  // for notifying web apps of the pointer state changes ASAP.
   1458  if (aEvent.mMessage == ePointerRawUpdate) {
   1459    return false;
   1460  }
   1461  MOZ_ASSERT(aEvent.mMessage != eMouseRawUpdate);
   1462 #ifdef XP_WIN
   1463  if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
   1464    // WM_POINTER does not support drag and drop, see bug 1692277
   1465    return (aEvent.mInputSource != dom::MouseEvent_Binding::MOZ_SOURCE_PEN &&
   1466            aEvent.mReason != WidgetMouseEvent::eSynthesized);  // bug 1692151
   1467  }
   1468 #endif
   1469  return true;
   1470 }
   1471 
   1472 /* static */
   1473 uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId) {
   1474  PointerInfo* pointerInfo = nullptr;
   1475  if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
   1476    return pointerInfo->mInputSource;
   1477  }
   1478  return MouseEvent_Binding::MOZ_SOURCE_UNKNOWN;
   1479 }
   1480 
   1481 /* static */
   1482 bool PointerEventHandler::GetPointerPrimaryState(uint32_t aPointerId) {
   1483  PointerInfo* pointerInfo = nullptr;
   1484  if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
   1485    return pointerInfo->mIsPrimary;
   1486  }
   1487  return false;
   1488 }
   1489 
   1490 /* static */
   1491 bool PointerEventHandler::HasActiveTouchPointer() {
   1492  for (auto iter = sActivePointersIds->ConstIter(); !iter.Done(); iter.Next()) {
   1493    if (iter.Data()->mFromTouchEvent) {
   1494      return true;
   1495    }
   1496  }
   1497  return false;
   1498 }
   1499 
   1500 /* static */
   1501 void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
   1502    bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent,
   1503    Element* aCaptureTarget) {
   1504  // Don't allow uncomposed element to capture a pointer.
   1505  if (NS_WARN_IF(aIsGotCapture && !aCaptureTarget->IsInComposedDoc())) {
   1506    return;
   1507  }
   1508  const OwningNonNull<Document> targetDoc = *aCaptureTarget->OwnerDoc();
   1509  const RefPtr<PresShell> presShell = targetDoc->GetPresShell();
   1510  if (NS_WARN_IF(!presShell || presShell->IsDestroying())) {
   1511    return;
   1512  }
   1513 
   1514  if (!aIsGotCapture && !aCaptureTarget->IsInComposedDoc()) {
   1515    // If the capturing element was removed from the DOM tree, fire
   1516    // ePointerLostCapture at the document.
   1517    PointerEventInit init;
   1518    init.mPointerId = aPointerEvent->pointerId;
   1519    init.mBubbles = true;
   1520    init.mComposed = true;
   1521    ConvertPointerTypeToString(aPointerEvent->mInputSource, init.mPointerType);
   1522    init.mIsPrimary = aPointerEvent->mIsPrimary;
   1523    RefPtr<PointerEvent> event;
   1524    event = PointerEvent::Constructor(aCaptureTarget, u"lostpointercapture"_ns,
   1525                                      init);
   1526    targetDoc->DispatchEvent(*event);
   1527    return;
   1528  }
   1529  nsEventStatus status = nsEventStatus_eIgnore;
   1530  WidgetPointerEvent localEvent(
   1531      aPointerEvent->IsTrusted(),
   1532      aIsGotCapture ? ePointerGotCapture : ePointerLostCapture,
   1533      aPointerEvent->mWidget);
   1534 
   1535  localEvent.AssignPointerEventData(*aPointerEvent, /* aCopyTargets */ true,
   1536                                    /* aCopyCoalescedEvents */ false);
   1537  DebugOnly<nsresult> rv = presShell->HandleEventWithTarget(
   1538      &localEvent, aCaptureTarget->GetPrimaryFrame(), aCaptureTarget, &status);
   1539 
   1540  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   1541                       "DispatchGotOrLostPointerCaptureEvent failed");
   1542 }
   1543 
   1544 /* static */
   1545 void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource,
   1546                                                     uint32_t aPointerId) {
   1547  if (sSpoofedPointerId.isSome() || aInputSource != SPOOFED_POINTER_INTERFACE) {
   1548    return;
   1549  }
   1550 
   1551  sSpoofedPointerId.emplace(aPointerId);
   1552 }
   1553 
   1554 void PointerEventHandler::UpdateLastPointerId(uint32_t aPointerId,
   1555                                              EventMessage aEventMessage) {
   1556  MOZ_ASSERT(aEventMessage != eMouseEnterIntoWidget);
   1557 
   1558  if (sLastPointerId && *sLastPointerId == aPointerId) {
   1559    return;
   1560  }
   1561  MOZ_LOG_DEBUG_ONLY(
   1562      EventStateManager::MouseCursorUpdateLogRef(), LogLevel::Info,
   1563      ("PointerEventHandler::UpdateLastPointerId(): "
   1564       "Last pointerId (%s) is changed to %u when %s",
   1565       ToString(sLastPointerId).c_str(), aPointerId, ToChar(aEventMessage)));
   1566  sLastPointerId = Some(aPointerId);
   1567 }
   1568 
   1569 void PointerEventHandler::MaybeForgetLastPointerId(uint32_t aPointerId,
   1570                                                   EventMessage aEventMessage) {
   1571  if (!sLastPointerId || *sLastPointerId != aPointerId) {
   1572    return;
   1573  }
   1574  sLastPointerId.reset();
   1575  MOZ_LOG_DEBUG_ONLY(EventStateManager::MouseCursorUpdateLogRef(),
   1576                     LogLevel::Info,
   1577                     ("PointerEventHandler::MaybeForgetLastPointerId(): "
   1578                      "Last pointerId (%u) is changed to Nothing when %s",
   1579                      aPointerId, ToChar(aEventMessage)));
   1580 }
   1581 
   1582 }  // namespace mozilla