Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/dom/events/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 34 kB image not shown  

Quelle  PointerEventHandler.cpp   Sprache: C

 
/* -*- 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 "mozilla/EventForwards.h"
#include "nsIContentInlines.h"
#include "nsIFrame.h"
#include "PointerEvent.h"
#include "PointerLockManager.h"
#include "nsRFPService.h"
#include "mozilla/PresShell.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/BrowserParent.h"
#include "mozilla/dom/Document.h"
#include "mozilla/dom/DocumentInlines.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "nsUserCharacteristics.h"

namespace mozilla {

using namespace dom;

Maybe<int32_t> PointerEventHandler::sSpoofedPointerId;

// 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;

// Keeps track of which BrowserParent requested pointer capture for a pointer
// id.
static nsTHashMap<nsUint32HashKey, BrowserParent*>*
    sPointerCaptureRemoteTargetTable = nullptr;

/* static */
void PointerEventHandler::InitializeStatics() {
  MOZ_ASSERT(!sPointerCaptureList, "InitializeStatics called multiple times!");
  sPointerCaptureList =
      new nsClassHashtable<nsUint32HashKey, PointerCaptureInfo>;
  sActivePointersIds = new nsClassHashtable<nsUint32HashKey, PointerInfo>;
  if (XRE_IsParentProcess()) {
    sPointerCaptureRemoteTargetTable =
        new nsTHashMap<nsUint32HashKey, BrowserParent*>;
  }
}

/* static */
void PointerEventHandler::ReleaseStatics() {
  MOZ_ASSERT(sPointerCaptureList, "ReleaseStatics called without Initialize!");
  delete sPointerCaptureList;
  sPointerCaptureList = nullptr;
  delete sActivePointersIds;
  sActivePointersIds = nullptr;
  if (sPointerCaptureRemoteTargetTable) {
    MOZ_ASSERT(XRE_IsParentProcess());
    delete sPointerCaptureRemoteTargetTable;
    sPointerCaptureRemoteTargetTable = nullptr;
  }
}

/* static */
bool PointerEventHandler::IsPointerEventImplicitCaptureForTouchEnabled() {
  return StaticPrefs::dom_w3c_pointer_events_implicit_capture();
}

/* static */
void PointerEventHandler::UpdateActivePointerState(WidgetMouseEvent* aEvent,
                                                   nsIContent* aTargetContent) {
  if (!aEvent) {
    return;
  }
  switch (aEvent->mMessage) {
    case eMouseEnterIntoWidget:
      // In this case we have to know information about available mouse pointers
      sActivePointersIds->InsertOrUpdate(
          aEvent->pointerId,
          MakeUnique<PointerInfo>(false, aEvent->mInputSource, truefalse,
                                  nullptr));

      MaybeCacheSpoofedPointerID(aEvent->mInputSource, aEvent->pointerId);
      break;
    case ePointerDown:
      // In this case we switch pointer to active state
      if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
        // XXXedgar, test could possibly synthesize a mousedown event on a
        // coordinate outside the browser window and cause aTargetContent to be
        // nullptr, not sure if this also happens on real usage.
        sActivePointersIds->InsertOrUpdate(
            pointerEvent->pointerId,
            MakeUnique<PointerInfo>(
                true, pointerEvent->mInputSource, pointerEvent->mIsPrimary,
                pointerEvent->mFromTouchEvent,
                aTargetContent ? aTargetContent->OwnerDoc() : nullptr));
        MaybeCacheSpoofedPointerID(pointerEvent->mInputSource,
                                   pointerEvent->pointerId);
      }
      break;
    case ePointerCancel:
      // pointercancel means a pointer is unlikely to continue to produce
      // pointer events. In that case, we should turn off active state or remove
      // the pointer from active pointers.
    case ePointerUp:
      // In this case we remove information about pointer or turn off active
      // state
      if (WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent()) {
        if (pointerEvent->mInputSource !=
            MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
          sActivePointersIds->InsertOrUpdate(
              pointerEvent->pointerId,
              MakeUnique<PointerInfo>(false, pointerEvent->mInputSource,
                                      pointerEvent->mIsPrimary,
                                      pointerEvent->mFromTouchEvent, nullptr));
        } else {
          sActivePointersIds->Remove(pointerEvent->pointerId);
        }
      }
      break;
    case eMouseExitFromWidget:
      // In this case we have to remove information about disappeared mouse
      // pointers
      sActivePointersIds->Remove(aEvent->pointerId);
      break;
    default:
      MOZ_ASSERT_UNREACHABLE("event has invalid type");
      break;
  }
}

/* static */
void PointerEventHandler::RequestPointerCaptureById(uint32_t aPointerId,
                                                    Element* aElement) {
  SetPointerCaptureById(aPointerId, aElement);

  if (BrowserChild* browserChild =
          BrowserChild::GetFrom(aElement->OwnerDoc()->GetDocShell())) {
    browserChild->SendRequestPointerCapture(
        aPointerId,
        [aPointerId](bool aSuccess) {
          if (!aSuccess) {
            PointerEventHandler::ReleasePointerCaptureById(aPointerId);
          }
        },
        [](mozilla::ipc::ResponseRejectReason) {});
  }
}

/* static */
void PointerEventHandler::SetPointerCaptureById(uint32_t aPointerId,
                                                Element* aElement) {
  MOZ_ASSERT(aElement);
  sPointerCaptureList->WithEntryHandle(aPointerId, [&](auto&& entry) {
    if (entry) {
      entry.Data()->mPendingElement = aElement;
    } else {
      entry.Insert(MakeUnique<PointerCaptureInfo>(aElement));
    }
  });
}

/* 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) {
    if (Element* pendingElement = pointerCaptureInfo->mPendingElement) {
      if (BrowserChild* browserChild = BrowserChild::GetFrom(
              pendingElement->OwnerDoc()->GetDocShell())) {
        browserChild->SendReleasePointerCapture(aPointerId);
      }
    }
    pointerCaptureInfo->mPendingElement = nullptr;
  }
}

/* static */
void PointerEventHandler::ReleaseAllPointerCapture() {
  for (const auto& entry : *sPointerCaptureList) {
    PointerCaptureInfo* data = entry.GetWeak();
    if (data && data->mPendingElement) {
      ReleasePointerCaptureById(entry.GetKey());
    }
  }
}

/* static */
bool PointerEventHandler::SetPointerCaptureRemoteTarget(
    uint32_t aPointerId, dom::BrowserParent* aBrowserParent) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
  MOZ_ASSERT(aBrowserParent);

  if (PointerLockManager::GetLockedRemoteTarget()) {
    return false;
  }

  BrowserParent* currentRemoteTarget =
      PointerEventHandler::GetPointerCapturingRemoteTarget(aPointerId);
  if (currentRemoteTarget && currentRemoteTarget != aBrowserParent) {
    return false;
  }

  sPointerCaptureRemoteTargetTable->InsertOrUpdate(aPointerId, aBrowserParent);
  return true;
}

/* static */
void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
    BrowserParent* aBrowserParent) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);
  MOZ_ASSERT(aBrowserParent);

  sPointerCaptureRemoteTargetTable->RemoveIf([aBrowserParent](
                                                 const auto& iter) {
    BrowserParent* browserParent = iter.Data();
    MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");

    return aBrowserParent == browserParent;
  });
}

/* static */
void PointerEventHandler::ReleasePointerCaptureRemoteTarget(
    uint32_t aPointerId) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);

  sPointerCaptureRemoteTargetTable->Remove(aPointerId);
}

/* static */
BrowserParent* PointerEventHandler::GetPointerCapturingRemoteTarget(
    uint32_t aPointerId) {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);

  return sPointerCaptureRemoteTargetTable->Get(aPointerId);
}

/* static */
void PointerEventHandler::ReleaseAllPointerCaptureRemoteTarget() {
  MOZ_ASSERT(XRE_IsParentProcess());
  MOZ_ASSERT(sPointerCaptureRemoteTargetTable);

  for (auto iter = sPointerCaptureRemoteTargetTable->Iter(); !iter.Done();
       iter.Next()) {
    BrowserParent* browserParent = iter.Data();
    MOZ_ASSERT(browserParent, "Null BrowserParent in pointer captured table?");

    Unused << browserParent->SendReleaseAllPointerCapture();
    iter.Remove();
  }
}

/* static */
const PointerInfo* PointerEventHandler::GetPointerInfo(uint32_t aPointerId) {
  return sActivePointersIds->Get(aPointerId);
}

/* static */
void PointerEventHandler::MaybeProcessPointerCapture(WidgetGUIEvent* aEvent) {
  switch (aEvent->mClass) {
    case eMouseEventClass:
      ProcessPointerCaptureForMouse(aEvent->AsMouseEvent());
      break;
    case eTouchEventClass:
      ProcessPointerCaptureForTouch(aEvent->AsTouchEvent());
      break;
    default:
      break;
  }
}

/* static */
void PointerEventHandler::ProcessPointerCaptureForMouse(
    WidgetMouseEvent* aEvent) {
  if (!ShouldGeneratePointerEventFromMouse(aEvent)) {
    return;
  }

  PointerCaptureInfo* info = GetPointerCaptureInfo(aEvent->pointerId);
  if (!info || info->mPendingElement == info->mOverrideElement) {
    return;
  }
  WidgetPointerEvent localEvent(*aEvent);
  InitPointerEventFromMouse(&localEvent, aEvent, eVoidEvent);
  CheckPointerCaptureState(&localEvent);
}

/* static */
void PointerEventHandler::ProcessPointerCaptureForTouch(
    WidgetTouchEvent* aEvent) {
  if (!ShouldGeneratePointerEventFromTouch(aEvent)) {
    return;
  }

  for (uint32_t i = 0; i < aEvent->mTouches.Length(); ++i) {
    Touch* touch = aEvent->mTouches[i];
    if (!TouchManager::ShouldConvertTouchToPointer(touch, aEvent)) {
      continue;
    }
    PointerCaptureInfo* info = GetPointerCaptureInfo(touch->Identifier());
    if (!info || info->mPendingElement == info->mOverrideElement) {
      continue;
    }
    WidgetPointerEvent event(aEvent->IsTrusted(), eVoidEvent, aEvent->mWidget);
    InitPointerEventFromTouch(event, *aEvent, *touch);
    CheckPointerCaptureState(&event);
  }
}

/* static */
void PointerEventHandler::CheckPointerCaptureState(WidgetPointerEvent* aEvent) {
  // Handle pending pointer capture before any pointer events except
  // gotpointercapture / lostpointercapture.
  if (!aEvent) {
    return;
  }
  MOZ_ASSERT(aEvent->mClass == ePointerEventClass);

  PointerCaptureInfo* captureInfo = GetPointerCaptureInfo(aEvent->pointerId);

  // When fingerprinting resistance is enabled, we need to map other pointer
  // ids into the spoofed one. We don't have to do the mapping if the capture
  // info exists for the non-spoofed pointer id because of we won't allow
  // content to set pointer capture other than the spoofed one. Thus, it must be
  // from chrome if the capture info exists in this case. And we don't have to
  // do anything if the pointer id is the same as the spoofed one.
  if (nsContentUtils::ShouldResistFingerprinting("Efficiency Check",
                                                 RFPTarget::PointerId) &&
      aEvent->pointerId != (uint32_t)GetSpoofedPointerIdForRFP() &&
      !captureInfo) {
    PointerCaptureInfo* spoofedCaptureInfo =
        GetPointerCaptureInfo(GetSpoofedPointerIdForRFP());

    // We need to check the target element's document should resist
    // fingerprinting. If not, we don't need to send a capture event
    // since the capture info of the original pointer id doesn't exist
    // in this case.
    if (!spoofedCaptureInfo || !spoofedCaptureInfo->mPendingElement ||
        !spoofedCaptureInfo->mPendingElement->OwnerDoc()
             ->ShouldResistFingerprinting(RFPTarget::PointerEvents)) {
      return;
    }

    captureInfo = spoofedCaptureInfo;
  }

  if (!captureInfo ||
      captureInfo->mPendingElement == captureInfo->mOverrideElement) {
    return;
  }

  RefPtr<Element> overrideElement = captureInfo->mOverrideElement;
  RefPtr<Element> pendingElement = captureInfo->mPendingElement;

  // Update captureInfo before dispatching event since sPointerCaptureList may
  // be changed in the pointer event listener.
  captureInfo->mOverrideElement = captureInfo->mPendingElement;
  if (captureInfo->Empty()) {
    sPointerCaptureList->Remove(aEvent->pointerId);
  }

  if (overrideElement) {
    DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ false, aEvent,
                                         overrideElement);
  }
  if (pendingElement) {
    DispatchGotOrLostPointerCaptureEvent(/* aIsGotCapture */ true, aEvent,
                                         pendingElement);
  }

  // If nobody captures the pointer and the pointer will not be removed, we need
  // to dispatch pointer boundary events if the pointer will keep hovering over
  // somewhere even after the pointer is up.
  // XXX Do we need to check whether there is new pending pointer capture
  // element? But if there is, what should we do?
  if (overrideElement && !pendingElement && aEvent->mWidget &&
      aEvent->mMessage != ePointerCancel &&
      (aEvent->mMessage != ePointerUp || aEvent->InputSourceSupportsHover())) {
    aEvent->mSynthesizeMoveAfterDispatch = true;
  }
}

/* static */
void PointerEventHandler::SynthesizeMoveToDispatchBoundaryEvents(
    const WidgetMouseEvent* aEvent) {
  nsCOMPtr<nsIWidget> widget = aEvent->mWidget;
  if (NS_WARN_IF(!widget)) {
    return;
  }
  Maybe<WidgetMouseEvent> mouseMoveEvent;
  Maybe<WidgetPointerEvent> pointerMoveEvent;
  if (aEvent->mClass == eMouseEventClass) {
    mouseMoveEvent.emplace(true, eMouseMove, aEvent->mWidget,
                           WidgetMouseEvent::eSynthesized);
  } else if (aEvent->mClass == ePointerEventClass) {
    pointerMoveEvent.emplace(true, ePointerMove, aEvent->mWidget);
    pointerMoveEvent->mReason = WidgetMouseEvent::eSynthesized;

    const WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
    MOZ_ASSERT(pointerEvent);
    pointerMoveEvent->mIsPrimary = pointerEvent->mIsPrimary;
    pointerMoveEvent->mFromTouchEvent = pointerEvent->mFromTouchEvent;
    pointerMoveEvent->mWidth = pointerEvent->mWidth;
    pointerMoveEvent->mHeight = pointerEvent->mHeight;
  } else {
    MOZ_ASSERT_UNREACHABLE(
        "The event must be WidgetMouseEvent or WidgetPointerEvent");
  }
  WidgetMouseEvent& event =
      mouseMoveEvent ? mouseMoveEvent.ref() : pointerMoveEvent.ref();
  event.mFlags.mIsSynthesizedForTests = aEvent->mFlags.mIsSynthesizedForTests;
  event.mIgnoreCapturingContent = true;
  event.mRefPoint = aEvent->mRefPoint;
  event.mInputSource = aEvent->mInputSource;
  event.mButtons = aEvent->mButtons;
  event.mModifiers = aEvent->mModifiers;
  event.convertToPointer = false;
  event.AssignPointerHelperData(*aEvent);

  // XXX If the pointer is already over a document in different process, we
  // cannot synthesize the pointermove/mousemove on the document since
  // dispatching events to the parent process is currently allowed only in
  // automation.
  nsEventStatus eventStatus = nsEventStatus_eIgnore;
  widget->DispatchEvent(&event, eventStatus);
}

/* static */
void PointerEventHandler::ImplicitlyCapturePointer(nsIFrame* aFrame,
                                                   WidgetEvent* aEvent) {
  MOZ_ASSERT(aEvent->mMessage == ePointerDown);
  if (!aFrame || !IsPointerEventImplicitCaptureForTouchEnabled()) {
    return;
  }
  WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
  NS_WARNING_ASSERTION(pointerEvent,
                       "Call ImplicitlyCapturePointer with non-pointer event");
  if (!pointerEvent->mFromTouchEvent) {
    // We only implicitly capture the pointer for touch device.
    return;
  }
  nsCOMPtr<nsIContent> target;
  aFrame->GetContentForEvent(aEvent, getter_AddRefs(target));
  while (target && !target->IsElement()) {
    target = target->GetParent();
  }
  if (NS_WARN_IF(!target)) {
    return;
  }
  RequestPointerCaptureById(pointerEvent->pointerId, target->AsElement());
}

/* static */
void PointerEventHandler::ImplicitlyReleasePointerCapture(WidgetEvent* aEvent) {
  MOZ_ASSERT(aEvent);
  if (aEvent->mMessage != ePointerUp && aEvent->mMessage != ePointerCancel) {
    return;
  }
  WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
  ReleasePointerCaptureById(pointerEvent->pointerId);
  CheckPointerCaptureState(pointerEvent);
}

/* static */
void PointerEventHandler::MaybeImplicitlyReleasePointerCapture(
    WidgetGUIEvent* aEvent) {
  MOZ_ASSERT(aEvent);
  const EventMessage pointerEventMessage =
      PointerEventHandler::ToPointerEventMessage(aEvent);
  if (pointerEventMessage != ePointerUp &&
      pointerEventMessage != ePointerCancel) {
    return;
  }
  PointerEventHandler::MaybeProcessPointerCapture(aEvent);
}

/* static */
Element* PointerEventHandler::GetPointerCapturingElement(uint32_t aPointerId) {
  PointerCaptureInfo* pointerCaptureInfo = GetPointerCaptureInfo(aPointerId);
  if (pointerCaptureInfo) {
    return pointerCaptureInfo->mOverrideElement;
  }
  return nullptr;
}

/* static */
Element* PointerEventHandler::GetPointerCapturingElement(
    WidgetGUIEvent* aEvent) {
  if ((aEvent->mClass != ePointerEventClass &&
       aEvent->mClass != eMouseEventClass) ||
      aEvent->mMessage == ePointerDown || aEvent->mMessage == eMouseDown) {
    // Pointer capture should only be applied to all pointer events and mouse
    // events except ePointerDown and eMouseDown;
    return nullptr;
  }

  // PointerEventHandler may synthesize ePointerMove event before releasing the
  // mouse capture (it's done by a default handler of eMouseUp) after handling
  // ePointerUp.  Then, we need to dispatch pointer boundary events for the
  // element under the pointer to emulate a pointer move after a pointer
  // capture.  Therefore, we need to ignore the capturing element if the event
  // dispatcher requests it.
  if (aEvent->ShouldIgnoreCapturingContent()) {
    return nullptr;
  }

  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
  if (!mouseEvent) {
    return nullptr;
  }
  return GetPointerCapturingElement(mouseEvent->pointerId);
}

/* 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.
  if (!sPointerCaptureList->IsEmpty()) {
    for (const auto& entry : *sPointerCaptureList) {
      PointerCaptureInfo* data = entry.GetWeak();
      if (data && data->mPendingElement &&
          data->mPendingElement->IsInclusiveDescendantOf(aContent)) {
        ReleasePointerCaptureById(entry.GetKey());
      }
    }
  }
}

/* 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). 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);
  aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
  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);
  aMouseOrTouchEvent->mFlags.mOnlyChromeDispatch = true;
  pointerInfo->mPreventMouseEventByContent = true;
}

/* static */
void PointerEventHandler::InitPointerEventFromMouse(
    WidgetPointerEvent* aPointerEvent, WidgetMouseEvent* aMouseEvent,
    EventMessage aMessage) {
  MOZ_ASSERT(aPointerEvent);
  MOZ_ASSERT(aMouseEvent);
  aPointerEvent->pointerId = aMouseEvent->pointerId;
  aPointerEvent->mInputSource = aMouseEvent->mInputSource;
  aPointerEvent->mMessage = aMessage;
  aPointerEvent->mButton = aMouseEvent->mMessage == eMouseMove
                               ? MouseButton::eNotPressed
                               : aMouseEvent->mButton;

  aPointerEvent->mButtons = aMouseEvent->mButtons;
  aPointerEvent->mPressure =
      aPointerEvent->mButtons
          ? aMouseEvent->mPressure ? aMouseEvent->mPressure : 0.5f
          : 0.0f;
}

/* static */
void PointerEventHandler::InitPointerEventFromTouch(
    WidgetPointerEvent& aPointerEvent, const WidgetTouchEvent& aTouchEvent,
    const mozilla::dom::Touch& aTouch) {
  // Use mButton/mButtons only when mButton got a value (from pen input)
  int16_t button = aTouchEvent.mMessage == eTouchMove ? MouseButton::eNotPressed
                   : aTouchEvent.mButton != MouseButton::eNotPressed
                       ? aTouchEvent.mButton
                       : MouseButton::ePrimary;
  int16_t buttons = aTouchEvent.mMessage == eTouchEnd
                        ? MouseButtonsFlag::eNoButtons
                    : aTouchEvent.mButton != MouseButton::eNotPressed
                        ? aTouchEvent.mButtons
                        : MouseButtonsFlag::ePrimaryFlag;

  // XXX: This doesn't support multi pen scenario (bug 1904865)
  if (aTouchEvent.mInputSource == MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
    // Only the first touch would be the primary pointer.
    aPointerEvent.mIsPrimary =
        aTouchEvent.mMessage == eTouchStart
            ? !HasActiveTouchPointer()
            : GetPointerPrimaryState(aTouch.Identifier());
  }
  aPointerEvent.pointerId = aTouch.Identifier();
  aPointerEvent.mRefPoint = aTouch.mRefPoint;
  aPointerEvent.mModifiers = aTouchEvent.mModifiers;
  aPointerEvent.mWidth = aTouch.RadiusX(CallerType::System);
  aPointerEvent.mHeight = aTouch.RadiusY(CallerType::System);
  aPointerEvent.tiltX = aTouch.tiltX;
  aPointerEvent.tiltY = aTouch.tiltY;
  aPointerEvent.twist = aTouch.twist;
  aPointerEvent.mTimeStamp = aTouchEvent.mTimeStamp;
  aPointerEvent.mFlags = aTouchEvent.mFlags;
  aPointerEvent.mButton = button;
  aPointerEvent.mButtons = buttons;
  aPointerEvent.mInputSource = aTouchEvent.mInputSource;
  aPointerEvent.mFromTouchEvent = true;
  aPointerEvent.mPressure = aTouch.mForce;
}

/* static */
void PointerEventHandler::InitCoalescedEventFromPointerEvent(
    WidgetPointerEvent& aCoalescedEvent,
    const WidgetPointerEvent& aSourceEvent) {
  aCoalescedEvent.mFlags.mCancelable = false;
  aCoalescedEvent.mFlags.mBubbles = false;

  aCoalescedEvent.mTimeStamp = aSourceEvent.mTimeStamp;
  aCoalescedEvent.mRefPoint = aSourceEvent.mRefPoint;
  aCoalescedEvent.mModifiers = aSourceEvent.mModifiers;

  // WidgetMouseEventBase
  aCoalescedEvent.mButton = aSourceEvent.mButton;
  aCoalescedEvent.mButtons = aSourceEvent.mButtons;
  aCoalescedEvent.mPressure = aSourceEvent.mPressure;
  aCoalescedEvent.mInputSource = aSourceEvent.mInputSource;

  // pointerId, tiltX, tiltY, twist, tangentialPressure and convertToPointer.
  aCoalescedEvent.AssignPointerHelperData(aSourceEvent);

  // WidgetPointerEvent
  aCoalescedEvent.mWidth = aSourceEvent.mWidth;
  aCoalescedEvent.mHeight = aSourceEvent.mHeight;
  aCoalescedEvent.mIsPrimary = aSourceEvent.mIsPrimary;
  aCoalescedEvent.mFromTouchEvent = aSourceEvent.mFromTouchEvent;
}

/* static */
EventMessage PointerEventHandler::ToPointerEventMessage(
    const WidgetGUIEvent* aMouseOrTouchEvent) {
  MOZ_ASSERT(aMouseOrTouchEvent);

  switch (aMouseOrTouchEvent->mMessage) {
    case eMouseMove:
      return ePointerMove;
    case eMouseUp:
      return aMouseOrTouchEvent->AsMouseEvent()->mButtons ? ePointerMove
                                                          : ePointerUp;
    case eMouseDown: {
      const WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent();
      return mouseEvent->mButtons & ~nsContentUtils::GetButtonsFlagForButton(
                                        mouseEvent->mButton)
                 ? ePointerMove
                 : ePointerDown;
    }
    case eTouchMove:
      return ePointerMove;
    case eTouchEnd:
      return ePointerUp;
    case eTouchStart:
      return ePointerDown;
    case eTouchCancel:
    case eTouchPointerCancel:
      return ePointerCancel;
    default:
      return eVoidEvent;
  }
}

/* static */
void PointerEventHandler::DispatchPointerFromMouseOrTouch(
    PresShell* aShell, nsIFrame* aEventTargetFrame,
    nsIContent* aEventTargetContent, WidgetGUIEvent* aMouseOrTouchEvent,
    bool aDontRetargetEvents, nsEventStatus* aStatus,
    nsIContent** aMouseOrTouchEventTarget /* = nullptr */) {
  MOZ_ASSERT(aEventTargetFrame || aEventTargetContent);
  MOZ_ASSERT(aMouseOrTouchEvent);

  EventMessage pointerMessage = eVoidEvent;
  if (aMouseOrTouchEvent->mClass == eMouseEventClass) {
    WidgetMouseEvent* mouseEvent = aMouseOrTouchEvent->AsMouseEvent();
    // Don't dispatch pointer events caused by a mouse when simulating touch
    // devices in RDM.
    Document* doc = aShell->GetDocument();
    if (!doc) {
      return;
    }

    BrowsingContext* bc = doc->GetBrowsingContext();
    if (bc && bc->TouchEventsOverride() == TouchEventsOverride::Enabled &&
        bc->InRDMPane()) {
      return;
    }

    // 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 ||
        !aMouseOrTouchEvent->IsAllowedToDispatchDOMEvent()) {
      return;
    }

    pointerMessage = PointerEventHandler::ToPointerEventMessage(mouseEvent);
    if (pointerMessage == eVoidEvent) {
      return;
    }
    WidgetPointerEvent event(*mouseEvent);
    InitPointerEventFromMouse(&event, mouseEvent, pointerMessage);
    event.convertToPointer = mouseEvent->convertToPointer = false;
    RefPtr<PresShell> shell(aShell);
    if (!aEventTargetFrame) {
      shell = PresShell::GetShellForEventTarget(nullptr, aEventTargetContent);
      if (!shell) {
        return;
      }
    }
    PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
    // Dispatch pointer event to the same target which is found by the
    // corresponding mouse event.
    shell->HandleEventWithTarget(&event, aEventTargetFrame, aEventTargetContent,
                                 aStatus, true, aMouseOrTouchEventTarget);
    PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
    // If pointer capture is released, we need to synthesize eMouseMove to
    // dispatch mouse boundary events later.
    mouseEvent->mSynthesizeMoveAfterDispatch |=
        event.mSynthesizeMoveAfterDispatch;
  } else if (aMouseOrTouchEvent->mClass == eTouchEventClass) {
    WidgetTouchEvent* touchEvent = aMouseOrTouchEvent->AsTouchEvent();
    // loop over all touches and dispatch pointer events on each touch
    // copy the event
    pointerMessage = PointerEventHandler::ToPointerEventMessage(touchEvent);
    if (pointerMessage == eVoidEvent) {
      return;
    }
    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);

      InitPointerEventFromTouch(event, *touchEvent, *touch);
      event.convertToPointer = touch->convertToPointer = false;
      event.mCoalescedWidgetEvents = touch->mCoalescedWidgetEvents;
      if (aMouseOrTouchEvent->mMessage == eTouchStart) {
        // We already did hit test for touchstart in PresShell. We should
        // dispatch pointerdown to the same target as touchstart.
        nsCOMPtr<nsIContent> content =
            nsIContent::FromEventTargetOrNull(touch->mTarget);
        if (!content) {
          continue;
        }

        nsIFrame* frame = content->GetPrimaryFrame();
        shell = PresShell::GetShellForEventTarget(frame, content);
        if (!shell) {
          continue;
        }

        PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
        shell->HandleEventWithTarget(&event, frame, content, aStatus, true,
                                     aMouseOrTouchEventTarget);
        PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
      } else {
        // We didn't hit test for other touch events. Spec doesn't mention that
        // all pointer events should be dispatched to the same target as their
        // corresponding touch events. Call PresShell::HandleEvent so that we do
        // hit test for pointer events.
        // FIXME: If aDontRetargetEvents is true and the event is fired on
        // different document, we cannot track the pointer event target when
        // it's removed from the tree.
        PreHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
        shell->HandleEvent(aEventTargetFrame, &event, aDontRetargetEvents,
                           aStatus);
        PostHandlePointerEventsPreventDefault(&event, aMouseOrTouchEvent);
      }
    }
  }
}

/* static */
void PointerEventHandler::NotifyDestroyPresContext(
    nsPresContext* aPresContext) {
  // Clean up pointer capture info
  for (auto iter = sPointerCaptureList->Iter(); !iter.Done(); iter.Next()) {
    PointerCaptureInfo* data = iter.UserData();
    MOZ_ASSERT(data, "how could we have a null PointerCaptureInfo here?");
    if (data->mPendingElement &&
        data->mPendingElement->GetPresContext(Element::eForComposedDoc) ==
            aPresContext) {
      data->mPendingElement = nullptr;
    }
    if (data->mOverrideElement &&
        data->mOverrideElement->GetPresContext(Element::eForComposedDoc) ==
            aPresContext) {
      data->mOverrideElement = nullptr;
    }
    if (data->Empty()) {
      iter.Remove();
    }
  }
  // Clean up active pointer info
  for (auto iter = sActivePointersIds->Iter(); !iter.Done(); iter.Next()) {
    PointerInfo* data = iter.UserData();
    MOZ_ASSERT(data, "how could we have a null PointerInfo here?");
    if (data->mActiveDocument &&
        data->mActiveDocument->GetPresContext() == aPresContext) {
      iter.Remove();
    }
  }
}

bool PointerEventHandler::IsDragAndDropEnabled(WidgetMouseEvent& aEvent) {
#ifdef XP_WIN
  if (StaticPrefs::dom_w3c_pointer_events_dispatch_by_pointer_messages()) {
    // WM_POINTER does not support drag and drop, see bug 1692277
    return (aEvent.mInputSource != dom::MouseEvent_Binding::MOZ_SOURCE_PEN &&
            aEvent.mReason != WidgetMouseEvent::eSynthesized);  // bug 1692151
  }
#endif
  return true;
}

/* static */
uint16_t PointerEventHandler::GetPointerType(uint32_t aPointerId) {
  PointerInfo* pointerInfo = nullptr;
  if (sActivePointersIds->Get(aPointerId, &pointerInfo) && pointerInfo) {
    return pointerInfo->mPointerType;
  }
  return MouseEvent_Binding::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 */
bool PointerEventHandler::HasActiveTouchPointer() {
  for (auto iter = sActivePointersIds->ConstIter(); !iter.Done(); iter.Next()) {
    if (iter.Data()->mFromTouchEvent) {
      return true;
    }
  }
  return false;
}

/* static */
void PointerEventHandler::DispatchGotOrLostPointerCaptureEvent(
    bool aIsGotCapture, const WidgetPointerEvent* aPointerEvent,
    Element* aCaptureTarget) {
  Document* targetDoc = aCaptureTarget->OwnerDoc();
  RefPtr<PresShell> presShell = targetDoc->GetPresShell();
  if (NS_WARN_IF(!presShell || presShell->IsDestroying())) {
    return;
  }

  if (!aIsGotCapture && !aCaptureTarget->IsInComposedDoc()) {
    // 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->mInputSource, init.mPointerType);
    init.mIsPrimary = aPointerEvent->mIsPrimary;
    RefPtr<PointerEvent> event;
    event = PointerEvent::Constructor(aCaptureTarget, u"lostpointercapture"_ns,
                                      init);
    targetDoc->DispatchEvent(*event);
    return;
  }
  nsEventStatus status = nsEventStatus_eIgnore;
  WidgetPointerEvent localEvent(
      aPointerEvent->IsTrusted(),
      aIsGotCapture ? ePointerGotCapture : ePointerLostCapture,
      aPointerEvent->mWidget);

  localEvent.AssignPointerEventData(*aPointerEvent, true);
  DebugOnly<nsresult> rv = presShell->HandleEventWithTarget(
      &localEvent, aCaptureTarget->GetPrimaryFrame(), aCaptureTarget, &status);

  NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
                       "DispatchGotOrLostPointerCaptureEvent failed");
}

/* static */
void PointerEventHandler::MaybeCacheSpoofedPointerID(uint16_t aInputSource,
                                                     uint32_t aPointerId) {
  if (sSpoofedPointerId.isSome() || aInputSource != SPOOFED_POINTER_INTERFACE) {
    return;
  }

  sSpoofedPointerId.emplace(aPointerId);
}

}  // namespace mozilla

Messung V0.5
C=89 H=97 G=93

¤ Dauer der Verarbeitung: 0.9 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.