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 286 kB image not shown  

Quelle  EventStateManager.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 "EventStateManager.h"

#include "mozilla/AsyncEventDispatcher.h"
#include "mozilla/Attributes.h"
#include "mozilla/EditorBase.h"
#include "mozilla/EventDispatcher.h"
#include "mozilla/EventForwards.h"
#include "mozilla/Hal.h"
#include "mozilla/HTMLEditor.h"
#include "mozilla/IMEStateManager.h"
#include "mozilla/Likely.h"
#include "mozilla/FocusModel.h"
#include "mozilla/MiscEvents.h"
#include "mozilla/MathAlgorithms.h"
#include "mozilla/MouseEvents.h"
#include "mozilla/PointerLockManager.h"
#include "mozilla/PresShell.h"
#include "mozilla/ScopeExit.h"
#include "mozilla/ScrollTypes.h"
#include "mozilla/TextComposition.h"
#include "mozilla/TextControlElement.h"
#include "mozilla/TextEditor.h"
#include "mozilla/TextEvents.h"
#include "mozilla/TouchEvents.h"
#include "mozilla/Telemetry.h"
#include "mozilla/UniquePtr.h"
#include "mozilla/dom/BrowserBridgeChild.h"
#include "mozilla/dom/BrowsingContext.h"
#include "mozilla/dom/CanonicalBrowsingContext.h"
#include "mozilla/dom/ContentChild.h"
#include "mozilla/dom/DOMIntersectionObserver.h"
#include "mozilla/dom/DragEvent.h"
#include "mozilla/dom/Event.h"
#include "mozilla/dom/FrameLoaderBinding.h"
#include "mozilla/dom/HTMLLabelElement.h"
#include "mozilla/dom/HTMLInputElement.h"
#include "mozilla/dom/MouseEventBinding.h"
#include "mozilla/dom/BrowserChild.h"
#include "mozilla/dom/PointerEventHandler.h"
#include "mozilla/dom/UIEvent.h"
#include "mozilla/dom/UIEventBinding.h"
#include "mozilla/dom/UserActivation.h"
#include "mozilla/dom/WheelEventBinding.h"
#include "mozilla/glean/ProcesstoolsMetrics.h"
#include "mozilla/ScrollContainerFrame.h"
#include "mozilla/StaticPrefs_accessibility.h"
#include "mozilla/StaticPrefs_browser.h"
#include "mozilla/StaticPrefs_dom.h"
#include "mozilla/StaticPrefs_layout.h"
#include "mozilla/StaticPrefs_mousewheel.h"
#include "mozilla/StaticPrefs_ui.h"
#include "mozilla/StaticPrefs_zoom.h"

#include "ContentEventHandler.h"
#include "IMEContentObserver.h"
#include "WheelHandlingHelper.h"
#include "RemoteDragStartData.h"

#include "nsCommandParams.h"
#include "nsCOMPtr.h"
#include "nsCopySupport.h"
#include "nsFocusManager.h"
#include "nsGenericHTMLElement.h"
#include "nsIClipboard.h"
#include "nsIContent.h"
#include "nsIContentInlines.h"
#include "mozilla/dom/Document.h"
#include "nsICookieJarSettings.h"
#include "nsIFrame.h"
#include "nsFrameLoaderOwner.h"
#include "nsIWeakReferenceUtils.h"
#include "nsIWidget.h"
#include "nsLiteralString.h"
#include "nsPresContext.h"
#include "nsTArray.h"
#include "nsGkAtoms.h"
#include "nsIFormControl.h"
#include "nsComboboxControlFrame.h"
#include "nsIDOMXULControlElement.h"
#include "nsNameSpaceManager.h"
#include "nsIBaseWindow.h"
#include "nsFrameSelection.h"
#include "nsPIDOMWindow.h"
#include "nsPIWindowRoot.h"
#include "nsIWebNavigation.h"
#include "nsIDocumentViewer.h"
#include "nsFrameManager.h"
#include "nsIBrowserChild.h"
#include "nsMenuPopupFrame.h"

#include "nsIObserverService.h"
#include "nsIDocShell.h"

#include "nsSubDocumentFrame.h"
#include "nsLayoutUtils.h"
#include "nsIInterfaceRequestorUtils.h"
#include "nsUnicharUtils.h"
#include "nsContentUtils.h"

#include "imgIContainer.h"
#include "nsIProperties.h"
#include "nsISupportsPrimitives.h"

#include "nsServiceManagerUtils.h"
#include "nsITimer.h"
#include "nsFontMetrics.h"
#include "nsIDragService.h"
#include "nsIDragSession.h"
#include "mozilla/dom/DataTransfer.h"
#include "nsContentAreaDragDrop.h"
#include "nsTreeBodyFrame.h"
#include "nsIController.h"
#include "mozilla/Services.h"
#include "mozilla/dom/ContentParent.h"
#include "mozilla/dom/Record.h"
#include "mozilla/dom/Selection.h"

#include "mozilla/Preferences.h"
#include "mozilla/LookAndFeel.h"
#include "mozilla/ProfilerLabels.h"
#include "Units.h"

#ifdef XP_MACOSX
#  import <ApplicationServices/ApplicationServices.h>
#endif

namespace mozilla {

using namespace dom;

static const LayoutDeviceIntPoint kInvalidRefPoint =
    LayoutDeviceIntPoint(-1, -1);

static uint32_t gMouseOrKeyboardEventCounter = 0;
static nsITimer* gUserInteractionTimer = nullptr;
static nsITimerCallback* gUserInteractionTimerCallback = nullptr;

static const double kCursorLoadingTimeout = 1000;  // ms
MOZ_RUNINIT static AutoWeakFrame gLastCursorSourceFrame;
static TimeStamp gLastCursorUpdateTime;
static TimeStamp gTypingStartTime;
static TimeStamp gTypingEndTime;
static int32_t gTypingInteractionKeyPresses = 0;
MOZ_RUNINIT static dom::InteractionData gTypingInteraction = {};

static inline int32_t RoundDown(double aDouble) {
  return (aDouble > 0) ? static_cast<int32_t>(floor(aDouble))
                       : static_cast<int32_t>(ceil(aDouble));
}

static bool IsSelectingLink(nsIFrame* aTargetFrame) {
  if (!aTargetFrame) {
    return false;
  }
  const nsFrameSelection* frameSel = aTargetFrame->GetConstFrameSelection();
  if (!frameSel || !frameSel->GetDragState()) {
    return false;
  }

  if (!nsContentUtils::GetClosestLinkInFlatTree(aTargetFrame->GetContent())) {
    return false;
  }
  return true;
}

static UniquePtr<WidgetMouseEvent> CreateMouseOrPointerWidgetEvent(
    WidgetMouseEvent* aMouseEvent, EventMessage aMessage,
    EventTarget* aRelatedTarget);

/**
 * Returns the common ancestor for mouseup purpose, given the
 * current mouseup target and the previous mousedown target.
 */

static nsINode* GetCommonAncestorForMouseUp(
    nsINode* aCurrentMouseUpTarget, nsINode* aLastMouseDownTarget,
    Maybe<FormControlType>& aLastMouseDownInputControlType) {
  if (!aCurrentMouseUpTarget || !aLastMouseDownTarget) {
    return nullptr;
  }

  if (aCurrentMouseUpTarget == aLastMouseDownTarget) {
    return aCurrentMouseUpTarget;
  }

  // Build the chain of parents
  AutoTArray<nsINode*, 30> parents1;
  do {
    parents1.AppendElement(aCurrentMouseUpTarget);
    aCurrentMouseUpTarget = aCurrentMouseUpTarget->GetFlattenedTreeParentNode();
  } while (aCurrentMouseUpTarget);

  AutoTArray<nsINode*, 30> parents2;
  do {
    parents2.AppendElement(aLastMouseDownTarget);
    if (aLastMouseDownTarget == parents1.LastElement()) {
      break;
    }
    aLastMouseDownTarget = aLastMouseDownTarget->GetFlattenedTreeParentNode();
  } while (aLastMouseDownTarget);

  // Find where the parent chain differs
  uint32_t pos1 = parents1.Length();
  uint32_t pos2 = parents2.Length();
  nsINode* parent = nullptr;
  for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
    nsINode* child1 = parents1.ElementAt(--pos1);
    nsINode* child2 = parents2.ElementAt(--pos2);
    if (child1 != child2) {
      break;
    }

    // If the input control type is different between mouseup and mousedown,
    // this is not a valid click.
    if (HTMLInputElement* input = HTMLInputElement::FromNodeOrNull(child1)) {
      if (aLastMouseDownInputControlType.isSome() &&
          aLastMouseDownInputControlType.ref() != input->ControlType()) {
        break;
      }
    }
    parent = child1;
  }

  return parent;
}

static bool HasNativeKeyBindings(nsIContent* aContent,
                                 WidgetKeyboardEvent* aEvent) {
  MOZ_ASSERT(aEvent->mMessage == eKeyPress);

  if (!aContent) {
    return false;
  }

  const RefPtr<dom::Element> targetElement = aContent->AsElement();
  if (!targetElement) {
    return false;
  }

  const auto type = [&]() -> Maybe<NativeKeyBindingsType> {
    if (BrowserParent::GetFrom(targetElement)) {
      const nsCOMPtr<nsIWidget> widget = aEvent->mWidget;
      if (MOZ_UNLIKELY(!widget)) {
        return Nothing();
      }
      widget::InputContext context = widget->GetInputContext();
      return context.mIMEState.IsEditable()
                 ? Some(context.GetNativeKeyBindingsType())
                 : Nothing();
    }

    const autoconst textControlElement =
        TextControlElement::FromNode(targetElement);
    if (textControlElement &&
        textControlElement->IsSingleLineTextControlOrTextArea() &&
        !textControlElement->IsInDesignMode()) {
      return textControlElement->IsTextArea()
                 ? Some(NativeKeyBindingsType::MultiLineEditor)
                 : Some(NativeKeyBindingsType::SingleLineEditor);
    }
    return targetElement->IsEditable()
               ? Some(NativeKeyBindingsType::RichTextEditor)
               : Nothing();
  }();
  if (type.isNothing()) {
    return false;
  }

  const nsTArray<CommandInt>& commands =
      aEvent->EditCommandsConstRef(type.value());
  return !commands.IsEmpty();
}

LazyLogModule sMouseBoundaryLog("MouseBoundaryEvents");
LazyLogModule sPointerBoundaryLog("PointerBoundaryEvents");

/******************************************************************/
/* mozilla::UITimerCallback                                       */
/******************************************************************/

class UITimerCallback final : public nsITimerCallback, public nsINamed {
 public:
  UITimerCallback() : mPreviousCount(0) {}
  NS_DECL_ISUPPORTS
  NS_DECL_NSITIMERCALLBACK
  NS_DECL_NSINAMED
 private:
  ~UITimerCallback() = default;
  uint32_t mPreviousCount;
};

NS_IMPL_ISUPPORTS(UITimerCallback, nsITimerCallback, nsINamed)

// If aTimer is nullptr, this method always sends "user-interaction-inactive"
// notification.
NS_IMETHODIMP
UITimerCallback::Notify(nsITimer* aTimer) {
  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
  if (!obs) return NS_ERROR_FAILURE;
  if ((gMouseOrKeyboardEventCounter == mPreviousCount) || !aTimer) {
    gMouseOrKeyboardEventCounter = 0;
    obs->NotifyObservers(nullptr, "user-interaction-inactive", nullptr);
    if (gUserInteractionTimer) {
      gUserInteractionTimer->Cancel();
      NS_RELEASE(gUserInteractionTimer);
    }
  } else {
    obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
    EventStateManager::UpdateUserActivityTimer();

    if (XRE_IsParentProcess()) {
      hal::BatteryInformation batteryInfo;
      hal::GetCurrentBatteryInformation(&batteryInfo);
      glean::power_battery::percentage_when_user_active.AccumulateSingleSample(
          uint64_t(batteryInfo.level() * 100));
    }
  }
  mPreviousCount = gMouseOrKeyboardEventCounter;
  return NS_OK;
}

NS_IMETHODIMP
UITimerCallback::GetName(nsACString& aName) {
  aName.AssignLiteral("UITimerCallback_timer");
  return NS_OK;
}

/******************************************************************/
/* mozilla::OverOutElementsWrapper                                */
/******************************************************************/

NS_IMPL_CYCLE_COLLECTION(OverOutElementsWrapper, mDeepestEnterEventTarget,
                         mDispatchingOverEventTarget,
                         mDispatchingOutOrDeepestLeaveEventTarget)
NS_IMPL_CYCLE_COLLECTING_ADDREF(OverOutElementsWrapper)
NS_IMPL_CYCLE_COLLECTING_RELEASE(OverOutElementsWrapper)

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OverOutElementsWrapper)
  NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_END

already_AddRefed<nsIWidget> OverOutElementsWrapper::GetLastOverWidget() const {
  nsCOMPtr<nsIWidget> widget = do_QueryReferent(mLastOverWidget);
  return widget.forget();
}

void OverOutElementsWrapper::ContentRemoved(nsIContent& aContent) {
  if (!mDeepestEnterEventTarget) {
    return;
  }

  if (!nsContentUtils::ContentIsFlattenedTreeDescendantOf(
          mDeepestEnterEventTarget, &aContent)) {
    return;
  }

  LogModule* const logModule = mType == BoundaryEventType::Mouse
                                   ? sMouseBoundaryLog
                                   : sPointerBoundaryLog;

  if (!StaticPrefs::
          dom_events_mouse_pointer_boundary_keep_enter_targets_after_over_target_removed()) {
    MOZ_LOG(logModule, LogLevel::Info,
            ("The last \"over\" event target (%p) is removed",
             mDeepestEnterEventTarget.get()));
    StoreOverEventTargetAndDeepestEnterEventTarget(nullptr);
    return;
  }

  if (mDispatchingOverEventTarget &&
      (mDeepestEnterEventTarget == mDispatchingOverEventTarget ||
       nsContentUtils::ContentIsFlattenedTreeDescendantOf(
           mDispatchingOverEventTarget, &aContent))) {
    if (mDispatchingOverEventTarget ==
        mDispatchingOutOrDeepestLeaveEventTarget) {
      MOZ_LOG(logModule, LogLevel::Info,
              ("The dispatching \"%s\" event target (%p) is removed",
               LastOverEventTargetIsOutEventTarget() ? "out" : "leave",
               mDispatchingOutOrDeepestLeaveEventTarget.get()));
      mDispatchingOutOrDeepestLeaveEventTarget = nullptr;
    }
    MOZ_LOG(logModule, LogLevel::Info,
            ("The dispatching \"over\" event target (%p) is removed",
             mDispatchingOverEventTarget.get()));
    mDispatchingOverEventTarget = nullptr;
  }
  if (mDispatchingOutOrDeepestLeaveEventTarget &&
      (mDeepestEnterEventTarget == mDispatchingOutOrDeepestLeaveEventTarget ||
       nsContentUtils::ContentIsFlattenedTreeDescendantOf(
           mDispatchingOutOrDeepestLeaveEventTarget, &aContent))) {
    MOZ_LOG(logModule, LogLevel::Info,
            ("The dispatching \"%s\" event target (%p) is removed",
             LastOverEventTargetIsOutEventTarget() ? "out" : "leave",
             mDispatchingOutOrDeepestLeaveEventTarget.get()));
    mDispatchingOutOrDeepestLeaveEventTarget = nullptr;
  }
  MOZ_LOG(logModule, LogLevel::Info,
          ("The last \"%s\" event target (%p) is removed and now the last "
           "deepest enter target becomes %s(%p)",
           LastOverEventTargetIsOutEventTarget() ? "over" : "enter",
           mDeepestEnterEventTarget.get(),
           aContent.GetFlattenedTreeParent()
               ? ToString(*aContent.GetFlattenedTreeParent()).c_str()
               : "nullptr",
           aContent.GetFlattenedTreeParent()));
  UpdateDeepestEnterEventTarget(aContent.GetFlattenedTreeParent());
}

void OverOutElementsWrapper::TryToRestorePendingRemovedOverTarget(
    const WidgetEvent* aEvent) {
  if (!MaybeHasPendingRemovingOverEventTarget()) {
    return;
  }

  LogModule* const logModule = mType == BoundaryEventType::Mouse
                                   ? sMouseBoundaryLog
                                   : sPointerBoundaryLog;

  // If we receive a mouse event immediately, let's try to restore the last
  // "over" event target as the following "out" event target.  We assume that a
  // synthesized mousemove or another mouse event is being dispatched at latest
  // the next animation frame from the removal.  However, synthesized mouse move
  // which is enqueued by ContentRemoved() may not sent to this instance because
  // the target is considered with the latest layout, so the document of this
  // instance may be moved somewhere before the next animation frame.
  // Therefore, we should not restore the last "over" target if we receive an
  // unexpected event like a keyboard event, a wheel event, etc.
  if (aEvent->AsMouseEvent()) {
    // Restore the original "over" event target should be allowed only when it's
    // reconnected under the last deepest "enter" event target because we need
    // to dispatch "leave" events later at least on the ancestors which have
    // never been removed from the tree.
    // XXX If new ancestor is inserted between mDeepestEnterEventTarget and
    // mPendingToRemoveLastOverEventTarget, we will dispatch "leave" event even
    // though we have not dispatched "enter" event on the element.  For fixing
    // this, we need to store the full path of the last "out" event target when
    // it's removed from the tree.  I guess we can be relax for this issue
    // because this hack is required for web apps which reconnect the target
    // to the same position immediately.
    // XXX Should be IsInclusiveFlatTreeDescendantOf()?  However, it may
    // be reconnected into a subtree which is different from where the
    // last over element was.
    nsCOMPtr<nsIContent> pendingRemovingOverEventTarget =
        GetPendingRemovingOverEventTarget();
    if (pendingRemovingOverEventTarget &&
        pendingRemovingOverEventTarget->IsInclusiveDescendantOf(
            mDeepestEnterEventTarget)) {
      // StoreOverEventTargetAndDeepestEnterEventTarget() always resets
      // mLastOverWidget.  When we restore the pending removing "over" event
      // target, we need to keep storing the original "over" widget too.
      nsCOMPtr<nsIWeakReference> widget = std::move(mLastOverWidget);
      StoreOverEventTargetAndDeepestEnterEventTarget(
          pendingRemovingOverEventTarget);
      mLastOverWidget = std::move(widget);
      MOZ_LOG(logModule, LogLevel::Info,
              ("The \"over\" event target (%p) is restored",
               mDeepestEnterEventTarget.get()));
      return;
    }
    MOZ_LOG(logModule, LogLevel::Debug,
            ("Forgetting the last \"over\" event target (%p) because it is not "
             "reconnected under the deepest enter event target (%p)",
             mPendingRemovingOverEventTarget.get(),
             mDeepestEnterEventTarget.get()));
  } else {
    MOZ_LOG(logModule, LogLevel::Debug,
            ("Forgetting the last \"over\" event target (%p) because an "
             "unexpected event (%s) is being dispatched, that means that "
             "EventStateManager didn't receive a synthesized mousemove which "
             "should be dispatched at next animation frame from the removal",
             mPendingRemovingOverEventTarget.get(), ToChar(aEvent->mMessage)));
  }

  // Now, we should not restore mPendingRemovingOverEventTarget to
  // mDeepestEnterEventTarget anymore since mPendingRemovingOverEventTarget was
  // moved outside the subtree of mDeepestEnterEventTarget.
  mPendingRemovingOverEventTarget = nullptr;
}

void OverOutElementsWrapper::WillDispatchOverAndEnterEvent(
    nsIContent* aOverEventTarget) {
  StoreOverEventTargetAndDeepestEnterEventTarget(aOverEventTarget);
  // Store the first "over" event target we fire and don't refire "over" event
  // to that element while the first "over" event is still ongoing.
  mDispatchingOverEventTarget = aOverEventTarget;
}

void OverOutElementsWrapper::DidDispatchOverAndEnterEvent(
    nsIContent* aOriginalOverTargetInComposedDoc,
    nsIWidget* aOverEventTargetWidget) {
  mDispatchingOverEventTarget = nullptr;
  mLastOverWidget = do_GetWeakReference(aOverEventTargetWidget);

  // Pointer Events define that once the `pointerover` event target is removed
  // from the tree, `pointerout` should not be fired on that and the closest
  // connected ancestor at the target removal should be kept as the deepest
  // `pointerleave` target.  Therefore, we don't need the special handling for
  // `pointerout` event target if the last `pointerover` target is temporarily
  // removed from the tree.
  if (mType == OverOutElementsWrapper::BoundaryEventType::Pointer) {
    return;
  }

  // Assume that the caller checks whether aOriginalOverTarget is in the
  // original document.  If we don't enable the strict mouse/pointer event
  // boundary event dispatching by the pref (see below),
  // mDeepestEnterEventTarget is set to nullptr when the last "over" target is
  // removed.  Therefore, we cannot check whether aOriginalOverTarget is in the
  // original document here.
  if (!aOriginalOverTargetInComposedDoc) {
    return;
  }
  MOZ_ASSERT_IF(mDeepestEnterEventTarget,
                mDeepestEnterEventTarget->GetComposedDoc() ==
                    aOriginalOverTargetInComposedDoc->GetComposedDoc());
  // If the "mouseover" event target is removed temporarily while we're
  // dispatching "mouseover" and "mouseenter" events and the target gets back
  // under the deepest enter event target, we should restore the "mouseover"
  // target.
  if ((!StaticPrefs::
           dom_events_mouse_pointer_boundary_keep_enter_targets_after_over_target_removed() &&
       !mDeepestEnterEventTarget) ||
      (!LastOverEventTargetIsOutEventTarget() && mDeepestEnterEventTarget &&
       nsContentUtils::ContentIsFlattenedTreeDescendantOf(
           aOriginalOverTargetInComposedDoc, mDeepestEnterEventTarget))) {
    StoreOverEventTargetAndDeepestEnterEventTarget(
        aOriginalOverTargetInComposedDoc);
    LogModule* const logModule = mType == BoundaryEventType::Mouse
                                     ? sMouseBoundaryLog
                                     : sPointerBoundaryLog;
    MOZ_LOG(logModule, LogLevel::Info,
            ("The \"over\" event target (%p) is restored",
             mDeepestEnterEventTarget.get()));
  }
}

void OverOutElementsWrapper::StoreOverEventTargetAndDeepestEnterEventTarget(
    nsIContent* aOverEventTargetAndDeepestEnterEventTarget) {
  mDeepestEnterEventTarget = aOverEventTargetAndDeepestEnterEventTarget;
  mPendingRemovingOverEventTarget = nullptr;
  mDeepestEnterEventTargetIsOverEventTarget = !!mDeepestEnterEventTarget;
  mLastOverWidget = nullptr;  // Set it after dispatching the "over" event.
}

void OverOutElementsWrapper::UpdateDeepestEnterEventTarget(
    nsIContent* aDeepestEnterEventTarget) {
  if (MOZ_UNLIKELY(mDeepestEnterEventTarget == aDeepestEnterEventTarget)) {
    return;
  }

  if (!aDeepestEnterEventTarget) {
    // If the root element is removed, we don't need to dispatch "leave"
    // events on any elements.  Therefore, we can forget everything.
    StoreOverEventTargetAndDeepestEnterEventTarget(nullptr);
    return;
  }

  if (LastOverEventTargetIsOutEventTarget()) {
    MOZ_ASSERT(mDeepestEnterEventTarget);
    if (mType == BoundaryEventType::Pointer) {
      // The spec of Pointer Events defines that once the `pointerover` event
      // target is removed from the tree, `pointerout` should not be fired on
      // that and the closest connected ancestor at the target removal should be
      // kept as the deepest `pointerleave` target.  All browsers considers the
      // last `pointerover` event target is removed immediately when it occurs.
      // Therefore, we don't need the special handling which we do for the
      // `mouseout` event target below for considering whether we'll dispatch
      // `pointerout` on the last `pointerover` target.
      mPendingRemovingOverEventTarget = nullptr;
    } else {
      // Now, the `mouseout` event target is removed from the DOM at least
      // temporarily.  Let's keep storing it for restoring it if it's
      // reconnected into mDeepestEnterEventTarget in a tick because the other
      // browsers do not treat temporary removal of the last `mouseover` target
      // keeps storing it as the next `mouseout` event target.
      MOZ_ASSERT(!mPendingRemovingOverEventTarget);
      MOZ_ASSERT(mDeepestEnterEventTarget);
      mPendingRemovingOverEventTarget =
          do_GetWeakReference(mDeepestEnterEventTarget);
    }
  } else {
    MOZ_ASSERT(!mDeepestEnterEventTargetIsOverEventTarget);
    // If mDeepestEnterEventTarget is not the last "over" event target, we've
    // already done the complicated state managing above.  Therefore, we only
    // need to update mDeepestEnterEventTarget in this case.
  }
  mDeepestEnterEventTarget = aDeepestEnterEventTarget;
  mDeepestEnterEventTargetIsOverEventTarget = false;
  // Do not update mLastOverWidget here because it's required to ignore some
  // following pointer events which are fired on widget under different top
  // level widget.
}

/******************************************************************/
/* mozilla::EventStateManager                                     */
/******************************************************************/

static uint32_t sESMInstanceCount = 0;

bool EventStateManager::sNormalLMouseEventInProcess = false;
int16_t EventStateManager::sCurrentMouseBtn = MouseButton::eNotPressed;
EventStateManager* EventStateManager::sActiveESM = nullptr;
EventStateManager* EventStateManager::sCursorSettingManager = nullptr;
MOZ_RUNINIT AutoWeakFrame EventStateManager::sLastDragOverFrame = nullptr;
LayoutDeviceIntPoint EventStateManager::sPreLockScreenPoint =
    LayoutDeviceIntPoint(0, 0);
LayoutDeviceIntPoint EventStateManager::sLastRefPoint = kInvalidRefPoint;
CSSIntPoint EventStateManager::sLastScreenPoint = CSSIntPoint(0, 0);
LayoutDeviceIntPoint EventStateManager::sSynthCenteringPoint = kInvalidRefPoint;
CSSIntPoint EventStateManager::sLastClientPoint = CSSIntPoint(0, 0);
MOZ_RUNINIT nsCOMPtr<nsIContent> EventStateManager::sDragOverContent = nullptr;

EventStateManager::WheelPrefs* EventStateManager::WheelPrefs::sInstance =
    nullptr;
EventStateManager::DeltaAccumulator*
    EventStateManager::DeltaAccumulator::sInstance = nullptr;

constexpr const StyleCursorKind kInvalidCursorKind =
    static_cast<StyleCursorKind>(255);

EventStateManager::EventStateManager()
    : mLockCursor(kInvalidCursorKind),
      mCurrentTarget(nullptr),
      // init d&d gesture state machine variables
      mGestureDownPoint(0, 0),
      mGestureModifiers(0),
      mGestureDownButtons(0),
      mGestureDownButton(0),
      mPresContext(nullptr),
      mShouldAlwaysUseLineDeltas(false),
      mShouldAlwaysUseLineDeltasInitialized(false),
      mGestureDownInTextControl(false),
      mInTouchDrag(false),
      m_haveShutdown(false) {
  if (sESMInstanceCount == 0) {
    gUserInteractionTimerCallback = new UITimerCallback();
    if (gUserInteractionTimerCallback) NS_ADDREF(gUserInteractionTimerCallback);
    UpdateUserActivityTimer();
  }
  ++sESMInstanceCount;
}

nsresult EventStateManager::UpdateUserActivityTimer() {
  if (!gUserInteractionTimerCallback) return NS_OK;

  if (!gUserInteractionTimer) {
    gUserInteractionTimer = NS_NewTimer().take();
  }

  if (gUserInteractionTimer) {
    gUserInteractionTimer->InitWithCallback(
        gUserInteractionTimerCallback,
        StaticPrefs::dom_events_user_interaction_interval(),
        nsITimer::TYPE_ONE_SHOT);
  }
  return NS_OK;
}

nsresult EventStateManager::Init() {
  nsCOMPtr<nsIObserverService> observerService =
      mozilla::services::GetObserverService();
  if (!observerService) return NS_ERROR_FAILURE;

  observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);

  return NS_OK;
}

bool EventStateManager::ShouldAlwaysUseLineDeltas() {
  if (MOZ_UNLIKELY(!mShouldAlwaysUseLineDeltasInitialized)) {
    mShouldAlwaysUseLineDeltasInitialized = true;
    mShouldAlwaysUseLineDeltas =
        !StaticPrefs::dom_event_wheel_deltaMode_lines_disabled();
    if (!mShouldAlwaysUseLineDeltas && mDocument) {
      if (nsIPrincipal* principal =
              mDocument->GetPrincipalForPrefBasedHacks()) {
        mShouldAlwaysUseLineDeltas = principal->IsURIInPrefList(
            "dom.event.wheel-deltaMode-lines.always-enabled");
      }
    }
  }
  return mShouldAlwaysUseLineDeltas;
}

EventStateManager::~EventStateManager() {
  ReleaseCurrentIMEContentObserver();

  if (sActiveESM == this) {
    sActiveESM = nullptr;
  }

  if (StaticPrefs::ui_click_hold_context_menus()) {
    KillClickHoldTimer();
  }

  if (sCursorSettingManager == this) {
    sCursorSettingManager = nullptr;
  }

  --sESMInstanceCount;
  if (sESMInstanceCount == 0) {
    WheelTransaction::Shutdown();
    if (gUserInteractionTimerCallback) {
      gUserInteractionTimerCallback->Notify(nullptr);
      NS_RELEASE(gUserInteractionTimerCallback);
    }
    if (gUserInteractionTimer) {
      gUserInteractionTimer->Cancel();
      NS_RELEASE(gUserInteractionTimer);
    }
    WheelPrefs::Shutdown();
    DeltaAccumulator::Shutdown();
  }

  if (sDragOverContent && sDragOverContent->OwnerDoc() == mDocument) {
    sDragOverContent = nullptr;
  }

  if (!m_haveShutdown) {
    Shutdown();

    // Don't remove from Observer service in Shutdown because Shutdown also
    // gets called from xpcom shutdown observer.  And we don't want to remove
    // from the service in that case.

    nsCOMPtr<nsIObserverService> observerService =
        mozilla::services::GetObserverService();
    if (observerService) {
      observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
    }
  }
}

nsresult EventStateManager::Shutdown() {
  m_haveShutdown = true;
  return NS_OK;
}

NS_IMETHODIMP
EventStateManager::Observe(nsISupports* aSubject, const char* aTopic,
                           const char16_t* someData) {
  if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    Shutdown();
  }

  return NS_OK;
}

NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventStateManager)
  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
  NS_INTERFACE_MAP_ENTRY(nsIObserver)
  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
NS_INTERFACE_MAP_END

NS_IMPL_CYCLE_COLLECTING_ADDREF(EventStateManager)
NS_IMPL_CYCLE_COLLECTING_RELEASE(EventStateManager)

NS_IMPL_CYCLE_COLLECTION_WEAK(EventStateManager, mCurrentTargetContent,
                              mGestureDownContent, mGestureDownFrameOwner,
                              mLastLeftMouseDownInfo.mLastMouseDownContent,
                              mLastMiddleMouseDownInfo.mLastMouseDownContent,
                              mLastRightMouseDownInfo.mLastMouseDownContent,
                              mActiveContent, mHoverContent, mURLTargetContent,
                              mPopoverPointerDownTarget, mMouseEnterLeaveHelper,
                              mPointersEnterLeaveHelper, mDocument,
                              mIMEContentObserver, mAccessKeys)

void EventStateManager::ReleaseCurrentIMEContentObserver() {
  if (mIMEContentObserver) {
    mIMEContentObserver->DisconnectFromEventStateManager();
  }
  mIMEContentObserver = nullptr;
}

void EventStateManager::OnStartToObserveContent(
    IMEContentObserver* aIMEContentObserver) {
  if (mIMEContentObserver == aIMEContentObserver) {
    return;
  }
  ReleaseCurrentIMEContentObserver();
  mIMEContentObserver = aIMEContentObserver;
}

void EventStateManager::OnStopObservingContent(
    IMEContentObserver* aIMEContentObserver) {
  aIMEContentObserver->DisconnectFromEventStateManager();
  NS_ENSURE_TRUE_VOID(mIMEContentObserver == aIMEContentObserver);
  mIMEContentObserver = nullptr;
}

void EventStateManager::TryToFlushPendingNotificationsToIME() {
  if (mIMEContentObserver) {
    mIMEContentObserver->TryToFlushPendingNotifications(true);
  }
}

static bool IsMessageMouseUserActivity(EventMessage aMessage) {
  return aMessage == eMouseMove || aMessage == eMouseUp ||
         aMessage == eMouseDown || aMessage == ePointerAuxClick ||
         aMessage == eMouseDoubleClick || aMessage == ePointerClick ||
         aMessage == eMouseActivate || aMessage == eMouseLongTap;
}

static bool IsMessageGamepadUserActivity(EventMessage aMessage) {
  return aMessage == eGamepadButtonDown || aMessage == eGamepadButtonUp ||
         aMessage == eGamepadAxisMove;
}

// static
bool EventStateManager::IsKeyboardEventUserActivity(WidgetEvent* aEvent) {
  // We ignore things that shouldn't cause popups, but also things that look
  // like shortcut presses. In some obscure cases these may actually be
  // website input, but any meaningful website will have other input anyway,
  // and we can't very well tell whether shortcut input was supposed to be
  // directed at chrome or the document.

  WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
  // Access keys should be treated as page interaction.
  if (keyEvent->ModifiersMatchWithAccessKey(AccessKeyType::eContent)) {
    return true;
  }
  if (!keyEvent->CanTreatAsUserInput() || keyEvent->IsControl() ||
      keyEvent->IsMeta() || keyEvent->IsAlt()) {
    return false;
  }
  // Deal with function keys:
  switch (keyEvent->mKeyNameIndex) {
    case KEY_NAME_INDEX_F1:
    case KEY_NAME_INDEX_F2:
    case KEY_NAME_INDEX_F3:
    case KEY_NAME_INDEX_F4:
    case KEY_NAME_INDEX_F5:
    case KEY_NAME_INDEX_F6:
    case KEY_NAME_INDEX_F7:
    case KEY_NAME_INDEX_F8:
    case KEY_NAME_INDEX_F9:
    case KEY_NAME_INDEX_F10:
    case KEY_NAME_INDEX_F11:
    case KEY_NAME_INDEX_F12:
    case KEY_NAME_INDEX_F13:
    case KEY_NAME_INDEX_F14:
    case KEY_NAME_INDEX_F15:
    case KEY_NAME_INDEX_F16:
    case KEY_NAME_INDEX_F17:
    case KEY_NAME_INDEX_F18:
    case KEY_NAME_INDEX_F19:
    case KEY_NAME_INDEX_F20:
    case KEY_NAME_INDEX_F21:
    case KEY_NAME_INDEX_F22:
    case KEY_NAME_INDEX_F23:
    case KEY_NAME_INDEX_F24:
      return false;
    default:
      return true;
  }
}

static void OnTypingInteractionEnded() {
  // We don't consider a single keystroke to be typing.
  if (gTypingInteractionKeyPresses > 1) {
    gTypingInteraction.mInteractionCount += gTypingInteractionKeyPresses;
    gTypingInteraction.mInteractionTimeInMilliseconds += static_cast<uint32_t>(
        std::ceil((gTypingEndTime - gTypingStartTime).ToMilliseconds()));
  }

  gTypingInteractionKeyPresses = 0;
  gTypingStartTime = TimeStamp();
  gTypingEndTime = TimeStamp();
}

static void HandleKeyUpInteraction(WidgetKeyboardEvent* aKeyEvent) {
  if (EventStateManager::IsKeyboardEventUserActivity(aKeyEvent)) {
    TimeStamp now = TimeStamp::Now();
    if (gTypingEndTime.IsNull()) {
      gTypingEndTime = now;
    }
    TimeDuration delay = now - gTypingEndTime;
    // Has it been too long since the last keystroke to be considered typing?
    if (gTypingInteractionKeyPresses > 0 &&
        delay >
            TimeDuration::FromMilliseconds(
                StaticPrefs::browser_places_interactions_typing_timeout_ms())) {
      OnTypingInteractionEnded();
    }
    gTypingInteractionKeyPresses++;
    if (gTypingStartTime.IsNull()) {
      gTypingStartTime = now;
    }
    gTypingEndTime = now;
  }
}

nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
                                           WidgetEvent* aEvent,
                                           nsIFrame* aTargetFrame,
                                           nsIContent* aTargetContent,
                                           nsEventStatus* aStatus,
                                           nsIContent* aOverrideClickTarget) {
  AUTO_PROFILER_LABEL("EventStateManager::PreHandleEvent", DOM);
  NS_ENSURE_ARG_POINTER(aStatus);
  NS_ENSURE_ARG(aPresContext);
  if (!aEvent) {
    NS_ERROR("aEvent is null. This should never happen.");
    return NS_ERROR_NULL_POINTER;
  }

  NS_WARNING_ASSERTION(
      !aTargetFrame || !aTargetFrame->GetContent() ||
          aTargetFrame->GetContent() == aTargetContent ||
          aTargetFrame->GetContent()->GetFlattenedTreeParent() ==
              aTargetContent ||
          aTargetFrame->IsGeneratedContentFrame(),
      "aTargetFrame should be related with aTargetContent");
#if DEBUG
  if (aTargetFrame && aTargetFrame->IsGeneratedContentFrame()) {
    nsCOMPtr<nsIContent> targetContent;
    aTargetFrame->GetContentForEvent(aEvent, getter_AddRefs(targetContent));
    MOZ_ASSERT(aTargetContent == targetContent,
               "Unexpected target for generated content frame!");
  }
#endif

  mCurrentTarget = aTargetFrame;
  mCurrentTargetContent = nullptr;

  // Do not take account eMouseEnterIntoWidget/ExitFromWidget so that loading
  // a page when user is not active doesn't change the state to active.
  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
  if (aEvent->IsTrusted() &&
      ((mouseEvent && mouseEvent->IsReal() &&
        IsMessageMouseUserActivity(mouseEvent->mMessage)) ||
       aEvent->mClass == eWheelEventClass ||
       aEvent->mClass == ePointerEventClass ||
       aEvent->mClass == eTouchEventClass ||
       aEvent->mClass == eKeyboardEventClass ||
       (aEvent->mClass == eDragEventClass && aEvent->mMessage == eDrop) ||
       IsMessageGamepadUserActivity(aEvent->mMessage))) {
    if (gMouseOrKeyboardEventCounter == 0) {
      nsCOMPtr<nsIObserverService> obs =
          mozilla::services::GetObserverService();
      if (obs) {
        obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
        UpdateUserActivityTimer();
      }
    }
    ++gMouseOrKeyboardEventCounter;

    nsCOMPtr<nsINode> node = aTargetContent;
    if (node &&
        ((aEvent->mMessage == eKeyUp && IsKeyboardEventUserActivity(aEvent)) ||
         aEvent->mMessage == eMouseUp || aEvent->mMessage == eWheel ||
         aEvent->mMessage == eTouchEnd || aEvent->mMessage == ePointerUp ||
         aEvent->mMessage == eDrop)) {
      Document* doc = node->OwnerDoc();
      while (doc) {
        doc->SetUserHasInteracted();
        doc = nsContentUtils::IsChildOfSameType(doc)
                  ? doc->GetInProcessParentDocument()
                  : nullptr;
      }
    }
  }

  WheelTransaction::OnEvent(aEvent);

  // Focus events don't necessarily need a frame.
  if (!mCurrentTarget && !aTargetContent) {
    NS_ERROR("mCurrentTarget and aTargetContent are null");
    return NS_ERROR_NULL_POINTER;
  }
#ifdef DEBUG
  if (aEvent->HasDragEventMessage() && PointerLockManager::IsLocked()) {
    NS_ASSERTION(PointerLockManager::IsLocked(),
                 "Pointer is locked. Drag events should be suppressed when "
                 "the pointer is locked.");
  }
#endif
  // Store last known screenPoint and clientPoint so pointer lock
  // can use these values as constants.
  if (aEvent->IsTrusted() &&
      ((mouseEvent && mouseEvent->IsReal()) ||
       aEvent->mClass == eWheelEventClass) &&
      !PointerLockManager::IsLocked()) {
    // XXX Probably doesn't matter much, but storing these in CSS pixels instead
    // of device pixels means behavior can be a bit odd if you zoom while
    // pointer-locked.
    sLastScreenPoint = RoundedToInt(
        Event::GetScreenCoords(aPresContext, aEvent, aEvent->mRefPoint)
            .extract());
    sLastClientPoint = RoundedToInt(Event::GetClientCoords(
        aPresContext, aEvent, aEvent->mRefPoint, CSSDoublePoint{0, 0}));
  }

  *aStatus = nsEventStatus_eIgnore;

  if (aEvent->mClass == eQueryContentEventClass) {
    HandleQueryContentEvent(aEvent->AsQueryContentEvent());
    return NS_OK;
  }

  WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
  if (touchEvent && mInTouchDrag) {
    if (touchEvent->mMessage == eTouchMove) {
      GenerateDragGesture(aPresContext, touchEvent);
    } else {
      mInTouchDrag = false;
      StopTrackingDragGesture(true);
    }
  }

  if (mMouseEnterLeaveHelper && aEvent->IsTrusted()) {
    // When the last `mouseover` event target is removed from the document,
    // we makes mMouseEnterLeaveHelper update the last deepest `mouseenter`
    // event target to the removed node parent and mark it as not the following
    // `mouseout` event target.  However, the other browsers may dispatch
    // `mouseout` on it if it's restored "immediately".  Therefore, we use
    // the next animation frame as the deadline.  ContentRemoved() enqueues a
    // synthesized `mousemove` to dispatch mouse boundary events under the
    // mouse cursor soon and the synthesized event (or eMouseExitFromWidget if
    // our window is moved) will reach here at latest the next animation frame.
    // Therefore, we can use the event as the deadline.  If the removed last
    // `mouseover` target is reconnected before a synthesized mouse event or
    // a real mouse event, let's restore it as the following `mouseout` event
    // target.  Otherwise, e.g., a keyboard event, let's forget it.
    mMouseEnterLeaveHelper->TryToRestorePendingRemovedOverTarget(aEvent);
  }

  static constexpr auto const allowSynthesisForTests = []() -> bool {
    nsCOMPtr<nsIDragService> dragService =
        do_GetService("@mozilla.org/widget/dragservice;1");
    return dragService &&
           !dragService->GetNeverAllowSessionIsSynthesizedForTests();
  };

  switch (aEvent->mMessage) {
    case eContextMenu:
      if (PointerLockManager::IsLocked()) {
        return NS_ERROR_DOM_INVALID_STATE_ERR;
      }
      break;
    case eMouseTouchDrag:
      mInTouchDrag = true;
      BeginTrackingDragGesture(aPresContext, mouseEvent, aTargetFrame);
      break;
    case eMouseDown: {
      switch (mouseEvent->mButton) {
        case MouseButton::ePrimary:
          BeginTrackingDragGesture(aPresContext, mouseEvent, aTargetFrame);
          mLastLeftMouseDownInfo.mClickCount = mouseEvent->mClickCount;
          SetClickCount(mouseEvent, aStatus);
          sNormalLMouseEventInProcess = true;
          break;
        case MouseButton::eMiddle:
          mLastMiddleMouseDownInfo.mClickCount = mouseEvent->mClickCount;
          SetClickCount(mouseEvent, aStatus);
          break;
        case MouseButton::eSecondary:
          mLastRightMouseDownInfo.mClickCount = mouseEvent->mClickCount;
          SetClickCount(mouseEvent, aStatus);
          break;
      }
      if (!StaticPrefs::dom_popup_experimental()) {
        NotifyTargetUserActivation(aEvent, aTargetContent);
      }
      break;
    }
    case eMouseUp: {
      switch (mouseEvent->mButton) {
        case MouseButton::ePrimary:
          if (StaticPrefs::ui_click_hold_context_menus()) {
            KillClickHoldTimer();
          }
          mInTouchDrag = false;
          StopTrackingDragGesture(true);
          sNormalLMouseEventInProcess = false;
          // then fall through...
          [[fallthrough]];
        case MouseButton::eSecondary:
        case MouseButton::eMiddle:
          RefPtr<EventStateManager> esm =
              ESMFromContentOrThis(aOverrideClickTarget);
          esm->SetClickCount(mouseEvent, aStatus, aOverrideClickTarget);
          break;
      }
      break;
    }
    case eMouseEnterIntoWidget:
      PointerEventHandler::UpdateActivePointerState(mouseEvent, aTargetContent);
      // In some cases on e10s eMouseEnterIntoWidget
      // event was sent twice into child process of content.
      // (From specific widget code (sending is not permanent) and
      // from ESM::DispatchMouseOrPointerBoundaryEvent (sending is permanent)).
      // IsCrossProcessForwardingStopped() helps to suppress sending accidental
      // event from widget code.
      aEvent->StopCrossProcessForwarding();
      break;
    case eMouseExitFromWidget:
      // If this is a remote frame, we receive eMouseExitFromWidget from the
      // parent the mouse exits our content. Since the parent may update the
      // cursor while the mouse is outside our frame, and since PuppetWidget
      // caches the current cursor internally, re-entering our content (say from
      // over a window edge) wont update the cursor if the cached value and the
      // current cursor match. So when the mouse exits a remote frame, clear the
      // cached widget cursor so a proper update will occur when the mouse
      // re-enters.
      if (XRE_IsContentProcess()) {
        ClearCachedWidgetCursor(mCurrentTarget);
      }

      // IsCrossProcessForwardingStopped() helps to suppress double event
      // sending into process of content. For more information see comment
      // above, at eMouseEnterIntoWidget case.
      aEvent->StopCrossProcessForwarding();

      // If the event is not a top-level window or puppet widget exit, then it's
      // not really an exit --- we may have traversed widget boundaries but
      // we're still in our toplevel window or puppet widget.
      if (mouseEvent->mExitFrom.value() !=
              WidgetMouseEvent::ePlatformTopLevel &&
          mouseEvent->mExitFrom.value() != WidgetMouseEvent::ePuppet) {
        // Treat it as a synthetic move so we don't generate spurious
        // "exit" or "move" events.  Any necessary "out" or "over" events
        // will be generated by GenerateMouseEnterExit
        mouseEvent->mMessage = eMouseMove;
        mouseEvent->mReason = WidgetMouseEvent::eSynthesized;
        // then fall through...
      } else {
        MOZ_ASSERT_IF(XRE_IsParentProcess(),
                      mouseEvent->mExitFrom.value() ==
                          WidgetMouseEvent::ePlatformTopLevel);
        MOZ_ASSERT_IF(XRE_IsContentProcess(), mouseEvent->mExitFrom.value() ==
                                                  WidgetMouseEvent::ePuppet);
        // We should synthetize corresponding pointer events
        GeneratePointerEnterExit(ePointerLeave, mouseEvent);
        GenerateMouseEnterExit(mouseEvent);
        // This is really an exit and should stop here
        aEvent->mMessage = eVoidEvent;
        break;
      }
      [[fallthrough]];
    case eMouseMove:
    case ePointerDown:
      if (aEvent->mMessage == ePointerDown) {
        PointerEventHandler::UpdateActivePointerState(mouseEvent,
                                                      aTargetContent);
        PointerEventHandler::ImplicitlyCapturePointer(aTargetFrame, aEvent);
        if (StaticPrefs::dom_popup_experimental()) {
          // https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event
          if (mouseEvent->mInputSource ==
              MouseEvent_Binding::MOZ_SOURCE_MOUSE) {
            NotifyTargetUserActivation(aEvent, aTargetContent);
          }
        } else if (mouseEvent->mInputSource !=
                   MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
          NotifyTargetUserActivation(aEvent, aTargetContent);
        }

        LightDismissOpenPopovers(aEvent, aTargetContent);
      }
      [[fallthrough]];
    case ePointerMove: {
      if (!mInTouchDrag &&
          PointerEventHandler::IsDragAndDropEnabled(*mouseEvent)) {
        GenerateDragGesture(aPresContext, mouseEvent);
      }
      // on the Mac, GenerateDragGesture() may not return until the drag
      // has completed and so |aTargetFrame| may have been deleted (moving
      // a bookmark, for example).  If this is the case, however, we know
      // that ClearFrameRefs() has been called and it cleared out
      // |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
      // into UpdateCursor().
      UpdateCursor(aPresContext, mouseEvent, mCurrentTarget, aStatus);

      UpdateLastRefPointOfMouseEvent(mouseEvent);
      if (PointerLockManager::IsLocked()) {
        ResetPointerToWindowCenterWhilePointerLocked(mouseEvent);
      }
      UpdateLastPointerPosition(mouseEvent);

      GenerateMouseEnterExit(mouseEvent);
      // Flush pending layout changes, so that later mouse move events
      // will go to the right nodes.
      FlushLayout(aPresContext);
      break;
    }
    case ePointerUp:
      LightDismissOpenPopovers(aEvent, aTargetContent);
      GenerateMouseEnterExit(mouseEvent);
      if (StaticPrefs::dom_popup_experimental() &&
          mouseEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_MOUSE) {
        NotifyTargetUserActivation(aEvent, aTargetContent);
      }
      break;
    case ePointerGotCapture:
      GenerateMouseEnterExit(mouseEvent);
      break;
    case eDragStart:
      if (StaticPrefs::ui_click_hold_context_menus()) {
        // an external drag gesture event came in, not generated internally
        // by Gecko. Make sure we get rid of the click-hold timer.
        KillClickHoldTimer();
      }
      break;
    case eDragOver: {
      WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
      MOZ_ASSERT(dragEvent);
      if (dragEvent->mFlags.mIsSynthesizedForTests &&
          allowSynthesisForTests()) {
        dragEvent->InitDropEffectForTests();
      }
      // Send the enter/exit events before eDrop.
      GenerateDragDropEnterExit(aPresContext, dragEvent);
      break;
    }
    case eDrop: {
      if (aEvent->mFlags.mIsSynthesizedForTests && allowSynthesisForTests()) {
        MOZ_ASSERT(aEvent->AsDragEvent());
        aEvent->AsDragEvent()->InitDropEffectForTests();
      }
      break;
    }
    case eKeyPress: {
      WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
      if ((keyEvent->ModifiersMatchWithAccessKey(AccessKeyType::eChrome) ||
           keyEvent->ModifiersMatchWithAccessKey(AccessKeyType::eContent)) &&
          // If the key binding of this event is a native key binding, we
          // prioritize it.
          !HasNativeKeyBindings(aTargetContent, keyEvent)) {
        // If the eKeyPress event will be sent to a remote process, this
        // process needs to wait reply from the remote process for checking if
        // preceding eKeyDown event is consumed.  If preceding eKeyDown event
        // is consumed in the remote process, BrowserChild won't send the event
        // back to this process.  So, only when this process receives a reply
        // eKeyPress event in BrowserParent, we should handle accesskey in this
        // process.
        if (IsTopLevelRemoteTarget(GetFocusedElement())) {
          // However, if there is no accesskey target for the key combination,
          // we don't need to wait reply from the remote process.  Otherwise,
          // Mark the event as waiting reply from remote process and stop
          // propagation in this process.
          if (CheckIfEventMatchesAccessKey(keyEvent, aPresContext)) {
            keyEvent->StopPropagation();
            keyEvent->MarkAsWaitingReplyFromRemoteProcess();
          }
        }
        // If the event target is in this process, we can handle accesskey now
        // since if preceding eKeyDown event was consumed, eKeyPress event
        // won't be dispatched by widget.  So, coming eKeyPress event means
        // that the preceding eKeyDown event wasn't consumed in this case.
        else {
          AutoTArray<uint32_t, 10> accessCharCodes;
          keyEvent->GetAccessKeyCandidates(accessCharCodes);

          if (HandleAccessKey(keyEvent, aPresContext, accessCharCodes)) {
            *aStatus = nsEventStatus_eConsumeNoDefault;
          }
        }
      }
    }
      // then fall through...
      [[fallthrough]];
    case eKeyDown:
      if (aEvent->mMessage == eKeyDown) {
        NotifyTargetUserActivation(aEvent, aTargetContent);
      }
      [[fallthrough]];
    case eKeyUp: {
      Element* element = GetFocusedElement();
      if (element) {
        mCurrentTargetContent = element;
      }

      // NOTE: Don't refer TextComposition::IsComposing() since UI Events
      //       defines that KeyboardEvent.isComposing is true when it's
      //       dispatched after compositionstart and compositionend.
      //       TextComposition::IsComposing() is false even before
      //       compositionend if there is no composing string.
      //       And also don't expose other document's composition state.
      //       A native IME context is typically shared by multiple documents.
      //       So, don't use GetTextCompositionFor(nsIWidget*) here.
      RefPtr<TextComposition> composition =
          IMEStateManager::GetTextCompositionFor(aPresContext);
      aEvent->AsKeyboardEvent()->mIsComposing = !!composition;

      // Widget may need to perform default action for specific keyboard
      // event if it's not consumed.  In this case, widget has already marked
      // the event as "waiting reply from remote process".  However, we need
      // to reset it if the target (focused content) isn't in a remote process
      // because PresShell needs to check if it's marked as so before
      // dispatching events into the DOM tree.
      if (aEvent->IsWaitingReplyFromRemoteProcess() &&
          !aEvent->PropagationStopped() && !IsTopLevelRemoteTarget(element)) {
        aEvent->ResetWaitingReplyFromRemoteProcessState();
      }
    } break;
    case eWheel:
    case eWheelOperationStart:
    case eWheelOperationEnd: {
      NS_ASSERTION(aEvent->IsTrusted(),
                   "Untrusted wheel event shouldn't be here");
      using DeltaModeCheckingState = WidgetWheelEvent::DeltaModeCheckingState;

      if (Element* element = GetFocusedElement()) {
        mCurrentTargetContent = element;
      }

      if (aEvent->mMessage != eWheel) {
        break;
      }

      WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
      WheelPrefs::GetInstance()->ApplyUserPrefsToDelta(wheelEvent);

      // If we won't dispatch a DOM event for this event, nothing to do anymore.
      if (!wheelEvent->IsAllowedToDispatchDOMEvent()) {
        break;
      }

      if (StaticPrefs::dom_event_wheel_deltaMode_lines_always_disabled()) {
        wheelEvent->mDeltaModeCheckingState = DeltaModeCheckingState::Unchecked;
      } else if (ShouldAlwaysUseLineDeltas()) {
        wheelEvent->mDeltaModeCheckingState = DeltaModeCheckingState::Checked;
      } else {
        wheelEvent->mDeltaModeCheckingState = DeltaModeCheckingState::Unknown;
      }

      // Init lineOrPageDelta values for line scroll events for some devices
      // on some platforms which might dispatch wheel events which don't
      // have lineOrPageDelta values.  And also, if delta values are
      // customized by prefs, this recomputes them.
      DeltaAccumulator::GetInstance()->InitLineOrPageDelta(aTargetFrame, this,
                                                           wheelEvent);
    } break;
    case eSetSelection: {
      RefPtr<Element> focuedElement = GetFocusedElement();
      IMEStateManager::HandleSelectionEvent(aPresContext, focuedElement,
                                            aEvent->AsSelectionEvent());
      break;
    }
    case eContentCommandCut:
    case eContentCommandCopy:
    case eContentCommandPaste:
    case eContentCommandDelete:
    case eContentCommandUndo:
    case eContentCommandRedo:
    case eContentCommandPasteTransferable:
    case eContentCommandLookUpDictionary:
      DoContentCommandEvent(aEvent->AsContentCommandEvent());
      break;
    case eContentCommandInsertText:
      DoContentCommandInsertTextEvent(aEvent->AsContentCommandEvent());
      break;
    case eContentCommandReplaceText:
      DoContentCommandReplaceTextEvent(aEvent->AsContentCommandEvent());
      break;
    case eContentCommandScroll:
      DoContentCommandScrollEvent(aEvent->AsContentCommandEvent());
      break;
    case eCompositionStart:
      if (aEvent->IsTrusted()) {
        // If the event is trusted event, set the selected text to data of
        // composition event.
        WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
        WidgetQueryContentEvent querySelectedTextEvent(
            true, eQuerySelectedText, compositionEvent->mWidget);
        HandleQueryContentEvent(&querySelectedTextEvent);
        if (querySelectedTextEvent.FoundSelection()) {
          compositionEvent->mData = querySelectedTextEvent.mReply->DataRef();
        }
        NS_ASSERTION(querySelectedTextEvent.Succeeded(),
                     "Failed to get selected text");
      }
      break;
    case eTouchStart:
      SetGestureDownPoint(aEvent->AsTouchEvent());
      break;
    case eTouchEnd:
      if (!StaticPrefs::dom_popup_experimental()) {
        NotifyTargetUserActivation(aEvent, aTargetContent);
      }
      break;
    default:
      break;
  }
  return NS_OK;
}

// Returns true if this event is likely an user activation for a link or
// a link-like button, where modifier keys are likely be used for controlling
// where the link is opened.
//
// The modifiers associated with the user activation is used for controlling
// where the `window.open` is opened into.
static bool CanReflectModifiersToUserActivation(WidgetInputEvent* aEvent) {
  if (StaticPrefs::dom_popup_experimental()) {
    MOZ_ASSERT(aEvent->mMessage == eKeyDown ||
               aEvent->mMessage == ePointerDown ||
               aEvent->mMessage == ePointerUp);
  } else {
    MOZ_ASSERT(aEvent->mMessage == eKeyDown || aEvent->mMessage == eMouseDown ||
               aEvent->mMessage == ePointerDown ||
               aEvent->mMessage == eTouchEnd);
  }

  WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
  if (keyEvent) {
    return keyEvent->CanReflectModifiersToUserActivation();
  }

  return true;
}

void EventStateManager::NotifyTargetUserActivation(WidgetEvent* aEvent,
                                                   nsIContent* aTargetContent) {
  if (!aEvent->IsTrusted()) {
    return;
  }

  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
  if (mouseEvent && !mouseEvent->IsReal()) {
    return;
  }

  nsCOMPtr<nsINode> node = aTargetContent;
  if (!node) {
    return;
  }

  Document* doc = node->OwnerDoc();
  if (!doc) {
    return;
  }

  // Don't gesture activate for key events for keys which are likely
  // to be interaction with the browser, OS.
  WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
  if (keyEvent && !keyEvent->CanUserGestureActivateTarget()) {
    return;
  }

  // Touch gestures that end outside the drag target were touches that turned
  // into scroll/pan/swipe actions. We don't want to gesture activate on such
  // actions, we want to only gesture activate on touches that are taps.
  // That is, touches that end in roughly the same place that they started.
  if ((aEvent->mMessage == eTouchEnd ||
       (aEvent->mMessage == ePointerUp &&
        aEvent->AsPointerEvent()->mInputSource ==
            MouseEvent_Binding::MOZ_SOURCE_TOUCH)) &&
      IsEventOutsideDragThreshold(aEvent->AsInputEvent())) {
    return;
  }

  // Do not treat the click on scrollbar as a user interaction with the web
  // content.
  if (StaticPrefs::dom_user_activation_ignore_scrollbars() &&
      ((StaticPrefs::dom_popup_experimental() &&
        (aEvent->mMessage == ePointerDown || aEvent->mMessage == ePointerUp)) ||
       (!StaticPrefs::dom_popup_experimental() &&
        (aEvent->mMessage == eMouseDown ||
         aEvent->mMessage == ePointerDown))) &&
      aTargetContent->IsInNativeAnonymousSubtree()) {
    nsIContent* current = aTargetContent;
    do {
      nsIContent* root = current->GetClosestNativeAnonymousSubtreeRoot();
      if (!root) {
        break;
      }
      if (root->IsXULElement(nsGkAtoms::scrollbar)) {
        return;
      }
      current = root->GetParent();
    } while (current);
  }

#ifdef DEBUG
  if (StaticPrefs::dom_popup_experimental()) {
    MOZ_ASSERT(aEvent->mMessage == eKeyDown ||
               aEvent->mMessage == ePointerDown ||
               aEvent->mMessage == ePointerUp);
  } else {
    MOZ_ASSERT(aEvent->mMessage == eKeyDown || aEvent->mMessage == eMouseDown ||
               aEvent->mMessage == ePointerDown ||
               aEvent->mMessage == eTouchEnd);
  }
#endif

  UserActivation::Modifiers modifiers;
  if (WidgetInputEvent* inputEvent = aEvent->AsInputEvent()) {
    if (CanReflectModifiersToUserActivation(inputEvent)) {
      if (inputEvent->IsShift()) {
        modifiers.SetShift();
      }
      if (inputEvent->IsMeta()) {
        modifiers.SetMeta();
      }
      if (inputEvent->IsControl()) {
        modifiers.SetControl();
      }
      if (inputEvent->IsAlt()) {
        modifiers.SetAlt();
      }

      WidgetMouseEvent* mouseEvent = inputEvent->AsMouseEvent();
      if (mouseEvent) {
        if (mouseEvent->mButton == MouseButton::eMiddle) {
          modifiers.SetMiddleMouse();
        }
      }
    }
  }
  doc->NotifyUserGestureActivation(modifiers);
}

// https://html.spec.whatwg.org/multipage/popover.html#popover-light-dismiss
void EventStateManager::LightDismissOpenPopovers(WidgetEvent* aEvent,
                                                 nsIContent* aTargetContent) {
  MOZ_ASSERT(aEvent->mMessage == ePointerDown || aEvent->mMessage == ePointerUp,
             "Light dismiss must be called for pointer up/down only");

  if (!aEvent->IsTrusted() || !aTargetContent) {
    return;
  }

  Element* topmostPopover = aTargetContent->OwnerDoc()->GetTopmostAutoPopover();
  if (!topmostPopover) {
    return;
  }

  // Pointerdown: set document's popover pointerdown target to the result of
  // running topmost clicked popover given target.
  if (aEvent->mMessage == ePointerDown) {
    mPopoverPointerDownTarget = aTargetContent->GetTopmostClickedPopover();
    return;
  }

  // Pointerup: hide open popovers.
  RefPtr<nsINode> ancestor = aTargetContent->GetTopmostClickedPopover();
  bool sameTarget = mPopoverPointerDownTarget == ancestor;
  mPopoverPointerDownTarget = nullptr;
  if (!sameTarget) {
    return;
  }

  if (!ancestor) {
    ancestor = aTargetContent->OwnerDoc();
  }
  RefPtr<Document> doc(ancestor->OwnerDoc());
  doc->HideAllPopoversUntil(*ancestor, falsetrue);
}

already_AddRefed<EventStateManager> EventStateManager::ESMFromContentOrThis(
    nsIContent* aContent) {
  if (aContent) {
    PresShell* presShell = aContent->OwnerDoc()->GetPresShell();
    if (presShell) {
      nsPresContext* prescontext = presShell->GetPresContext();
      if (prescontext) {
        RefPtr<EventStateManager> esm = prescontext->EventStateManager();
        if (esm) {
          return esm.forget();
        }
      }
    }
  }

  RefPtr<EventStateManager> esm = this;
  return esm.forget();
}

EventStateManager::LastMouseDownInfo& EventStateManager::GetLastMouseDownInfo(
    int16_t aButton) {
  switch (aButton) {
    case MouseButton::ePrimary:
      return mLastLeftMouseDownInfo;
    case MouseButton::eMiddle:
      return mLastMiddleMouseDownInfo;
    case MouseButton::eSecondary:
      return mLastRightMouseDownInfo;
    default:
      MOZ_ASSERT_UNREACHABLE("This button shouldn't use this method");
      return mLastLeftMouseDownInfo;
  }
}

void EventStateManager::HandleQueryContentEvent(
    WidgetQueryContentEvent* aEvent) {
  switch (aEvent->mMessage) {
    case eQuerySelectedText:
    case eQueryTextContent:
    case eQueryCaretRect:
    case eQueryTextRect:
    case eQueryEditorRect:
      if (!IsTargetCrossProcess(aEvent)) {
        break;
      }
      // Will not be handled locally, remote the event
      GetCrossProcessTarget()->HandleQueryContentEvent(*aEvent);
      return;
    // Following events have not been supported in e10s mode yet.
    case eQueryContentState:
    case eQuerySelectionAsTransferable:
    case eQueryCharacterAtPoint:
    case eQueryDOMWidgetHittest:
    case eQueryTextRectArray:
    case eQueryDropTargetHittest:
      break;
    default:
      return;
  }

  // If there is an IMEContentObserver, we need to handle QueryContentEvent
  // with it.
  // eQueryDropTargetHittest is not really an IME event, though
  if (mIMEContentObserver && aEvent->mMessage != eQueryDropTargetHittest) {
    RefPtr<IMEContentObserver> contentObserver = mIMEContentObserver;
    contentObserver->HandleQueryContentEvent(aEvent);
    return;
  }

  ContentEventHandler handler(mPresContext);
  handler.HandleQueryContentEvent(aEvent);
}

static AccessKeyType GetAccessKeyTypeFor(nsISupports* aDocShell) {
  nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(aDocShell));
  if (!treeItem) {
    return AccessKeyType::eNone;
  }

  switch (treeItem->ItemType()) {
    case nsIDocShellTreeItem::typeChrome:
      return AccessKeyType::eChrome;
    case nsIDocShellTreeItem::typeContent:
      return AccessKeyType::eContent;
    default:
      return AccessKeyType::eNone;
  }
}

static bool IsAccessKeyTarget(Element* aElement, nsAString& aKey) {
  // Use GetAttr because we want Unicode case=insensitive matching
  // XXXbz shouldn't this be case-sensitive, per spec?
  nsString contentKey;
  if (!aElement || !aElement->GetAttr(nsGkAtoms::accesskey, contentKey) ||
      !contentKey.Equals(aKey, nsCaseInsensitiveStringComparator)) {
    return false;
  }

  if (!aElement->IsXULElement()) {
    return true;
  }

  // For XUL we do visibility checks.
  nsIFrame* frame = aElement->GetPrimaryFrame();
  if (!frame) {
    return false;
  }

  if (frame->IsFocusable()) {
    return true;
  }

  if (!frame->IsVisibleConsideringAncestors()) {
    return false;
  }

  // XUL controls can be activated.
  nsCOMPtr<nsIDOMXULControlElement> control = aElement->AsXULControl();
  if (control) {
    return true;
  }

  // XUL label elements are never focusable, so we need to check for them
  // explicitly before giving up.
  if (aElement->IsXULElement(nsGkAtoms::label)) {
    return true;
  }

  return false;
}

bool EventStateManager::CheckIfEventMatchesAccessKey(
    WidgetKeyboardEvent* aEvent, nsPresContext* aPresContext) {
  AutoTArray<uint32_t, 10> accessCharCodes;
  aEvent->GetAccessKeyCandidates(accessCharCodes);
  return WalkESMTreeToHandleAccessKey(aEvent, aPresContext, accessCharCodes,
                                      nullptr, eAccessKeyProcessingNormal,
                                      false);
}

bool EventStateManager::LookForAccessKeyAndExecute(
    nsTArray<uint32_t>& aAccessCharCodes, bool aIsTrustedEvent, bool aIsRepeat,
    bool aExecute) {
  int32_t count, start = -1;
  if (Element* focusedElement = GetFocusedElement()) {
    start = mAccessKeys.IndexOf(focusedElement);
    if (start == -1 && focusedElement->IsInNativeAnonymousSubtree()) {
      start = mAccessKeys.IndexOf(Element::FromNodeOrNull(
          focusedElement->GetClosestNativeAnonymousSubtreeRootParentOrHost()));
    }
  }
  RefPtr<Element> element;
  int32_t length = mAccessKeys.Count();
  for (uint32_t i = 0; i < aAccessCharCodes.Length(); ++i) {
    uint32_t ch = aAccessCharCodes[i];
    nsAutoString accessKey;
    AppendUCS4ToUTF16(ch, accessKey);
    for (count = 1; count <= length; ++count) {
      // mAccessKeys always stores Element instances.
      MOZ_DIAGNOSTIC_ASSERT(length == mAccessKeys.Count());
      element = mAccessKeys[(start + count) % length];
      if (IsAccessKeyTarget(element, accessKey)) {
        if (!aExecute) {
          return true;
        }
        Document* doc = element->OwnerDoc();
        const bool shouldActivate = [&] {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=87 H=97 G=91

¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.21Angebot  Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können  ¤

*Eine klare Vorstellung vom Zielzustand






Wurzel

Bemerkung:

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Anfrage:

Dauer der Verarbeitung:

Sekunden

sprechenden Kalenders