tor-browser

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

EventStateManager.cpp (302180B)


      1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
      2 /* vim: set ts=8 sts=2 et sw=2 tw=80: */
      3 /* This Source Code Form is subject to the terms of the Mozilla Public
      4 * License, v. 2.0. If a copy of the MPL was not distributed with this
      5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
      6 
      7 #include "EventStateManager.h"
      8 
      9 #include "ContentEventHandler.h"
     10 #include "IMEContentObserver.h"
     11 #include "RemoteDragStartData.h"
     12 #include "Units.h"
     13 #include "WheelHandlingHelper.h"
     14 #include "imgIContainer.h"
     15 #include "mozilla/AppShutdown.h"
     16 #include "mozilla/AsyncEventDispatcher.h"
     17 #include "mozilla/Attributes.h"
     18 #include "mozilla/ConnectedAncestorTracker.h"
     19 #include "mozilla/EditorBase.h"
     20 #include "mozilla/EventDispatcher.h"
     21 #include "mozilla/EventForwards.h"
     22 #include "mozilla/FocusModel.h"
     23 #include "mozilla/HTMLEditor.h"
     24 #include "mozilla/Hal.h"
     25 #include "mozilla/IMEStateManager.h"
     26 #include "mozilla/Likely.h"
     27 #include "mozilla/Logging.h"
     28 #include "mozilla/LookAndFeel.h"
     29 #include "mozilla/MathAlgorithms.h"
     30 #include "mozilla/MiscEvents.h"
     31 #include "mozilla/MouseEvents.h"
     32 #include "mozilla/PointerLockManager.h"
     33 #include "mozilla/Preferences.h"
     34 #include "mozilla/PresShell.h"
     35 #include "mozilla/ProfilerLabels.h"
     36 #include "mozilla/ScopeExit.h"
     37 #include "mozilla/ScrollContainerFrame.h"
     38 #include "mozilla/ScrollTypes.h"
     39 #include "mozilla/Services.h"
     40 #include "mozilla/StaticPrefs_accessibility.h"
     41 #include "mozilla/StaticPrefs_browser.h"
     42 #include "mozilla/StaticPrefs_dom.h"
     43 #include "mozilla/StaticPrefs_layout.h"
     44 #include "mozilla/StaticPrefs_mousewheel.h"
     45 #include "mozilla/StaticPrefs_ui.h"
     46 #include "mozilla/StaticPrefs_zoom.h"
     47 #include "mozilla/TextComposition.h"
     48 #include "mozilla/TextControlElement.h"
     49 #include "mozilla/TextEditor.h"
     50 #include "mozilla/TextEvents.h"
     51 #include "mozilla/TouchEvents.h"
     52 #include "mozilla/UniquePtr.h"
     53 #include "mozilla/dom/AncestorIterator.h"
     54 #include "mozilla/dom/BrowserBridgeChild.h"
     55 #include "mozilla/dom/BrowserChild.h"
     56 #include "mozilla/dom/BrowsingContext.h"
     57 #include "mozilla/dom/CanonicalBrowsingContext.h"
     58 #include "mozilla/dom/ContentChild.h"
     59 #include "mozilla/dom/ContentParent.h"
     60 #include "mozilla/dom/DOMIntersectionObserver.h"
     61 #include "mozilla/dom/DataTransfer.h"
     62 #include "mozilla/dom/Document.h"
     63 #include "mozilla/dom/DragEvent.h"
     64 #include "mozilla/dom/Event.h"
     65 #include "mozilla/dom/FrameLoaderBinding.h"
     66 #include "mozilla/dom/HTMLDialogElement.h"
     67 #include "mozilla/dom/HTMLInputElement.h"
     68 #include "mozilla/dom/HTMLLabelElement.h"
     69 #include "mozilla/dom/MouseEventBinding.h"
     70 #include "mozilla/dom/PointerEventHandler.h"
     71 #include "mozilla/dom/PopoverData.h"
     72 #include "mozilla/dom/Record.h"
     73 #include "mozilla/dom/Selection.h"
     74 #include "mozilla/dom/UIEvent.h"
     75 #include "mozilla/dom/UIEventBinding.h"
     76 #include "mozilla/dom/UserActivation.h"
     77 #include "mozilla/dom/WheelEventBinding.h"
     78 #include "mozilla/glean/ProcesstoolsMetrics.h"
     79 #include "nsCOMPtr.h"
     80 #include "nsComboboxControlFrame.h"
     81 #include "nsCommandParams.h"
     82 #include "nsContentAreaDragDrop.h"
     83 #include "nsContentUtils.h"
     84 #include "nsCopySupport.h"
     85 #include "nsFocusManager.h"
     86 #include "nsFontMetrics.h"
     87 #include "nsFrameLoaderOwner.h"
     88 #include "nsFrameManager.h"
     89 #include "nsFrameSelection.h"
     90 #include "nsGenericHTMLElement.h"
     91 #include "nsGkAtoms.h"
     92 #include "nsIBaseWindow.h"
     93 #include "nsIBrowserChild.h"
     94 #include "nsIClipboard.h"
     95 #include "nsIContent.h"
     96 #include "nsIContentInlines.h"
     97 #include "nsIController.h"
     98 #include "nsICookieJarSettings.h"
     99 #include "nsIDOMXULControlElement.h"
    100 #include "nsIDocShell.h"
    101 #include "nsIDocumentViewer.h"
    102 #include "nsIDragService.h"
    103 #include "nsIDragSession.h"
    104 #include "nsIFormControl.h"
    105 #include "nsIFrame.h"
    106 #include "nsIInterfaceRequestorUtils.h"
    107 #include "nsIObserverService.h"
    108 #include "nsIProperties.h"
    109 #include "nsISupportsPrimitives.h"
    110 #include "nsITimer.h"
    111 #include "nsIWeakReferenceUtils.h"
    112 #include "nsIWebNavigation.h"
    113 #include "nsIWidget.h"
    114 #include "nsLayoutUtils.h"
    115 #include "nsLiteralString.h"
    116 #include "nsMenuPopupFrame.h"
    117 #include "nsNameSpaceManager.h"
    118 #include "nsPIDOMWindow.h"
    119 #include "nsPIWindowRoot.h"
    120 #include "nsPresContext.h"
    121 #include "nsServiceManagerUtils.h"
    122 #include "nsSubDocumentFrame.h"
    123 #include "nsTArray.h"
    124 #include "nsTreeBodyFrame.h"
    125 #include "nsUnicharUtils.h"
    126 
    127 #ifdef XP_MACOSX
    128 #  import <ApplicationServices/ApplicationServices.h>
    129 #endif
    130 
    131 namespace mozilla {
    132 
    133 using namespace dom;
    134 
    135 // Log the mouse cursor updates.  That should be updated only by the events for
    136 // the last pointer which is actually handled as a user input.  I.e., should not
    137 // be updated by synthesized mouse/pointer move events which are not for the
    138 // last pointer.
    139 // - MouseCursorUpdate:3 logs only when EventStateManager and BrowserParent
    140 // updated the cursor.
    141 // - MouseCursorUpdate:4 logs any results when BrowserParent handles that.
    142 // - MouseCursorUpdate:5 logs when UpdateCursor() stopped updating the cursor.
    143 //
    144 // NOTE: This can work only on debug builds for avoiding to the damage to the
    145 // performance.
    146 LazyLogModule gMouseCursorUpdates("MouseCursorUpdates");
    147 
    148 static const LayoutDeviceIntPoint kInvalidRefPoint =
    149    LayoutDeviceIntPoint(-1, -1);
    150 
    151 static uint32_t gMouseOrKeyboardEventCounter = 0;
    152 static nsITimer* gUserInteractionTimer = nullptr;
    153 static nsITimerCallback* gUserInteractionTimerCallback = nullptr;
    154 
    155 static const double kCursorLoadingTimeout = 1000;  // ms
    156 constinit static AutoWeakFrame gLastCursorSourceFrame;
    157 static TimeStamp gLastCursorUpdateTime;
    158 static TimeStamp gTypingStartTime;
    159 static TimeStamp gTypingEndTime;
    160 static int32_t gTypingInteractionKeyPresses = 0;
    161 MOZ_RUNINIT static dom::InteractionData gTypingInteraction = {};
    162 
    163 static inline int32_t RoundDown(double aDouble) {
    164  return (aDouble > 0) ? static_cast<int32_t>(floor(aDouble))
    165                       : static_cast<int32_t>(ceil(aDouble));
    166 }
    167 
    168 static bool IsSelectingLink(nsIFrame* aTargetFrame) {
    169  if (!aTargetFrame) {
    170    return false;
    171  }
    172  const nsFrameSelection* frameSel = aTargetFrame->GetConstFrameSelection();
    173  if (!frameSel || !frameSel->GetDragState()) {
    174    return false;
    175  }
    176 
    177  if (!nsContentUtils::GetClosestLinkInFlatTree(aTargetFrame->GetContent())) {
    178    return false;
    179  }
    180  return true;
    181 }
    182 
    183 static UniquePtr<WidgetMouseEvent> CreateMouseOrPointerWidgetEvent(
    184    WidgetMouseEvent* aMouseEvent, EventMessage aMessage,
    185    EventTarget* aRelatedTarget);
    186 
    187 /**
    188 * Returns the common ancestor for mouseup purpose, given the
    189 * current mouseup target and the previous mousedown target.
    190 */
    191 static nsINode* GetCommonAncestorForMouseUp(
    192    nsINode* aCurrentMouseUpTarget, nsINode* aLastMouseDownTarget,
    193    const Maybe<FormControlType>& aLastMouseDownInputControlType) {
    194  if (!aCurrentMouseUpTarget || !aLastMouseDownTarget) {
    195    return nullptr;
    196  }
    197 
    198  if (aCurrentMouseUpTarget == aLastMouseDownTarget) {
    199    return aCurrentMouseUpTarget;
    200  }
    201 
    202  // Build the chain of parents
    203  AutoTArray<nsINode*, 30> parents1;
    204  do {
    205    parents1.AppendElement(aCurrentMouseUpTarget);
    206    aCurrentMouseUpTarget = aCurrentMouseUpTarget->GetFlattenedTreeParentNode();
    207  } while (aCurrentMouseUpTarget);
    208 
    209  AutoTArray<nsINode*, 30> parents2;
    210  do {
    211    parents2.AppendElement(aLastMouseDownTarget);
    212    if (aLastMouseDownTarget == parents1.LastElement()) {
    213      break;
    214    }
    215    aLastMouseDownTarget = aLastMouseDownTarget->GetFlattenedTreeParentNode();
    216  } while (aLastMouseDownTarget);
    217 
    218  // Find where the parent chain differs
    219  uint32_t pos1 = parents1.Length();
    220  uint32_t pos2 = parents2.Length();
    221  nsINode* parent = nullptr;
    222  for (uint32_t len = std::min(pos1, pos2); len > 0; --len) {
    223    nsINode* child1 = parents1.ElementAt(--pos1);
    224    nsINode* child2 = parents2.ElementAt(--pos2);
    225    if (child1 != child2) {
    226      break;
    227    }
    228 
    229    // If the input control type is different between mouseup and mousedown,
    230    // this is not a valid click.
    231    if (HTMLInputElement* input = HTMLInputElement::FromNodeOrNull(child1)) {
    232      if (aLastMouseDownInputControlType.isSome() &&
    233          aLastMouseDownInputControlType.ref() != input->ControlType()) {
    234        break;
    235      }
    236    }
    237    parent = child1;
    238  }
    239 
    240  return parent;
    241 }
    242 
    243 static bool HasNativeKeyBindings(nsIContent* aContent,
    244                                 WidgetKeyboardEvent* aEvent) {
    245  MOZ_ASSERT(aEvent->mMessage == eKeyPress);
    246 
    247  if (!aContent) {
    248    return false;
    249  }
    250 
    251  const RefPtr<dom::Element> targetElement = aContent->AsElement();
    252  if (!targetElement) {
    253    return false;
    254  }
    255 
    256  const auto type = [&]() -> Maybe<NativeKeyBindingsType> {
    257    if (BrowserParent::GetFrom(targetElement)) {
    258      const nsCOMPtr<nsIWidget> widget = aEvent->mWidget;
    259      if (MOZ_UNLIKELY(!widget)) {
    260        return Nothing();
    261      }
    262      widget::InputContext context = widget->GetInputContext();
    263      return context.mIMEState.IsEditable()
    264                 ? Some(context.GetNativeKeyBindingsType())
    265                 : Nothing();
    266    }
    267 
    268    const auto* const textControlElement =
    269        TextControlElement::FromNode(targetElement);
    270    if (textControlElement &&
    271        textControlElement->IsSingleLineTextControlOrTextArea() &&
    272        !textControlElement->IsInDesignMode()) {
    273      return textControlElement->IsTextArea()
    274                 ? Some(NativeKeyBindingsType::MultiLineEditor)
    275                 : Some(NativeKeyBindingsType::SingleLineEditor);
    276    }
    277    return targetElement->IsEditable()
    278               ? Some(NativeKeyBindingsType::RichTextEditor)
    279               : Nothing();
    280  }();
    281  if (type.isNothing()) {
    282    return false;
    283  }
    284 
    285  const nsTArray<CommandInt>& commands =
    286      aEvent->EditCommandsConstRef(type.value());
    287  return !commands.IsEmpty();
    288 }
    289 
    290 LazyLogModule sMouseBoundaryLog("MouseBoundaryEvents");
    291 LazyLogModule sPointerBoundaryLog("PointerBoundaryEvents");
    292 
    293 /******************************************************************/
    294 /* mozilla::UITimerCallback                                       */
    295 /******************************************************************/
    296 
    297 class UITimerCallback final : public nsITimerCallback, public nsINamed {
    298 public:
    299  UITimerCallback() : mPreviousCount(0) {}
    300  NS_DECL_ISUPPORTS
    301  NS_DECL_NSITIMERCALLBACK
    302  NS_DECL_NSINAMED
    303 private:
    304  ~UITimerCallback() = default;
    305  uint32_t mPreviousCount;
    306 };
    307 
    308 NS_IMPL_ISUPPORTS(UITimerCallback, nsITimerCallback, nsINamed)
    309 
    310 // If aTimer is nullptr, this method always sends "user-interaction-inactive"
    311 // notification.
    312 NS_IMETHODIMP
    313 UITimerCallback::Notify(nsITimer* aTimer) {
    314  nsCOMPtr<nsIObserverService> obs = mozilla::services::GetObserverService();
    315  // ObserverService shutdown happens after XPCOMShutdownThreads.
    316  if (!obs || AppShutdown::IsInOrBeyond(ShutdownPhase::XPCOMShutdownThreads)) {
    317    return NS_ERROR_FAILURE;
    318  }
    319  if ((gMouseOrKeyboardEventCounter == mPreviousCount) || !aTimer) {
    320    gMouseOrKeyboardEventCounter = 0;
    321    obs->NotifyObservers(nullptr, "user-interaction-inactive", nullptr);
    322    if (gUserInteractionTimer) {
    323      gUserInteractionTimer->Cancel();
    324      NS_RELEASE(gUserInteractionTimer);
    325    }
    326  } else {
    327    obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
    328    EventStateManager::UpdateUserActivityTimer();
    329 
    330    if (XRE_IsParentProcess()) {
    331      hal::BatteryInformation batteryInfo;
    332      hal::GetCurrentBatteryInformation(&batteryInfo);
    333      glean::power_battery::percentage_when_user_active.AccumulateSingleSample(
    334          uint64_t(batteryInfo.level() * 100));
    335    }
    336  }
    337  mPreviousCount = gMouseOrKeyboardEventCounter;
    338  return NS_OK;
    339 }
    340 
    341 NS_IMETHODIMP
    342 UITimerCallback::GetName(nsACString& aName) {
    343  aName.AssignLiteral("UITimerCallback_timer");
    344  return NS_OK;
    345 }
    346 
    347 /******************************************************************/
    348 /* mozilla::OverOutElementsWrapper                                */
    349 /******************************************************************/
    350 
    351 NS_IMPL_CYCLE_COLLECTION(OverOutElementsWrapper, mDeepestEnterEventTarget,
    352                         mDispatchingOverEventTarget,
    353                         mDispatchingOutOrDeepestLeaveEventTarget)
    354 NS_IMPL_CYCLE_COLLECTING_ADDREF(OverOutElementsWrapper)
    355 NS_IMPL_CYCLE_COLLECTING_RELEASE(OverOutElementsWrapper)
    356 
    357 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(OverOutElementsWrapper)
    358  NS_INTERFACE_MAP_ENTRY(nsISupports)
    359 NS_INTERFACE_MAP_END
    360 
    361 already_AddRefed<nsIWidget> OverOutElementsWrapper::GetLastOverWidget() const {
    362  nsCOMPtr<nsIWidget> widget = do_QueryReferent(mLastOverWidget);
    363  return widget.forget();
    364 }
    365 
    366 void OverOutElementsWrapper::ContentRemoved(nsIContent& aContent) {
    367  if (!mDeepestEnterEventTarget) {
    368    return;
    369  }
    370 
    371  if (!nsContentUtils::ContentIsFlattenedTreeDescendantOf(
    372          mDeepestEnterEventTarget, &aContent)) {
    373    return;
    374  }
    375 
    376  LogModule* const logModule = mType == BoundaryEventType::Mouse
    377                                   ? sMouseBoundaryLog
    378                                   : sPointerBoundaryLog;
    379 
    380  if (mDispatchingOverEventTarget &&
    381      (mDeepestEnterEventTarget == mDispatchingOverEventTarget ||
    382       nsContentUtils::ContentIsFlattenedTreeDescendantOf(
    383           mDispatchingOverEventTarget, &aContent))) {
    384    if (mDispatchingOverEventTarget ==
    385        mDispatchingOutOrDeepestLeaveEventTarget) {
    386      MOZ_LOG(logModule, LogLevel::Info,
    387              ("The dispatching \"%s\" event target (%p) is removed",
    388               LastOverEventTargetIsOutEventTarget() ? "out" : "leave",
    389               mDispatchingOutOrDeepestLeaveEventTarget.get()));
    390      mDispatchingOutOrDeepestLeaveEventTarget = nullptr;
    391    }
    392    MOZ_LOG(logModule, LogLevel::Info,
    393            ("The dispatching \"over\" event target (%p) is removed",
    394             mDispatchingOverEventTarget.get()));
    395    mDispatchingOverEventTarget = nullptr;
    396  }
    397  if (mDispatchingOutOrDeepestLeaveEventTarget &&
    398      (mDeepestEnterEventTarget == mDispatchingOutOrDeepestLeaveEventTarget ||
    399       nsContentUtils::ContentIsFlattenedTreeDescendantOf(
    400           mDispatchingOutOrDeepestLeaveEventTarget, &aContent))) {
    401    MOZ_LOG(logModule, LogLevel::Info,
    402            ("The dispatching \"%s\" event target (%p) is removed",
    403             LastOverEventTargetIsOutEventTarget() ? "out" : "leave",
    404             mDispatchingOutOrDeepestLeaveEventTarget.get()));
    405    mDispatchingOutOrDeepestLeaveEventTarget = nullptr;
    406  }
    407  MOZ_LOG(logModule, LogLevel::Info,
    408          ("The last \"%s\" event target (%p) is removed and now the last "
    409           "deepest enter target becomes %s(%p)",
    410           LastOverEventTargetIsOutEventTarget() ? "over" : "enter",
    411           mDeepestEnterEventTarget.get(),
    412           aContent.GetFlattenedTreeParent()
    413               ? ToString(*aContent.GetFlattenedTreeParent()).c_str()
    414               : "nullptr",
    415           aContent.GetFlattenedTreeParent()));
    416  UpdateDeepestEnterEventTarget(aContent.GetFlattenedTreeParent());
    417 }
    418 
    419 void OverOutElementsWrapper::TryToRestorePendingRemovedOverTarget(
    420    const WidgetEvent* aEvent) {
    421  if (!MaybeHasPendingRemovingOverEventTarget()) {
    422    return;
    423  }
    424 
    425  LogModule* const logModule = mType == BoundaryEventType::Mouse
    426                                   ? sMouseBoundaryLog
    427                                   : sPointerBoundaryLog;
    428 
    429  // If we receive a mouse event immediately, let's try to restore the last
    430  // "over" event target as the following "out" event target.  We assume that a
    431  // synthesized mousemove or another mouse event is being dispatched at latest
    432  // the next animation frame from the removal.  However, synthesized mouse move
    433  // which is enqueued by ContentRemoved() may not sent to this instance because
    434  // the target is considered with the latest layout, so the document of this
    435  // instance may be moved somewhere before the next animation frame.
    436  // Therefore, we should not restore the last "over" target if we receive an
    437  // unexpected event like a keyboard event, a wheel event, etc.
    438  if (aEvent->AsMouseEvent()) {
    439    // Restore the original "over" event target should be allowed only when it's
    440    // reconnected under the last deepest "enter" event target because we need
    441    // to dispatch "leave" events later at least on the ancestors which have
    442    // never been removed from the tree.
    443    // XXX If new ancestor is inserted between mDeepestEnterEventTarget and
    444    // mPendingToRemoveLastOverEventTarget, we will dispatch "leave" event even
    445    // though we have not dispatched "enter" event on the element.  For fixing
    446    // this, we need to store the full path of the last "out" event target when
    447    // it's removed from the tree.  I guess we can be relax for this issue
    448    // because this hack is required for web apps which reconnect the target
    449    // to the same position immediately.
    450    // XXX Should be IsInclusiveFlatTreeDescendantOf()?  However, it may
    451    // be reconnected into a subtree which is different from where the
    452    // last over element was.
    453    nsCOMPtr<nsIContent> pendingRemovingOverEventTarget =
    454        GetPendingRemovingOverEventTarget();
    455    if (pendingRemovingOverEventTarget &&
    456        pendingRemovingOverEventTarget->IsInclusiveDescendantOf(
    457            mDeepestEnterEventTarget)) {
    458      // StoreOverEventTargetAndDeepestEnterEventTarget() always resets
    459      // mLastOverWidget.  When we restore the pending removing "over" event
    460      // target, we need to keep storing the original "over" widget too.
    461      nsCOMPtr<nsIWeakReference> widget = std::move(mLastOverWidget);
    462      StoreOverEventTargetAndDeepestEnterEventTarget(
    463          pendingRemovingOverEventTarget);
    464      mLastOverWidget = std::move(widget);
    465      MOZ_LOG(logModule, LogLevel::Info,
    466              ("The \"over\" event target (%p) is restored",
    467               mDeepestEnterEventTarget.get()));
    468      return;
    469    }
    470    MOZ_LOG(logModule, LogLevel::Debug,
    471            ("Forgetting the last \"over\" event target (%p) because it is not "
    472             "reconnected under the deepest enter event target (%p)",
    473             mPendingRemovingOverEventTarget.get(),
    474             mDeepestEnterEventTarget.get()));
    475  } else {
    476    MOZ_LOG(logModule, LogLevel::Debug,
    477            ("Forgetting the last \"over\" event target (%p) because an "
    478             "unexpected event (%s) is being dispatched, that means that "
    479             "EventStateManager didn't receive a synthesized mousemove which "
    480             "should be dispatched at next animation frame from the removal",
    481             mPendingRemovingOverEventTarget.get(), ToChar(aEvent->mMessage)));
    482  }
    483 
    484  // Now, we should not restore mPendingRemovingOverEventTarget to
    485  // mDeepestEnterEventTarget anymore since mPendingRemovingOverEventTarget was
    486  // moved outside the subtree of mDeepestEnterEventTarget.
    487  mPendingRemovingOverEventTarget = nullptr;
    488 }
    489 
    490 void OverOutElementsWrapper::WillDispatchOverAndEnterEvent(
    491    nsIContent* aOverEventTarget) {
    492  StoreOverEventTargetAndDeepestEnterEventTarget(aOverEventTarget);
    493  // Store the first "over" event target we fire and don't refire "over" event
    494  // to that element while the first "over" event is still ongoing.
    495  mDispatchingOverEventTarget = aOverEventTarget;
    496 }
    497 
    498 void OverOutElementsWrapper::DidDispatchOverAndEnterEvent(
    499    nsIContent* aOriginalOverTargetInComposedDoc,
    500    nsIWidget* aOverEventTargetWidget) {
    501  mDispatchingOverEventTarget = nullptr;
    502  mLastOverWidget = do_GetWeakReference(aOverEventTargetWidget);
    503 
    504  // Pointer Events define that once the `pointerover` event target is removed
    505  // from the tree, `pointerout` should not be fired on that and the closest
    506  // connected ancestor at the target removal should be kept as the deepest
    507  // `pointerleave` target.  Therefore, we don't need the special handling for
    508  // `pointerout` event target if the last `pointerover` target is temporarily
    509  // removed from the tree.
    510  if (mType == OverOutElementsWrapper::BoundaryEventType::Pointer) {
    511    return;
    512  }
    513 
    514  // Assume that the caller checks whether aOriginalOverTarget is in the
    515  // original document.  If we don't enable the strict mouse/pointer event
    516  // boundary event dispatching by the pref (see below),
    517  // mDeepestEnterEventTarget is set to nullptr when the last "over" target is
    518  // removed.  Therefore, we cannot check whether aOriginalOverTarget is in the
    519  // original document here.
    520  if (!aOriginalOverTargetInComposedDoc) {
    521    return;
    522  }
    523  MOZ_ASSERT_IF(mDeepestEnterEventTarget,
    524                mDeepestEnterEventTarget->GetComposedDoc() ==
    525                    aOriginalOverTargetInComposedDoc->GetComposedDoc());
    526  // If the "mouseover" event target is removed temporarily while we're
    527  // dispatching "mouseover" and "mouseenter" events and the target gets back
    528  // under the deepest enter event target, we should restore the "mouseover"
    529  // target.
    530  if (!LastOverEventTargetIsOutEventTarget() && mDeepestEnterEventTarget &&
    531      nsContentUtils::ContentIsFlattenedTreeDescendantOf(
    532          aOriginalOverTargetInComposedDoc, mDeepestEnterEventTarget)) {
    533    StoreOverEventTargetAndDeepestEnterEventTarget(
    534        aOriginalOverTargetInComposedDoc);
    535    LogModule* const logModule = mType == BoundaryEventType::Mouse
    536                                     ? sMouseBoundaryLog
    537                                     : sPointerBoundaryLog;
    538    MOZ_LOG(logModule, LogLevel::Info,
    539            ("The \"over\" event target (%p) is restored",
    540             mDeepestEnterEventTarget.get()));
    541  }
    542 }
    543 
    544 void OverOutElementsWrapper::StoreOverEventTargetAndDeepestEnterEventTarget(
    545    nsIContent* aOverEventTargetAndDeepestEnterEventTarget) {
    546  mDeepestEnterEventTarget = aOverEventTargetAndDeepestEnterEventTarget;
    547  mPendingRemovingOverEventTarget = nullptr;
    548  mDeepestEnterEventTargetIsOverEventTarget = !!mDeepestEnterEventTarget;
    549  mLastOverWidget = nullptr;  // Set it after dispatching the "over" event.
    550 }
    551 
    552 void OverOutElementsWrapper::UpdateDeepestEnterEventTarget(
    553    nsIContent* aDeepestEnterEventTarget) {
    554  if (MOZ_UNLIKELY(mDeepestEnterEventTarget == aDeepestEnterEventTarget)) {
    555    return;
    556  }
    557 
    558  if (!aDeepestEnterEventTarget) {
    559    // If the root element is removed, we don't need to dispatch "leave"
    560    // events on any elements.  Therefore, we can forget everything.
    561    StoreOverEventTargetAndDeepestEnterEventTarget(nullptr);
    562    return;
    563  }
    564 
    565  if (LastOverEventTargetIsOutEventTarget()) {
    566    MOZ_ASSERT(mDeepestEnterEventTarget);
    567    if (mType == BoundaryEventType::Pointer) {
    568      // The spec of Pointer Events defines that once the `pointerover` event
    569      // target is removed from the tree, `pointerout` should not be fired on
    570      // that and the closest connected ancestor at the target removal should be
    571      // kept as the deepest `pointerleave` target.  All browsers considers the
    572      // last `pointerover` event target is removed immediately when it occurs.
    573      // Therefore, we don't need the special handling which we do for the
    574      // `mouseout` event target below for considering whether we'll dispatch
    575      // `pointerout` on the last `pointerover` target.
    576      mPendingRemovingOverEventTarget = nullptr;
    577    } else if (
    578        !StaticPrefs::
    579            dom_event_mouse_boundary_restore_last_over_target_from_temporary_removal()) {
    580      // The spec of UI Events do not define that browsers should keep storing
    581      // the last `mouseover` target when it's removed temporarily and
    582      // reconnected immediately.  We've decided to follow Chrome's behavior for
    583      // now.  However, there is a pref to bring back the old behavior if
    584      // needed.
    585      mPendingRemovingOverEventTarget = nullptr;
    586    } else {
    587      // However, Safari and old Chrome restore the last `mouseover` target when
    588      // it's temporarily removed and reconnected immediately.  Therefore, we
    589      // should follow them by default.  However, we should keep the old
    590      // behavior for making it easier to backout the new behavior with
    591      // disabling the pref.
    592      MOZ_ASSERT(!mPendingRemovingOverEventTarget);
    593      MOZ_ASSERT(mDeepestEnterEventTarget);
    594      mPendingRemovingOverEventTarget =
    595          do_GetWeakReference(mDeepestEnterEventTarget);
    596    }
    597  } else {
    598    MOZ_ASSERT(!mDeepestEnterEventTargetIsOverEventTarget);
    599    // If mDeepestEnterEventTarget is not the last "over" event target, we've
    600    // already done the complicated state managing above.  Therefore, we only
    601    // need to update mDeepestEnterEventTarget in this case.
    602  }
    603  mDeepestEnterEventTarget = aDeepestEnterEventTarget;
    604  mDeepestEnterEventTargetIsOverEventTarget = false;
    605  // Do not update mLastOverWidget here because it's required to ignore some
    606  // following pointer events which are fired on widget under different top
    607  // level widget.
    608 }
    609 
    610 /******************************************************************/
    611 /* mozilla::EventStateManager                                     */
    612 /******************************************************************/
    613 
    614 static uint32_t sESMInstanceCount = 0;
    615 
    616 bool EventStateManager::sNormalLMouseEventInProcess = false;
    617 int16_t EventStateManager::sCurrentMouseBtn = MouseButton::eNotPressed;
    618 EventStateManager* EventStateManager::sActiveESM = nullptr;
    619 EventStateManager* EventStateManager::sCursorSettingManager = nullptr;
    620 constinit AutoWeakFrame EventStateManager::sLastDragOverFrame{};
    621 LayoutDeviceIntPoint EventStateManager::sPreLockScreenPoint =
    622    LayoutDeviceIntPoint(0, 0);
    623 LayoutDeviceIntPoint EventStateManager::sLastRefPoint = kInvalidRefPoint;
    624 LayoutDeviceIntPoint EventStateManager::sLastRefPointOfRawUpdate =
    625    kInvalidRefPoint;
    626 CSSIntPoint EventStateManager::sLastScreenPoint = CSSIntPoint(0, 0);
    627 LayoutDeviceIntPoint EventStateManager::sSynthCenteringPoint = kInvalidRefPoint;
    628 CSSIntPoint EventStateManager::sLastClientPoint = CSSIntPoint(0, 0);
    629 MOZ_RUNINIT nsCOMPtr<nsIContent> EventStateManager::sDragOverContent = nullptr;
    630 
    631 EventStateManager::WheelPrefs* EventStateManager::WheelPrefs::sInstance =
    632    nullptr;
    633 EventStateManager::DeltaAccumulator*
    634    EventStateManager::DeltaAccumulator::sInstance = nullptr;
    635 
    636 constexpr const StyleCursorKind kInvalidCursorKind =
    637    static_cast<StyleCursorKind>(255);
    638 
    639 EventStateManager::EventStateManager()
    640    : mLockCursor(kInvalidCursorKind),
    641      mCurrentTarget(nullptr),
    642      // init d&d gesture state machine variables
    643      mGestureDownPoint(0, 0),
    644      mGestureModifiers(0),
    645      mGestureDownButtons(0),
    646      mGestureDownButton(0),
    647      mPresContext(nullptr),
    648      mShouldAlwaysUseLineDeltas(false),
    649      mShouldAlwaysUseLineDeltasInitialized(false),
    650      mGestureDownInTextControl(false),
    651      mInTouchDrag(false),
    652      m_haveShutdown(false) {
    653  if (sESMInstanceCount == 0) {
    654    gUserInteractionTimerCallback = new UITimerCallback();
    655    if (gUserInteractionTimerCallback) NS_ADDREF(gUserInteractionTimerCallback);
    656    UpdateUserActivityTimer();
    657  }
    658  ++sESMInstanceCount;
    659 }
    660 
    661 // static
    662 LazyLogModule& EventStateManager::MouseCursorUpdateLogRef() {
    663  return gMouseCursorUpdates;
    664 }
    665 
    666 nsresult EventStateManager::UpdateUserActivityTimer() {
    667  if (!gUserInteractionTimerCallback) return NS_OK;
    668 
    669  if (!gUserInteractionTimer) {
    670    gUserInteractionTimer = NS_NewTimer().take();
    671  }
    672 
    673  if (gUserInteractionTimer) {
    674    gUserInteractionTimer->InitWithCallback(
    675        gUserInteractionTimerCallback,
    676        StaticPrefs::dom_events_user_interaction_interval(),
    677        nsITimer::TYPE_ONE_SHOT);
    678  }
    679  return NS_OK;
    680 }
    681 
    682 nsresult EventStateManager::Init() {
    683  nsCOMPtr<nsIObserverService> observerService =
    684      mozilla::services::GetObserverService();
    685  if (!observerService) return NS_ERROR_FAILURE;
    686 
    687  observerService->AddObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID, true);
    688 
    689  return NS_OK;
    690 }
    691 
    692 bool EventStateManager::ShouldAlwaysUseLineDeltas() {
    693  if (MOZ_UNLIKELY(!mShouldAlwaysUseLineDeltasInitialized)) {
    694    mShouldAlwaysUseLineDeltasInitialized = true;
    695    mShouldAlwaysUseLineDeltas =
    696        !StaticPrefs::dom_event_wheel_deltaMode_lines_disabled();
    697    if (!mShouldAlwaysUseLineDeltas && mDocument) {
    698      if (nsIPrincipal* principal =
    699              mDocument->GetPrincipalForPrefBasedHacks()) {
    700        mShouldAlwaysUseLineDeltas = principal->IsURIInPrefList(
    701            "dom.event.wheel-deltaMode-lines.always-enabled");
    702      }
    703    }
    704  }
    705  return mShouldAlwaysUseLineDeltas;
    706 }
    707 
    708 EventStateManager::~EventStateManager() {
    709  ReleaseCurrentIMEContentObserver();
    710 
    711  if (sActiveESM == this) {
    712    sActiveESM = nullptr;
    713  }
    714 
    715  if (StaticPrefs::ui_click_hold_context_menus()) {
    716    KillClickHoldTimer();
    717  }
    718 
    719  if (sCursorSettingManager == this) {
    720    sCursorSettingManager = nullptr;
    721  }
    722 
    723  --sESMInstanceCount;
    724  if (sESMInstanceCount == 0) {
    725    WheelTransaction::Shutdown();
    726    if (gUserInteractionTimerCallback) {
    727      gUserInteractionTimerCallback->Notify(nullptr);
    728      NS_RELEASE(gUserInteractionTimerCallback);
    729    }
    730    if (gUserInteractionTimer) {
    731      gUserInteractionTimer->Cancel();
    732      NS_RELEASE(gUserInteractionTimer);
    733    }
    734    WheelPrefs::Shutdown();
    735    DeltaAccumulator::Shutdown();
    736  }
    737 
    738  if (sDragOverContent && sDragOverContent->OwnerDoc() == mDocument) {
    739    sDragOverContent = nullptr;
    740  }
    741 
    742  if (!m_haveShutdown) {
    743    Shutdown();
    744 
    745    // Don't remove from Observer service in Shutdown because Shutdown also
    746    // gets called from xpcom shutdown observer.  And we don't want to remove
    747    // from the service in that case.
    748 
    749    nsCOMPtr<nsIObserverService> observerService =
    750        mozilla::services::GetObserverService();
    751    if (observerService) {
    752      observerService->RemoveObserver(this, NS_XPCOM_SHUTDOWN_OBSERVER_ID);
    753    }
    754  }
    755 }
    756 
    757 nsresult EventStateManager::Shutdown() {
    758  m_haveShutdown = true;
    759  return NS_OK;
    760 }
    761 
    762 NS_IMETHODIMP
    763 EventStateManager::Observe(nsISupports* aSubject, const char* aTopic,
    764                           const char16_t* someData) {
    765  if (!nsCRT::strcmp(aTopic, NS_XPCOM_SHUTDOWN_OBSERVER_ID)) {
    766    Shutdown();
    767  }
    768 
    769  return NS_OK;
    770 }
    771 
    772 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(EventStateManager)
    773  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIObserver)
    774  NS_INTERFACE_MAP_ENTRY(nsIObserver)
    775  NS_INTERFACE_MAP_ENTRY(nsISupportsWeakReference)
    776 NS_INTERFACE_MAP_END
    777 
    778 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventStateManager)
    779 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventStateManager)
    780 
    781 NS_IMPL_CYCLE_COLLECTION_WEAK(EventStateManager, mCurrentTargetContent,
    782                              mGestureDownContent, mGestureDownFrameOwner,
    783                              mLastLeftMouseDownInfo.mLastMouseDownContent,
    784                              mLastMiddleMouseDownInfo.mLastMouseDownContent,
    785                              mLastRightMouseDownInfo.mLastMouseDownContent,
    786                              mActiveContent, mHoverContent, mURLTargetContent,
    787                              mPopoverPointerDownTarget, mMouseEnterLeaveHelper,
    788                              mPointersEnterLeaveHelper, mDocument,
    789                              mIMEContentObserver, mAccessKeys)
    790 
    791 void EventStateManager::ReleaseCurrentIMEContentObserver() {
    792  if (mIMEContentObserver) {
    793    mIMEContentObserver->DisconnectFromEventStateManager();
    794  }
    795  mIMEContentObserver = nullptr;
    796 }
    797 
    798 void EventStateManager::OnStartToObserveContent(
    799    IMEContentObserver* aIMEContentObserver) {
    800  if (mIMEContentObserver == aIMEContentObserver) {
    801    return;
    802  }
    803  ReleaseCurrentIMEContentObserver();
    804  mIMEContentObserver = aIMEContentObserver;
    805 }
    806 
    807 void EventStateManager::OnStopObservingContent(
    808    IMEContentObserver* aIMEContentObserver) {
    809  aIMEContentObserver->DisconnectFromEventStateManager();
    810  NS_ENSURE_TRUE_VOID(mIMEContentObserver == aIMEContentObserver);
    811  mIMEContentObserver = nullptr;
    812 }
    813 
    814 void EventStateManager::TryToFlushPendingNotificationsToIME() {
    815  if (mIMEContentObserver) {
    816    mIMEContentObserver->TryToFlushPendingNotifications(true);
    817  }
    818 }
    819 
    820 static bool IsMessageMouseUserActivity(EventMessage aMessage) {
    821  return aMessage == eMouseMove || aMessage == eMouseUp ||
    822         aMessage == eMouseDown || aMessage == ePointerAuxClick ||
    823         aMessage == eMouseDoubleClick || aMessage == ePointerClick ||
    824         aMessage == eMouseActivate || aMessage == eMouseLongTap;
    825 }
    826 
    827 static bool IsMessageGamepadUserActivity(EventMessage aMessage) {
    828  return aMessage == eGamepadButtonDown || aMessage == eGamepadButtonUp ||
    829         aMessage == eGamepadAxisMove;
    830 }
    831 
    832 // static
    833 bool EventStateManager::IsKeyboardEventUserActivity(WidgetEvent* aEvent) {
    834  // We ignore things that shouldn't cause popups, but also things that look
    835  // like shortcut presses. In some obscure cases these may actually be
    836  // website input, but any meaningful website will have other input anyway,
    837  // and we can't very well tell whether shortcut input was supposed to be
    838  // directed at chrome or the document.
    839 
    840  WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
    841  // Access keys should be treated as page interaction.
    842  if (keyEvent->ModifiersMatchWithAccessKey(AccessKeyType::eContent)) {
    843    return true;
    844  }
    845  if (!keyEvent->CanTreatAsUserInput() || keyEvent->IsControl() ||
    846      keyEvent->IsMeta() || keyEvent->IsAlt()) {
    847    return false;
    848  }
    849  // Deal with function keys:
    850  switch (keyEvent->mKeyNameIndex) {
    851    case KEY_NAME_INDEX_F1:
    852    case KEY_NAME_INDEX_F2:
    853    case KEY_NAME_INDEX_F3:
    854    case KEY_NAME_INDEX_F4:
    855    case KEY_NAME_INDEX_F5:
    856    case KEY_NAME_INDEX_F6:
    857    case KEY_NAME_INDEX_F7:
    858    case KEY_NAME_INDEX_F8:
    859    case KEY_NAME_INDEX_F9:
    860    case KEY_NAME_INDEX_F10:
    861    case KEY_NAME_INDEX_F11:
    862    case KEY_NAME_INDEX_F12:
    863    case KEY_NAME_INDEX_F13:
    864    case KEY_NAME_INDEX_F14:
    865    case KEY_NAME_INDEX_F15:
    866    case KEY_NAME_INDEX_F16:
    867    case KEY_NAME_INDEX_F17:
    868    case KEY_NAME_INDEX_F18:
    869    case KEY_NAME_INDEX_F19:
    870    case KEY_NAME_INDEX_F20:
    871    case KEY_NAME_INDEX_F21:
    872    case KEY_NAME_INDEX_F22:
    873    case KEY_NAME_INDEX_F23:
    874    case KEY_NAME_INDEX_F24:
    875      return false;
    876    default:
    877      return true;
    878  }
    879 }
    880 
    881 static void OnTypingInteractionEnded() {
    882  // We don't consider a single keystroke to be typing.
    883  if (gTypingInteractionKeyPresses > 1) {
    884    gTypingInteraction.mInteractionCount += gTypingInteractionKeyPresses;
    885    gTypingInteraction.mInteractionTimeInMilliseconds += static_cast<uint32_t>(
    886        std::ceil((gTypingEndTime - gTypingStartTime).ToMilliseconds()));
    887  }
    888 
    889  gTypingInteractionKeyPresses = 0;
    890  gTypingStartTime = TimeStamp();
    891  gTypingEndTime = TimeStamp();
    892 }
    893 
    894 static void HandleKeyUpInteraction(WidgetKeyboardEvent* aKeyEvent) {
    895  if (EventStateManager::IsKeyboardEventUserActivity(aKeyEvent)) {
    896    TimeStamp now = TimeStamp::Now();
    897    if (gTypingEndTime.IsNull()) {
    898      gTypingEndTime = now;
    899    }
    900    TimeDuration delay = now - gTypingEndTime;
    901    // Has it been too long since the last keystroke to be considered typing?
    902    if (gTypingInteractionKeyPresses > 0 &&
    903        delay >
    904            TimeDuration::FromMilliseconds(
    905                StaticPrefs::browser_places_interactions_typing_timeout_ms())) {
    906      OnTypingInteractionEnded();
    907    }
    908    gTypingInteractionKeyPresses++;
    909    if (gTypingStartTime.IsNull()) {
    910      gTypingStartTime = now;
    911    }
    912    gTypingEndTime = now;
    913  }
    914 }
    915 
    916 nsresult EventStateManager::PreHandleEvent(nsPresContext* aPresContext,
    917                                           WidgetEvent* aEvent,
    918                                           nsIFrame* aTargetFrame,
    919                                           nsIContent* aTargetContent,
    920                                           nsEventStatus* aStatus,
    921                                           nsIContent* aOverrideClickTarget) {
    922  AUTO_PROFILER_LABEL("EventStateManager::PreHandleEvent", DOM);
    923  NS_ENSURE_ARG_POINTER(aStatus);
    924  NS_ENSURE_ARG(aPresContext);
    925  if (!aEvent) {
    926    NS_ERROR("aEvent is null.  This should never happen.");
    927    return NS_ERROR_NULL_POINTER;
    928  }
    929 
    930  NS_WARNING_ASSERTION(
    931      !aTargetFrame || !aTargetFrame->GetContent() ||
    932          aTargetFrame->GetContent() == aTargetContent ||
    933          aTargetFrame->GetContent()->GetFlattenedTreeParent() ==
    934              aTargetContent ||
    935          aTargetFrame->IsGeneratedContentFrame(),
    936      "aTargetFrame should be related with aTargetContent");
    937 #if DEBUG
    938  if (aTargetFrame && aTargetFrame->IsGeneratedContentFrame()) {
    939    MOZ_ASSERT(aTargetContent == aTargetFrame->GetContentForEvent(aEvent),
    940               "Unexpected target for generated content frame!");
    941  }
    942 #endif
    943 
    944  mCurrentTarget = aTargetFrame;
    945  mCurrentTargetContent = nullptr;
    946 
    947  // Do not take account eMouseEnterIntoWidget/ExitFromWidget so that loading
    948  // a page when user is not active doesn't change the state to active.
    949  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
    950  if (aEvent->IsTrusted() &&
    951      ((mouseEvent && mouseEvent->IsReal() &&
    952        IsMessageMouseUserActivity(mouseEvent->mMessage)) ||
    953       aEvent->mClass == eWheelEventClass ||
    954       aEvent->mClass == ePointerEventClass ||
    955       aEvent->mClass == eTouchEventClass ||
    956       aEvent->mClass == eKeyboardEventClass ||
    957       (aEvent->mClass == eDragEventClass && aEvent->mMessage == eDrop) ||
    958       IsMessageGamepadUserActivity(aEvent->mMessage))) {
    959    if (gMouseOrKeyboardEventCounter == 0) {
    960      nsCOMPtr<nsIObserverService> obs =
    961          mozilla::services::GetObserverService();
    962      if (obs) {
    963        obs->NotifyObservers(nullptr, "user-interaction-active", nullptr);
    964        UpdateUserActivityTimer();
    965      }
    966    }
    967    ++gMouseOrKeyboardEventCounter;
    968 
    969    nsCOMPtr<nsINode> node = aTargetContent;
    970    if (node &&
    971        ((aEvent->mMessage == eKeyUp && IsKeyboardEventUserActivity(aEvent)) ||
    972         aEvent->mMessage == eMouseUp || aEvent->mMessage == eWheel ||
    973         aEvent->mMessage == eTouchEnd || aEvent->mMessage == ePointerUp ||
    974         aEvent->mMessage == eDrop)) {
    975      Document* doc = node->OwnerDoc();
    976      while (doc) {
    977        doc->SetUserHasInteracted();
    978        doc = nsContentUtils::IsChildOfSameType(doc)
    979                  ? doc->GetInProcessParentDocument()
    980                  : nullptr;
    981      }
    982    }
    983  }
    984 
    985  WheelTransaction::OnEvent(aEvent);
    986 
    987  // Focus events don't necessarily need a frame.
    988  if (!mCurrentTarget && !aTargetContent) {
    989    NS_ERROR("mCurrentTarget and aTargetContent are null");
    990    return NS_ERROR_NULL_POINTER;
    991  }
    992 #ifdef DEBUG
    993  if (aEvent->HasDragEventMessage() && PointerLockManager::IsLocked()) {
    994    NS_ASSERTION(PointerLockManager::IsLocked(),
    995                 "Pointer is locked. Drag events should be suppressed when "
    996                 "the pointer is locked.");
    997  }
    998 #endif
    999  // Store last known screenPoint and clientPoint so pointer lock
   1000  // can use these values as constants.
   1001  if (aEvent->IsTrusted() &&
   1002      ((mouseEvent && mouseEvent->IsReal()) ||
   1003       aEvent->mClass == eWheelEventClass) &&
   1004      !PointerLockManager::IsLocked()) {
   1005    // XXX Probably doesn't matter much, but storing these in CSS pixels instead
   1006    // of device pixels means behavior can be a bit odd if you zoom while
   1007    // pointer-locked.
   1008    sLastScreenPoint = RoundedToInt(
   1009        Event::GetScreenCoords(aPresContext, aEvent, aEvent->mRefPoint)
   1010            .extract());
   1011    sLastClientPoint = RoundedToInt(Event::GetClientCoords(
   1012        aPresContext, aEvent, aEvent->mRefPoint, CSSDoublePoint{0, 0}));
   1013  }
   1014 
   1015  *aStatus = nsEventStatus_eIgnore;
   1016 
   1017  if (aEvent->mClass == eQueryContentEventClass) {
   1018    HandleQueryContentEvent(aEvent->AsQueryContentEvent());
   1019    return NS_OK;
   1020  }
   1021 
   1022  WidgetTouchEvent* touchEvent = aEvent->AsTouchEvent();
   1023  if (touchEvent && mInTouchDrag) {
   1024    if (touchEvent->mMessage == eTouchMove) {
   1025      GenerateDragGesture(aPresContext, touchEvent);
   1026    } else {
   1027      MOZ_ASSERT(touchEvent->mMessage != eTouchRawUpdate);
   1028      mInTouchDrag = false;
   1029      StopTrackingDragGesture(true);
   1030    }
   1031  }
   1032 
   1033  if (mMouseEnterLeaveHelper && aEvent->IsTrusted()) {
   1034    // When the last `mouseover` event target is removed from the document,
   1035    // we makes mMouseEnterLeaveHelper update the last deepest `mouseenter`
   1036    // event target to the removed node parent and mark it as not the following
   1037    // `mouseout` event target.  However, the other browsers may dispatch
   1038    // `mouseout` on it if it's restored "immediately".  Therefore, we use
   1039    // the next animation frame as the deadline.  ContentRemoved() enqueues a
   1040    // synthesized `mousemove` to dispatch mouse boundary events under the
   1041    // mouse cursor soon and the synthesized event (or eMouseExitFromWidget if
   1042    // our window is moved) will reach here at latest the next animation frame.
   1043    // Therefore, we can use the event as the deadline.  If the removed last
   1044    // `mouseover` target is reconnected before a synthesized mouse event or
   1045    // a real mouse event, let's restore it as the following `mouseout` event
   1046    // target.  Otherwise, e.g., a keyboard event, let's forget it.
   1047    mMouseEnterLeaveHelper->TryToRestorePendingRemovedOverTarget(aEvent);
   1048  }
   1049 
   1050  static constexpr auto const allowSynthesisForTests = []() -> bool {
   1051    nsCOMPtr<nsIDragService> dragService =
   1052        do_GetService("@mozilla.org/widget/dragservice;1");
   1053    return dragService &&
   1054           !dragService->GetNeverAllowSessionIsSynthesizedForTests();
   1055  };
   1056 
   1057  switch (aEvent->mMessage) {
   1058    case eContextMenu:
   1059      if (PointerLockManager::IsLocked()) {
   1060        return NS_ERROR_DOM_INVALID_STATE_ERR;
   1061      }
   1062      break;
   1063    case eMouseTouchDrag:
   1064      mInTouchDrag = true;
   1065      BeginTrackingDragGesture(aPresContext, mouseEvent, aTargetFrame);
   1066      break;
   1067    case eMouseDown: {
   1068      switch (mouseEvent->mButton) {
   1069        case MouseButton::ePrimary:
   1070          BeginTrackingDragGesture(aPresContext, mouseEvent, aTargetFrame);
   1071          mLastLeftMouseDownInfo.mClickCount = mouseEvent->mClickCount;
   1072          PrepareForFollowingClickEvent(*mouseEvent);
   1073          sNormalLMouseEventInProcess = true;
   1074          break;
   1075        case MouseButton::eMiddle:
   1076          mLastMiddleMouseDownInfo.mClickCount = mouseEvent->mClickCount;
   1077          PrepareForFollowingClickEvent(*mouseEvent);
   1078          break;
   1079        case MouseButton::eSecondary:
   1080          mLastRightMouseDownInfo.mClickCount = mouseEvent->mClickCount;
   1081          PrepareForFollowingClickEvent(*mouseEvent);
   1082          break;
   1083        case MouseButton::eX1:
   1084        case MouseButton::eX2:
   1085          // XXX FIXME: We won't dispatch `auxclick` for 4th nor 5th button.
   1086          break;
   1087        default:
   1088          break;
   1089      }
   1090      break;
   1091    }
   1092    case eMouseUp: {
   1093      switch (mouseEvent->mButton) {
   1094        case MouseButton::ePrimary:
   1095          if (StaticPrefs::ui_click_hold_context_menus()) {
   1096            KillClickHoldTimer();
   1097          }
   1098          mInTouchDrag = false;
   1099          StopTrackingDragGesture(true);
   1100          sNormalLMouseEventInProcess = false;
   1101          // then fall through...
   1102          [[fallthrough]];
   1103        case MouseButton::eSecondary:
   1104        case MouseButton::eMiddle: {
   1105          RefPtr<EventStateManager> esm =
   1106              ESMFromContentOrThis(aOverrideClickTarget);
   1107          esm->PrepareForFollowingClickEvent(*mouseEvent, aOverrideClickTarget);
   1108          break;
   1109        }
   1110        case MouseButton::eX1:
   1111        case MouseButton::eX2:
   1112          // XXX FIXME: We won't dispatch `auxclick` for 4th nor 5th button.
   1113          break;
   1114        default:
   1115          break;
   1116      }
   1117      break;
   1118    }
   1119    case eMouseEnterIntoWidget:
   1120      PointerEventHandler::UpdatePointerActiveState(mouseEvent, aTargetContent);
   1121      // In some cases on e10s eMouseEnterIntoWidget
   1122      // event was sent twice into child process of content.
   1123      // (From specific widget code (sending is not permanent) and
   1124      // from ESM::DispatchMouseOrPointerBoundaryEvent (sending is permanent)).
   1125      // IsCrossProcessForwardingStopped() helps to suppress sending accidental
   1126      // event from widget code.
   1127      aEvent->StopCrossProcessForwarding();
   1128      break;
   1129    case eMouseExitFromWidget:
   1130      // If this is a remote frame, we receive eMouseExitFromWidget from the
   1131      // parent the mouse exits our content. Since the parent may update the
   1132      // cursor while the mouse is outside our frame, and since PuppetWidget
   1133      // caches the current cursor internally, re-entering our content (say from
   1134      // over a window edge) wont update the cursor if the cached value and the
   1135      // current cursor match. So when the mouse exits a remote frame, clear the
   1136      // cached widget cursor so a proper update will occur when the mouse
   1137      // re-enters.
   1138      if (XRE_IsContentProcess()) {
   1139        ClearCachedWidgetCursor(mCurrentTarget);
   1140      }
   1141 
   1142      // IsCrossProcessForwardingStopped() helps to suppress double event
   1143      // sending into process of content. For more information see comment
   1144      // above, at eMouseEnterIntoWidget case.
   1145      aEvent->StopCrossProcessForwarding();
   1146 
   1147      // If the event is not a top-level window or puppet widget exit, then it's
   1148      // not really an exit --- we may have traversed widget boundaries but
   1149      // we're still in our toplevel window or puppet widget.
   1150      if (mouseEvent->mExitFrom.value() !=
   1151              WidgetMouseEvent::ePlatformTopLevel &&
   1152          mouseEvent->mExitFrom.value() != WidgetMouseEvent::ePuppet) {
   1153        // Treat it as a synthetic move so we don't generate spurious
   1154        // "exit" or "move" events.  Any necessary "out" or "over" events
   1155        // will be generated by GenerateMouseEnterExit
   1156        mouseEvent->mMessage = eMouseMove;
   1157        mouseEvent->mReason = WidgetMouseEvent::eSynthesized;
   1158        // then fall through...
   1159      } else {
   1160        MOZ_ASSERT_IF(XRE_IsParentProcess(),
   1161                      mouseEvent->mExitFrom.value() ==
   1162                          WidgetMouseEvent::ePlatformTopLevel);
   1163        MOZ_ASSERT_IF(XRE_IsContentProcess(), mouseEvent->mExitFrom.value() ==
   1164                                                  WidgetMouseEvent::ePuppet);
   1165        // We should synthetize corresponding pointer events
   1166        GeneratePointerEnterExit(ePointerLeave, mouseEvent);
   1167        GenerateMouseEnterExit(mouseEvent);
   1168        // Remove the pointer from the active pointerId table.
   1169        PointerEventHandler::UpdatePointerActiveState(mouseEvent);
   1170        // This is really an exit and should stop here
   1171        aEvent->mMessage = eVoidEvent;
   1172        break;
   1173      }
   1174      [[fallthrough]];
   1175    case ePointerDown:
   1176      if (aEvent->mMessage == ePointerDown) {
   1177        PointerEventHandler::UpdatePointerActiveState(mouseEvent,
   1178                                                      aTargetContent);
   1179        PointerEventHandler::ImplicitlyCapturePointer(aTargetFrame, aEvent);
   1180        // https://html.spec.whatwg.org/multipage/interaction.html#activation-triggering-input-event
   1181        if (mouseEvent->mInputSource == MouseEvent_Binding::MOZ_SOURCE_MOUSE) {
   1182          NotifyTargetUserActivation(aEvent, aTargetContent);
   1183        }
   1184 
   1185        LightDismissOpenPopovers(aEvent, aTargetContent);
   1186        LightDismissOpenDialogs(aEvent, aTargetContent);
   1187      }
   1188      [[fallthrough]];
   1189    case eMouseMove:
   1190    case ePointerMove:
   1191    case ePointerRawUpdate: {
   1192      if (aEvent->mMessage == ePointerMove) {
   1193        PointerEventHandler::UpdatePointerActiveState(mouseEvent,
   1194                                                      aTargetContent);
   1195      }
   1196      if (!mInTouchDrag &&
   1197          PointerEventHandler::IsDragAndDropEnabled(*mouseEvent)) {
   1198        GenerateDragGesture(aPresContext, mouseEvent);
   1199      }
   1200      // on the Mac, GenerateDragGesture() may not return until the drag
   1201      // has completed and so |aTargetFrame| may have been deleted (moving
   1202      // a bookmark, for example).  If this is the case, however, we know
   1203      // that ClearFrameRefs() has been called and it cleared out
   1204      // |mCurrentTarget|. As a result, we should pass |mCurrentTarget|
   1205      // into UpdateCursor().
   1206      UpdateCursor(aPresContext, mouseEvent, mCurrentTarget, aStatus);
   1207 
   1208      UpdateLastRefPointOfMouseEvent(mouseEvent);
   1209      if (PointerLockManager::IsLocked()) {
   1210        ResetPointerToWindowCenterWhilePointerLocked(mouseEvent);
   1211      }
   1212      UpdateLastPointerPosition(mouseEvent);
   1213 
   1214      GenerateMouseEnterExit(mouseEvent);
   1215      // Flush pending layout changes, so that later mouse move events
   1216      // will go to the right nodes.
   1217      FlushLayout(aPresContext);
   1218      break;
   1219    }
   1220    case ePointerUp:
   1221      LightDismissOpenPopovers(aEvent, aTargetContent);
   1222      LightDismissOpenDialogs(aEvent, aTargetContent);
   1223      GenerateMouseEnterExit(mouseEvent);
   1224      if (mouseEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_MOUSE) {
   1225        NotifyTargetUserActivation(aEvent, aTargetContent);
   1226      }
   1227      break;
   1228    case ePointerGotCapture:
   1229      GenerateMouseEnterExit(mouseEvent);
   1230      break;
   1231    case eDragStart:
   1232      if (StaticPrefs::ui_click_hold_context_menus()) {
   1233        // an external drag gesture event came in, not generated internally
   1234        // by Gecko. Make sure we get rid of the click-hold timer.
   1235        KillClickHoldTimer();
   1236      }
   1237      break;
   1238    case eDragOver: {
   1239      WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
   1240      MOZ_ASSERT(dragEvent);
   1241      if (dragEvent->mFlags.mIsSynthesizedForTests &&
   1242          allowSynthesisForTests()) {
   1243        dragEvent->InitDropEffectForTests();
   1244      }
   1245      // Send the enter/exit events before eDrop.
   1246      GenerateDragDropEnterExit(aPresContext, dragEvent);
   1247      break;
   1248    }
   1249    case eDrop: {
   1250      if (aEvent->mFlags.mIsSynthesizedForTests && allowSynthesisForTests()) {
   1251        MOZ_ASSERT(aEvent->AsDragEvent());
   1252        aEvent->AsDragEvent()->InitDropEffectForTests();
   1253      }
   1254      break;
   1255    }
   1256    case eKeyPress: {
   1257      WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
   1258      if ((keyEvent->ModifiersMatchWithAccessKey(AccessKeyType::eChrome) ||
   1259           keyEvent->ModifiersMatchWithAccessKey(AccessKeyType::eContent)) &&
   1260          // If the key binding of this event is a native key binding, we
   1261          // prioritize it.
   1262          !HasNativeKeyBindings(aTargetContent, keyEvent)) {
   1263        // If the eKeyPress event will be sent to a remote process, this
   1264        // process needs to wait reply from the remote process for checking if
   1265        // preceding eKeyDown event is consumed.  If preceding eKeyDown event
   1266        // is consumed in the remote process, BrowserChild won't send the event
   1267        // back to this process.  So, only when this process receives a reply
   1268        // eKeyPress event in BrowserParent, we should handle accesskey in this
   1269        // process.
   1270        if (IsTopLevelRemoteTarget(GetFocusedElement())) {
   1271          // However, if there is no accesskey target for the key combination,
   1272          // we don't need to wait reply from the remote process.  Otherwise,
   1273          // Mark the event as waiting reply from remote process and stop
   1274          // propagation in this process.
   1275          if (CheckIfEventMatchesAccessKey(keyEvent, aPresContext)) {
   1276            keyEvent->StopPropagation();
   1277            keyEvent->MarkAsWaitingReplyFromRemoteProcess();
   1278          }
   1279        }
   1280        // If the event target is in this process, we can handle accesskey now
   1281        // since if preceding eKeyDown event was consumed, eKeyPress event
   1282        // won't be dispatched by widget.  So, coming eKeyPress event means
   1283        // that the preceding eKeyDown event wasn't consumed in this case.
   1284        else {
   1285          AutoTArray<uint32_t, 10> accessCharCodes;
   1286          keyEvent->GetAccessKeyCandidates(accessCharCodes);
   1287 
   1288          if (HandleAccessKey(keyEvent, aPresContext, accessCharCodes)) {
   1289            *aStatus = nsEventStatus_eConsumeNoDefault;
   1290          }
   1291        }
   1292      }
   1293    }
   1294      // then fall through...
   1295      [[fallthrough]];
   1296    case eKeyDown:
   1297      if (aEvent->mMessage == eKeyDown) {
   1298        NotifyTargetUserActivation(aEvent, aTargetContent);
   1299      }
   1300      [[fallthrough]];
   1301    case eKeyUp: {
   1302      Element* element = GetFocusedElement();
   1303      if (element) {
   1304        mCurrentTargetContent = element;
   1305      }
   1306 
   1307      // NOTE: Don't refer TextComposition::IsComposing() since UI Events
   1308      //       defines that KeyboardEvent.isComposing is true when it's
   1309      //       dispatched after compositionstart and compositionend.
   1310      //       TextComposition::IsComposing() is false even before
   1311      //       compositionend if there is no composing string.
   1312      //       And also don't expose other document's composition state.
   1313      //       A native IME context is typically shared by multiple documents.
   1314      //       So, don't use GetTextCompositionFor(nsIWidget*) here.
   1315      RefPtr<TextComposition> composition =
   1316          IMEStateManager::GetTextCompositionFor(aPresContext);
   1317      aEvent->AsKeyboardEvent()->mIsComposing = !!composition;
   1318 
   1319      // Widget may need to perform default action for specific keyboard
   1320      // event if it's not consumed.  In this case, widget has already marked
   1321      // the event as "waiting reply from remote process".  However, we need
   1322      // to reset it if the target (focused content) isn't in a remote process
   1323      // because PresShell needs to check if it's marked as so before
   1324      // dispatching events into the DOM tree.
   1325      if (aEvent->IsWaitingReplyFromRemoteProcess() &&
   1326          !aEvent->PropagationStopped() && !IsTopLevelRemoteTarget(element)) {
   1327        aEvent->ResetWaitingReplyFromRemoteProcessState();
   1328      }
   1329    } break;
   1330    case eWheel:
   1331    case eWheelOperationStart:
   1332    case eWheelOperationEnd: {
   1333      NS_ASSERTION(aEvent->IsTrusted(),
   1334                   "Untrusted wheel event shouldn't be here");
   1335      using DeltaModeCheckingState = WidgetWheelEvent::DeltaModeCheckingState;
   1336 
   1337      if (Element* element = GetFocusedElement()) {
   1338        mCurrentTargetContent = element;
   1339      }
   1340 
   1341      if (aEvent->mMessage != eWheel) {
   1342        break;
   1343      }
   1344 
   1345      WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
   1346      WheelPrefs::GetInstance()->ApplyUserPrefsToDelta(wheelEvent);
   1347 
   1348      // If we won't dispatch a DOM event for this event, nothing to do anymore.
   1349      if (!wheelEvent->IsAllowedToDispatchDOMEvent()) {
   1350        break;
   1351      }
   1352 
   1353      if (StaticPrefs::dom_event_wheel_deltaMode_lines_always_disabled()) {
   1354        wheelEvent->mDeltaModeCheckingState = DeltaModeCheckingState::Unchecked;
   1355      } else if (ShouldAlwaysUseLineDeltas()) {
   1356        wheelEvent->mDeltaModeCheckingState = DeltaModeCheckingState::Checked;
   1357      } else {
   1358        wheelEvent->mDeltaModeCheckingState = DeltaModeCheckingState::Unknown;
   1359      }
   1360 
   1361      // Init lineOrPageDelta values for line scroll events for some devices
   1362      // on some platforms which might dispatch wheel events which don't
   1363      // have lineOrPageDelta values.  And also, if delta values are
   1364      // customized by prefs, this recomputes them.
   1365      DeltaAccumulator::GetInstance()->InitLineOrPageDelta(aTargetFrame, this,
   1366                                                           wheelEvent);
   1367    } break;
   1368    case eSetSelection: {
   1369      RefPtr<Element> focuedElement = GetFocusedElement();
   1370      IMEStateManager::HandleSelectionEvent(aPresContext, focuedElement,
   1371                                            aEvent->AsSelectionEvent());
   1372      break;
   1373    }
   1374    case eContentCommandCut:
   1375    case eContentCommandCopy:
   1376    case eContentCommandPaste:
   1377    case eContentCommandDelete:
   1378    case eContentCommandUndo:
   1379    case eContentCommandRedo:
   1380    case eContentCommandPasteTransferable:
   1381    case eContentCommandLookUpDictionary:
   1382      DoContentCommandEvent(aEvent->AsContentCommandEvent());
   1383      break;
   1384    case eContentCommandInsertText:
   1385      DoContentCommandInsertTextEvent(aEvent->AsContentCommandEvent());
   1386      break;
   1387    case eContentCommandReplaceText:
   1388      DoContentCommandReplaceTextEvent(aEvent->AsContentCommandEvent());
   1389      break;
   1390    case eContentCommandScroll:
   1391      DoContentCommandScrollEvent(aEvent->AsContentCommandEvent());
   1392      break;
   1393    case eCompositionStart:
   1394      if (aEvent->IsTrusted()) {
   1395        // If the event is trusted event, set the selected text to data of
   1396        // composition event.
   1397        WidgetCompositionEvent* compositionEvent = aEvent->AsCompositionEvent();
   1398        WidgetQueryContentEvent querySelectedTextEvent(
   1399            true, eQuerySelectedText, compositionEvent->mWidget);
   1400        HandleQueryContentEvent(&querySelectedTextEvent);
   1401        if (querySelectedTextEvent.FoundSelection()) {
   1402          compositionEvent->mData = querySelectedTextEvent.mReply->DataRef();
   1403        }
   1404        NS_ASSERTION(querySelectedTextEvent.Succeeded(),
   1405                     "Failed to get selected text");
   1406      }
   1407      break;
   1408    case eTouchStart:
   1409      SetGestureDownPoint(aEvent->AsTouchEvent());
   1410      break;
   1411    default:
   1412      break;
   1413  }
   1414  return NS_OK;
   1415 }
   1416 
   1417 // Returns true if this event is likely an user activation for a link or
   1418 // a link-like button, where modifier keys are likely be used for controlling
   1419 // where the link is opened.
   1420 //
   1421 // The modifiers associated with the user activation is used for controlling
   1422 // where the `window.open` is opened into.
   1423 static bool CanReflectModifiersToUserActivation(WidgetInputEvent* aEvent) {
   1424  MOZ_ASSERT(aEvent->mMessage == eKeyDown || aEvent->mMessage == ePointerDown ||
   1425             aEvent->mMessage == ePointerUp);
   1426 
   1427  WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
   1428  if (keyEvent) {
   1429    return keyEvent->CanReflectModifiersToUserActivation();
   1430  }
   1431 
   1432  return true;
   1433 }
   1434 
   1435 void EventStateManager::NotifyTargetUserActivation(WidgetEvent* aEvent,
   1436                                                   nsIContent* aTargetContent) {
   1437  if (!aEvent->IsTrusted()) {
   1438    return;
   1439  }
   1440 
   1441  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   1442  if (mouseEvent && !mouseEvent->IsReal()) {
   1443    return;
   1444  }
   1445 
   1446  nsCOMPtr<nsINode> node = aTargetContent;
   1447  if (!node) {
   1448    return;
   1449  }
   1450 
   1451  Document* doc = node->OwnerDoc();
   1452  if (!doc) {
   1453    return;
   1454  }
   1455 
   1456  // Don't gesture activate for key events for keys which are likely
   1457  // to be interaction with the browser, OS.
   1458  WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
   1459  if (keyEvent && !keyEvent->CanUserGestureActivateTarget()) {
   1460    return;
   1461  }
   1462 
   1463  // Touch gestures that end outside the drag target were touches that turned
   1464  // into scroll/pan/swipe actions. We don't want to gesture activate on such
   1465  // actions, we want to only gesture activate on touches that are taps.
   1466  // That is, touches that end in roughly the same place that they started.
   1467  if ((aEvent->mMessage == eTouchEnd ||
   1468       (aEvent->mMessage == ePointerUp &&
   1469        aEvent->AsPointerEvent()->mInputSource ==
   1470            MouseEvent_Binding::MOZ_SOURCE_TOUCH)) &&
   1471      IsEventOutsideDragThreshold(aEvent->AsInputEvent())) {
   1472    return;
   1473  }
   1474 
   1475  // Do not treat the click on scrollbar as a user interaction with the web
   1476  // content.
   1477  if (StaticPrefs::dom_user_activation_ignore_scrollbars() &&
   1478      (aEvent->mMessage == ePointerDown || aEvent->mMessage == ePointerUp) &&
   1479      aTargetContent->IsInNativeAnonymousSubtree()) {
   1480    nsIContent* current = aTargetContent;
   1481    do {
   1482      nsIContent* root = current->GetClosestNativeAnonymousSubtreeRoot();
   1483      if (!root) {
   1484        break;
   1485      }
   1486      if (root->IsXULElement(nsGkAtoms::scrollbar)) {
   1487        return;
   1488      }
   1489      current = root->GetParent();
   1490    } while (current);
   1491  }
   1492 
   1493  MOZ_ASSERT(aEvent->mMessage == eKeyDown || aEvent->mMessage == ePointerDown ||
   1494             aEvent->mMessage == ePointerUp);
   1495 
   1496  UserActivation::Modifiers modifiers;
   1497  if (WidgetInputEvent* inputEvent = aEvent->AsInputEvent()) {
   1498    if (CanReflectModifiersToUserActivation(inputEvent)) {
   1499      if (inputEvent->IsShift()) {
   1500        modifiers.SetShift();
   1501      }
   1502      if (inputEvent->IsMeta()) {
   1503        modifiers.SetMeta();
   1504      }
   1505      if (inputEvent->IsControl()) {
   1506        modifiers.SetControl();
   1507      }
   1508      if (inputEvent->IsAlt()) {
   1509        modifiers.SetAlt();
   1510      }
   1511 
   1512      WidgetMouseEvent* mouseEvent = inputEvent->AsMouseEvent();
   1513      if (mouseEvent) {
   1514        if (mouseEvent->mButton == MouseButton::eMiddle) {
   1515          modifiers.SetMiddleMouse();
   1516        }
   1517      }
   1518    }
   1519  }
   1520  doc->NotifyUserGestureActivation(modifiers);
   1521 }
   1522 
   1523 // https://html.spec.whatwg.org/multipage/popover.html#popover-light-dismiss
   1524 void EventStateManager::LightDismissOpenPopovers(WidgetEvent* aEvent,
   1525                                                 nsIContent* aTargetContent) {
   1526  MOZ_ASSERT(aEvent->mMessage == ePointerDown || aEvent->mMessage == ePointerUp,
   1527             "Light dismiss must be called for pointer up/down only");
   1528 
   1529  if (!aEvent->IsTrusted() || !aTargetContent) {
   1530    return;
   1531  }
   1532 
   1533  Element* topmostPopover = aTargetContent->OwnerDoc()->GetTopmostPopoverOf(
   1534      PopoverAttributeState::Auto);
   1535  if (!topmostPopover) {
   1536    return;
   1537  }
   1538 
   1539  // Pointerdown: set document's popover pointerdown target to the result of
   1540  // running topmost clicked popover given target.
   1541  if (aEvent->mMessage == ePointerDown) {
   1542    mPopoverPointerDownTarget = aTargetContent->GetTopmostClickedPopover();
   1543    return;
   1544  }
   1545 
   1546  // Pointerup: hide open popovers.
   1547  RefPtr<nsINode> ancestor = aTargetContent->GetTopmostClickedPopover();
   1548  bool sameTarget = mPopoverPointerDownTarget == ancestor;
   1549  mPopoverPointerDownTarget = nullptr;
   1550  if (!sameTarget) {
   1551    return;
   1552  }
   1553 
   1554  if (!ancestor) {
   1555    ancestor = aTargetContent->OwnerDoc();
   1556  }
   1557  RefPtr<Document> doc(ancestor->OwnerDoc());
   1558  doc->HideAllPopoversUntil(*ancestor, false, true);
   1559 }
   1560 
   1561 // https://html.spec.whatwg.org/multipage/interactive-elements.html#run-light-dismiss-activities
   1562 // https://html.spec.whatwg.org/multipage/interactive-elements.html#light-dismiss-open-dialogs
   1563 void EventStateManager::LightDismissOpenDialogs(WidgetEvent* aEvent,
   1564                                                nsIContent* aTargetContent) {
   1565  // 1. Assert: event's isTrusted attribute is true.
   1566  // 2. Let document be event's target's node document.
   1567  // (Skipped - not applicable)
   1568 
   1569  if (!StaticPrefs::dom_dialog_light_dismiss_enabled()) {
   1570    return;
   1571  }
   1572 
   1573  MOZ_ASSERT(aEvent->mMessage == ePointerDown || aEvent->mMessage == ePointerUp,
   1574             "Light dismiss must be called for pointer up/down only");
   1575 
   1576  if (aEvent->mFlags.mDefaultPrevented || !aEvent->IsTrusted() ||
   1577      !aTargetContent) {
   1578    return;
   1579  }
   1580 
   1581  auto* doc = aTargetContent->OwnerDoc();
   1582 
   1583  // 3. If document's open dialogs list is empty, then return.
   1584  if (!doc->HasOpenDialogs()) {
   1585    return;
   1586  }
   1587 
   1588  // 4. Let ancestor be the result of running nearest clicked dialog given
   1589  // event.
   1590  RefPtr<HTMLDialogElement> ancestor =
   1591      aTargetContent->NearestClickedDialog(aEvent);
   1592 
   1593  // 5. If event's type is "pointerdown", then set document's dialog pointerdown
   1594  // target to ancestor.
   1595  if (aEvent->mMessage == ePointerDown) {
   1596    // XXX: "document's dialog pointerdown target" can be null, but
   1597    // `SetLastDialogPointerdownTarget` takes `&` to avoid incidental nullptrs,
   1598    // meaning we need to nullcheck `ancestor` & call
   1599    // `ClearLastDialogPointerdownTarget` instead.
   1600    if (!ancestor) {
   1601      doc->ClearLastDialogPointerdownTarget();
   1602    } else {
   1603      doc->SetLastDialogPointerdownTarget(*ancestor);
   1604    }
   1605    return;
   1606  }
   1607 
   1608  MOZ_ASSERT(aEvent->mMessage == ePointerUp);
   1609 
   1610  // 6.1 Let sameTarget be true if ancestor is document's dialog pointerdown
   1611  // target.
   1612  RefPtr<HTMLDialogElement> lastDialog = doc->GetLastDialogPointerdownTarget();
   1613  bool sameTarget = ancestor == lastDialog;
   1614 
   1615  // 6.2 Set document's dialog pointerdown target to null.
   1616  doc->ClearLastDialogPointerdownTarget();
   1617 
   1618  // 6.3 If sameTarget is false, then return.
   1619  if (!sameTarget) {
   1620    return;
   1621  }
   1622 
   1623  // 6.4 Let topmostDialog be the last element of document's open dialogs list.
   1624  RefPtr<HTMLDialogElement> topmostDialog = doc->GetTopMostOpenDialog();
   1625 
   1626  // 6.5 If ancestor is topmostDialog, then return.
   1627  if (ancestor == topmostDialog) {
   1628    return;
   1629  }
   1630 
   1631  // 6.6 If topmostDialog's computed closed-by state is not Any, then return.
   1632  if (!topmostDialog ||
   1633      topmostDialog->GetClosedBy() != HTMLDialogElement::ClosedBy::Any) {
   1634    return;
   1635  }
   1636 
   1637  // 7. Assert: topmostDialog's close watcher is not null.
   1638 
   1639  // 8. Request to close topmostDialog's close watcher with false.
   1640  const mozilla::dom::Optional<nsAString> returnValue;
   1641  topmostDialog->RequestClose(returnValue);
   1642 }
   1643 
   1644 already_AddRefed<EventStateManager> EventStateManager::ESMFromContentOrThis(
   1645    nsIContent* aContent) {
   1646  if (aContent) {
   1647    PresShell* presShell = aContent->OwnerDoc()->GetPresShell();
   1648    if (presShell) {
   1649      nsPresContext* prescontext = presShell->GetPresContext();
   1650      if (prescontext) {
   1651        RefPtr<EventStateManager> esm = prescontext->EventStateManager();
   1652        if (esm) {
   1653          return esm.forget();
   1654        }
   1655      }
   1656    }
   1657  }
   1658 
   1659  RefPtr<EventStateManager> esm = this;
   1660  return esm.forget();
   1661 }
   1662 
   1663 EventStateManager::LastMouseDownInfo& EventStateManager::GetLastMouseDownInfo(
   1664    int16_t aButton) {
   1665  switch (aButton) {
   1666    case MouseButton::ePrimary:
   1667      return mLastLeftMouseDownInfo;
   1668    case MouseButton::eMiddle:
   1669      return mLastMiddleMouseDownInfo;
   1670    case MouseButton::eSecondary:
   1671      return mLastRightMouseDownInfo;
   1672    default:
   1673      MOZ_ASSERT_UNREACHABLE("This button shouldn't use this method");
   1674      return mLastLeftMouseDownInfo;
   1675  }
   1676 }
   1677 
   1678 void EventStateManager::HandleQueryContentEvent(
   1679    WidgetQueryContentEvent* aEvent) {
   1680  switch (aEvent->mMessage) {
   1681    case eQuerySelectedText:
   1682    case eQueryTextContent:
   1683    case eQueryCaretRect:
   1684    case eQueryTextRect:
   1685    case eQueryEditorRect:
   1686      if (!IsTargetCrossProcess(aEvent)) {
   1687        break;
   1688      }
   1689      // Will not be handled locally, remote the event
   1690      GetCrossProcessTarget()->HandleQueryContentEvent(*aEvent);
   1691      return;
   1692    // Following events have not been supported in e10s mode yet.
   1693    case eQueryContentState:
   1694    case eQuerySelectionAsTransferable:
   1695    case eQueryCharacterAtPoint:
   1696    case eQueryDOMWidgetHittest:
   1697    case eQueryTextRectArray:
   1698    case eQueryDropTargetHittest:
   1699      break;
   1700    default:
   1701      return;
   1702  }
   1703 
   1704  // If there is an IMEContentObserver, we need to handle QueryContentEvent
   1705  // with it.
   1706  // eQueryDropTargetHittest is not really an IME event, though
   1707  if (mIMEContentObserver && aEvent->mMessage != eQueryDropTargetHittest) {
   1708    RefPtr<IMEContentObserver> contentObserver = mIMEContentObserver;
   1709    contentObserver->HandleQueryContentEvent(aEvent);
   1710    return;
   1711  }
   1712 
   1713  ContentEventHandler handler(mPresContext);
   1714  handler.HandleQueryContentEvent(aEvent);
   1715 }
   1716 
   1717 static AccessKeyType GetAccessKeyTypeFor(nsISupports* aDocShell) {
   1718  nsCOMPtr<nsIDocShellTreeItem> treeItem(do_QueryInterface(aDocShell));
   1719  if (!treeItem) {
   1720    return AccessKeyType::eNone;
   1721  }
   1722 
   1723  switch (treeItem->ItemType()) {
   1724    case nsIDocShellTreeItem::typeChrome:
   1725      return AccessKeyType::eChrome;
   1726    case nsIDocShellTreeItem::typeContent:
   1727      return AccessKeyType::eContent;
   1728    default:
   1729      return AccessKeyType::eNone;
   1730  }
   1731 }
   1732 
   1733 static bool IsAccessKeyTarget(Element* aElement, nsAString& aKey) {
   1734  // Use GetAttr because we want Unicode case=insensitive matching
   1735  // XXXbz shouldn't this be case-sensitive, per spec?
   1736  nsString contentKey;
   1737  if (!aElement || !aElement->GetAttr(nsGkAtoms::accesskey, contentKey) ||
   1738      !contentKey.Equals(aKey, nsCaseInsensitiveStringComparator)) {
   1739    return false;
   1740  }
   1741 
   1742  if (!aElement->IsXULElement()) {
   1743    return true;
   1744  }
   1745 
   1746  // For XUL we do visibility checks.
   1747  nsIFrame* frame = aElement->GetPrimaryFrame();
   1748  if (!frame) {
   1749    return false;
   1750  }
   1751 
   1752  if (frame->IsFocusable()) {
   1753    return true;
   1754  }
   1755 
   1756  if (!frame->IsVisibleConsideringAncestors()) {
   1757    return false;
   1758  }
   1759 
   1760  // XUL controls can be activated.
   1761  nsCOMPtr<nsIDOMXULControlElement> control = aElement->AsXULControl();
   1762  if (control) {
   1763    return true;
   1764  }
   1765 
   1766  // XUL label elements are never focusable, so we need to check for them
   1767  // explicitly before giving up.
   1768  if (aElement->IsXULElement(nsGkAtoms::label)) {
   1769    return true;
   1770  }
   1771 
   1772  return false;
   1773 }
   1774 
   1775 bool EventStateManager::CheckIfEventMatchesAccessKey(
   1776    WidgetKeyboardEvent* aEvent, nsPresContext* aPresContext) {
   1777  AutoTArray<uint32_t, 10> accessCharCodes;
   1778  aEvent->GetAccessKeyCandidates(accessCharCodes);
   1779  return WalkESMTreeToHandleAccessKey(aEvent, aPresContext, accessCharCodes,
   1780                                      nullptr, eAccessKeyProcessingNormal,
   1781                                      false);
   1782 }
   1783 
   1784 bool EventStateManager::LookForAccessKeyAndExecute(
   1785    nsTArray<uint32_t>& aAccessCharCodes, bool aIsTrustedEvent, bool aIsRepeat,
   1786    bool aExecute) {
   1787  int32_t count, start = -1;
   1788  if (Element* focusedElement = GetFocusedElement()) {
   1789    start = mAccessKeys.IndexOf(focusedElement);
   1790    if (start == -1 && focusedElement->IsInNativeAnonymousSubtree()) {
   1791      start = mAccessKeys.IndexOf(Element::FromNodeOrNull(
   1792          focusedElement->GetClosestNativeAnonymousSubtreeRootParentOrHost()));
   1793    }
   1794  }
   1795  RefPtr<Element> element;
   1796  int32_t length = mAccessKeys.Count();
   1797  for (uint32_t i = 0; i < aAccessCharCodes.Length(); ++i) {
   1798    uint32_t ch = aAccessCharCodes[i];
   1799    nsAutoString accessKey;
   1800    AppendUCS4ToUTF16(ch, accessKey);
   1801    for (count = 1; count <= length; ++count) {
   1802      // mAccessKeys always stores Element instances.
   1803      MOZ_DIAGNOSTIC_ASSERT(length == mAccessKeys.Count());
   1804      element = mAccessKeys[(start + count) % length];
   1805      if (IsAccessKeyTarget(element, accessKey)) {
   1806        if (!aExecute) {
   1807          return true;
   1808        }
   1809        Document* doc = element->OwnerDoc();
   1810        const bool shouldActivate = [&] {
   1811          if (!StaticPrefs::accessibility_accesskeycausesactivation()) {
   1812            return false;
   1813          }
   1814          if (aIsRepeat && nsContentUtils::IsChromeDoc(doc)) {
   1815            return false;
   1816          }
   1817 
   1818          // XXXedgar, Bug 1700646, maybe we could use other data structure to
   1819          // make searching target with same accesskey easier, and current setup
   1820          // could not ensure we cycle the target with tree order.
   1821          int32_t j = 0;
   1822          while (++j < length) {
   1823            Element* el = mAccessKeys[(start + count + j) % length];
   1824            if (IsAccessKeyTarget(el, accessKey)) {
   1825              return false;
   1826            }
   1827          }
   1828          return true;
   1829        }();
   1830 
   1831        // TODO(bug 1641171): This shouldn't be needed if we considered the
   1832        // accesskey combination properly.
   1833        if (aIsTrustedEvent) {
   1834          doc->NotifyUserGestureActivation();
   1835        }
   1836 
   1837        auto result =
   1838            element->PerformAccesskey(shouldActivate, aIsTrustedEvent);
   1839        if (result.isOk()) {
   1840          if (result.unwrap() && aIsTrustedEvent) {
   1841            // If this is a child process, inform the parent that we want the
   1842            // focus, but pass false since we don't want to change the window
   1843            // order.
   1844            nsIDocShell* docShell = mPresContext->GetDocShell();
   1845            nsCOMPtr<nsIBrowserChild> child =
   1846                docShell ? docShell->GetBrowserChild() : nullptr;
   1847            if (child) {
   1848              child->SendRequestFocus(false, CallerType::System);
   1849            }
   1850          }
   1851          return true;
   1852        }
   1853      }
   1854    }
   1855  }
   1856  return false;
   1857 }
   1858 
   1859 // static
   1860 void EventStateManager::GetAccessKeyLabelPrefix(Element* aElement,
   1861                                                nsAString& aPrefix) {
   1862  aPrefix.Truncate();
   1863  nsAutoString separator, modifierText;
   1864  nsContentUtils::GetModifierSeparatorText(separator);
   1865 
   1866  AccessKeyType accessKeyType =
   1867      GetAccessKeyTypeFor(aElement->OwnerDoc()->GetDocShell());
   1868  if (accessKeyType == AccessKeyType::eNone) {
   1869    return;
   1870  }
   1871  Modifiers modifiers = WidgetKeyboardEvent::AccessKeyModifiers(accessKeyType);
   1872  if (modifiers == MODIFIER_NONE) {
   1873    return;
   1874  }
   1875 
   1876  if (modifiers & MODIFIER_CONTROL) {
   1877    nsContentUtils::GetControlText(modifierText);
   1878    aPrefix.Append(modifierText + separator);
   1879  }
   1880  if (modifiers & MODIFIER_META) {
   1881    nsContentUtils::GetCommandOrWinText(modifierText);
   1882    aPrefix.Append(modifierText + separator);
   1883  }
   1884  if (modifiers & MODIFIER_ALT) {
   1885    nsContentUtils::GetAltText(modifierText);
   1886    aPrefix.Append(modifierText + separator);
   1887  }
   1888  if (modifiers & MODIFIER_SHIFT) {
   1889    nsContentUtils::GetShiftText(modifierText);
   1890    aPrefix.Append(modifierText + separator);
   1891  }
   1892 }
   1893 
   1894 struct MOZ_STACK_CLASS AccessKeyInfo {
   1895  WidgetKeyboardEvent* event;
   1896  nsTArray<uint32_t>& charCodes;
   1897 
   1898  AccessKeyInfo(WidgetKeyboardEvent* aEvent, nsTArray<uint32_t>& aCharCodes)
   1899      : event(aEvent), charCodes(aCharCodes) {}
   1900 };
   1901 
   1902 bool EventStateManager::WalkESMTreeToHandleAccessKey(
   1903    WidgetKeyboardEvent* aEvent, nsPresContext* aPresContext,
   1904    nsTArray<uint32_t>& aAccessCharCodes, nsIDocShellTreeItem* aBubbledFrom,
   1905    ProcessingAccessKeyState aAccessKeyState, bool aExecute) {
   1906  EnsureDocument(mPresContext);
   1907  nsCOMPtr<nsIDocShell> docShell = aPresContext->GetDocShell();
   1908  if (NS_WARN_IF(!docShell) || NS_WARN_IF(!mDocument)) {
   1909    return false;
   1910  }
   1911  AccessKeyType accessKeyType = GetAccessKeyTypeFor(docShell);
   1912  if (accessKeyType == AccessKeyType::eNone) {
   1913    return false;
   1914  }
   1915  // Alt or other accesskey modifier is down, we may need to do an accesskey.
   1916  if (mAccessKeys.Count() > 0 &&
   1917      aEvent->ModifiersMatchWithAccessKey(accessKeyType)) {
   1918    // Someone registered an accesskey.  Find and activate it.
   1919    if (LookForAccessKeyAndExecute(aAccessCharCodes, aEvent->IsTrusted(),
   1920                                   aEvent->mIsRepeat, aExecute)) {
   1921      return true;
   1922    }
   1923  }
   1924 
   1925  int32_t childCount;
   1926  docShell->GetInProcessChildCount(&childCount);
   1927  for (int32_t counter = 0; counter < childCount; counter++) {
   1928    // Not processing the child which bubbles up the handling
   1929    nsCOMPtr<nsIDocShellTreeItem> subShellItem;
   1930    docShell->GetInProcessChildAt(counter, getter_AddRefs(subShellItem));
   1931    if (aAccessKeyState == eAccessKeyProcessingUp &&
   1932        subShellItem == aBubbledFrom) {
   1933      continue;
   1934    }
   1935 
   1936    nsCOMPtr<nsIDocShell> subDS = do_QueryInterface(subShellItem);
   1937    if (subDS && IsShellVisible(subDS)) {
   1938      // Guarantee subPresShell lifetime while we're handling access key
   1939      // since somebody may assume that it won't be deleted before the
   1940      // corresponding nsPresContext and EventStateManager.
   1941      RefPtr<PresShell> subPresShell = subDS->GetPresShell();
   1942 
   1943      // Docshells need not have a presshell (eg. display:none
   1944      // iframes, docshells in transition between documents, etc).
   1945      if (!subPresShell) {
   1946        // Oh, well.  Just move on to the next child
   1947        continue;
   1948      }
   1949 
   1950      RefPtr<nsPresContext> subPresContext = subPresShell->GetPresContext();
   1951 
   1952      RefPtr<EventStateManager> esm =
   1953          static_cast<EventStateManager*>(subPresContext->EventStateManager());
   1954 
   1955      if (esm && esm->WalkESMTreeToHandleAccessKey(
   1956                     aEvent, subPresContext, aAccessCharCodes, nullptr,
   1957                     eAccessKeyProcessingDown, aExecute)) {
   1958        return true;
   1959      }
   1960    }
   1961  }  // if end . checking all sub docshell ends here.
   1962 
   1963  // bubble up the process to the parent docshell if necessary
   1964  if (eAccessKeyProcessingDown != aAccessKeyState) {
   1965    nsCOMPtr<nsIDocShellTreeItem> parentShellItem;
   1966    docShell->GetInProcessParent(getter_AddRefs(parentShellItem));
   1967    nsCOMPtr<nsIDocShell> parentDS = do_QueryInterface(parentShellItem);
   1968    if (parentDS) {
   1969      // Guarantee parentPresShell lifetime while we're handling access key
   1970      // since somebody may assume that it won't be deleted before the
   1971      // corresponding nsPresContext and EventStateManager.
   1972      RefPtr<PresShell> parentPresShell = parentDS->GetPresShell();
   1973      NS_ASSERTION(parentPresShell,
   1974                   "Our PresShell exists but the parent's does not?");
   1975 
   1976      RefPtr<nsPresContext> parentPresContext =
   1977          parentPresShell->GetPresContext();
   1978      NS_ASSERTION(parentPresContext, "PresShell without PresContext");
   1979 
   1980      RefPtr<EventStateManager> esm = static_cast<EventStateManager*>(
   1981          parentPresContext->EventStateManager());
   1982      if (esm && esm->WalkESMTreeToHandleAccessKey(
   1983                     aEvent, parentPresContext, aAccessCharCodes, docShell,
   1984                     eAccessKeyProcessingDown, aExecute)) {
   1985        return true;
   1986      }
   1987    }
   1988  }  // if end. bubble up process
   1989 
   1990  // If the content access key modifier is pressed, try remote children
   1991  if (aExecute &&
   1992      aEvent->ModifiersMatchWithAccessKey(AccessKeyType::eContent) &&
   1993      mDocument && mDocument->GetWindow()) {
   1994    // If the focus is currently on a node with a BrowserParent, the key event
   1995    // should've gotten forwarded to the child process and HandleAccessKey
   1996    // called from there.
   1997    if (BrowserParent::GetFrom(GetFocusedElement())) {
   1998      // If access key may be only in remote contents, this method won't handle
   1999      // access key synchronously.  In this case, only reply event should reach
   2000      // here.
   2001      MOZ_ASSERT(aEvent->IsHandledInRemoteProcess() ||
   2002                 !aEvent->IsWaitingReplyFromRemoteProcess());
   2003    }
   2004    // If focus is somewhere else, then we need to check the remote children.
   2005    // However, if the event has already been handled in a remote process,
   2006    // then, focus is moved from the remote process after posting the event.
   2007    // In such case, we shouldn't retry to handle access keys in remote
   2008    // processes.
   2009    else if (!aEvent->IsHandledInRemoteProcess()) {
   2010      AccessKeyInfo accessKeyInfo(aEvent, aAccessCharCodes);
   2011      nsContentUtils::CallOnAllRemoteChildren(
   2012          mDocument->GetWindow(),
   2013          [&accessKeyInfo](BrowserParent* aBrowserParent) -> CallState {
   2014            // Only forward accesskeys for the active tab.
   2015            if (aBrowserParent->GetDocShellIsActive()) {
   2016              // Even if there is no target for the accesskey in this process,
   2017              // the event may match with a content accesskey.  If so, the
   2018              // keyboard event should be handled with reply event for
   2019              // preventing double action. (e.g., Alt+Shift+F on Windows may
   2020              // focus a content in remote and open "File" menu.)
   2021              accessKeyInfo.event->StopPropagation();
   2022              accessKeyInfo.event->MarkAsWaitingReplyFromRemoteProcess();
   2023              aBrowserParent->HandleAccessKey(*accessKeyInfo.event,
   2024                                              accessKeyInfo.charCodes);
   2025              return CallState::Stop;
   2026            }
   2027 
   2028            return CallState::Continue;
   2029          });
   2030    }
   2031  }
   2032 
   2033  return false;
   2034 }  // end of HandleAccessKey
   2035 
   2036 static BrowserParent* GetBrowserParentAncestor(BrowserParent* aBrowserParent) {
   2037  MOZ_ASSERT(aBrowserParent);
   2038 
   2039  BrowserBridgeParent* bbp = aBrowserParent->GetBrowserBridgeParent();
   2040  if (!bbp) {
   2041    return nullptr;
   2042  }
   2043 
   2044  return bbp->Manager();
   2045 }
   2046 
   2047 static void DispatchCrossProcessMouseExitEvents(WidgetMouseEvent* aMouseEvent,
   2048                                                BrowserParent* aRemoteTarget,
   2049                                                BrowserParent* aStopAncestor,
   2050                                                bool aIsReallyExit) {
   2051  MOZ_ASSERT(aMouseEvent);
   2052  MOZ_ASSERT(aRemoteTarget);
   2053  MOZ_ASSERT(aRemoteTarget != aStopAncestor);
   2054  MOZ_ASSERT_IF(aStopAncestor, nsContentUtils::GetCommonBrowserParentAncestor(
   2055                                   aRemoteTarget, aStopAncestor));
   2056 
   2057  while (aRemoteTarget != aStopAncestor) {
   2058    UniquePtr<WidgetMouseEvent> mouseExitEvent =
   2059        CreateMouseOrPointerWidgetEvent(aMouseEvent, eMouseExitFromWidget,
   2060                                        aMouseEvent->mRelatedTarget);
   2061    mouseExitEvent->mExitFrom =
   2062        Some(aIsReallyExit ? WidgetMouseEvent::ePuppet
   2063                           : WidgetMouseEvent::ePuppetParentToPuppetChild);
   2064 
   2065    auto ContentReactsToPointerEvents = [](BrowserParent* aRemoteTarget) {
   2066      if (Element* owner = aRemoteTarget->GetOwnerElement()) {
   2067        if (nsSubDocumentFrame* subDocFrame =
   2068                do_QueryFrame(owner->GetPrimaryFrame())) {
   2069          return subDocFrame->ContentReactsToPointerEvents();
   2070        }
   2071      }
   2072      return true;
   2073    };
   2074 
   2075    if (ContentReactsToPointerEvents(aRemoteTarget)) {
   2076      aRemoteTarget->SendRealMouseEvent(*mouseExitEvent);
   2077    }
   2078 
   2079    aRemoteTarget = GetBrowserParentAncestor(aRemoteTarget);
   2080  }
   2081 }
   2082 
   2083 void EventStateManager::DispatchCrossProcessEvent(WidgetEvent* aEvent,
   2084                                                  BrowserParent* aRemoteTarget,
   2085                                                  nsEventStatus* aStatus) {
   2086  MOZ_ASSERT(aEvent);
   2087  MOZ_ASSERT(aRemoteTarget);
   2088  MOZ_ASSERT(aStatus);
   2089 
   2090  BrowserParent* remote = aRemoteTarget;
   2091 
   2092  WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   2093  bool isContextMenuKey = mouseEvent && mouseEvent->IsContextMenuKeyEvent();
   2094  if (aEvent->mClass == eKeyboardEventClass || isContextMenuKey) {
   2095    // APZ attaches a LayersId to hit-testable events, for keyboard events,
   2096    // we use focus.
   2097    BrowserParent* preciseRemote = BrowserParent::GetFocused();
   2098    if (preciseRemote) {
   2099      remote = preciseRemote;
   2100    }
   2101    // else there is a race between layout and focus tracking,
   2102    // so fall back to delivering the event to the topmost child process.
   2103  } else if (aEvent->mLayersId.IsValid()) {
   2104    BrowserParent* preciseRemote =
   2105        BrowserParent::GetBrowserParentFromLayersId(aEvent->mLayersId);
   2106    if (preciseRemote) {
   2107      remote = preciseRemote;
   2108    }
   2109    // else there is a race between APZ and the LayersId to BrowserParent
   2110    // mapping, so fall back to delivering the event to the topmost child
   2111    // process.
   2112  }
   2113 
   2114  MOZ_ASSERT(aEvent->mMessage != ePointerClick);
   2115  MOZ_ASSERT(aEvent->mMessage != ePointerAuxClick);
   2116 
   2117  // SendReal* will transform the coordinate to the child process coordinate
   2118  // space. So restore the coordinate after the event has been dispatched to the
   2119  // child process to avoid using the transformed coordinate afterward.
   2120  AutoRestore<LayoutDeviceIntPoint> restore(aEvent->mRefPoint);
   2121  switch (aEvent->mClass) {
   2122    case ePointerEventClass:
   2123      MOZ_ASSERT(aEvent->mMessage == eContextMenu);
   2124      [[fallthrough]];
   2125    case eMouseEventClass: {
   2126      BrowserParent* oldRemote = BrowserParent::GetLastMouseRemoteTarget();
   2127 
   2128      // If this is a eMouseExitFromWidget event, need to redirect the event to
   2129      // the last remote and and notify all its ancestors about the exit, if
   2130      // any.
   2131      if (mouseEvent->mMessage == eMouseExitFromWidget) {
   2132        MOZ_ASSERT(mouseEvent->mExitFrom.value() == WidgetMouseEvent::ePuppet);
   2133        MOZ_ASSERT(mouseEvent->mReason == WidgetMouseEvent::eReal);
   2134        MOZ_ASSERT(!mouseEvent->mLayersId.IsValid());
   2135        MOZ_ASSERT(remote->GetBrowserHost());
   2136 
   2137        if (oldRemote && oldRemote != remote) {
   2138          (void)NS_WARN_IF(nsContentUtils::GetCommonBrowserParentAncestor(
   2139                               remote, oldRemote) != remote);
   2140          remote = oldRemote;
   2141        }
   2142 
   2143        DispatchCrossProcessMouseExitEvents(mouseEvent, remote, nullptr, true);
   2144        return;
   2145      }
   2146 
   2147      if (BrowserParent* pointerLockedRemote =
   2148              PointerLockManager::GetLockedRemoteTarget()) {
   2149        remote = pointerLockedRemote;
   2150      } else if (BrowserParent* pointerCapturedRemote =
   2151                     PointerEventHandler::GetPointerCapturingRemoteTarget(
   2152                         mouseEvent->pointerId)) {
   2153        remote = pointerCapturedRemote;
   2154      } else if (BrowserParent* capturingRemote =
   2155                     PresShell::GetCapturingRemoteTarget()) {
   2156        remote = capturingRemote;
   2157      }
   2158 
   2159      // If a mouse is over a remote target A, and then moves to
   2160      // remote target B, we'd deliver the event directly to remote target B
   2161      // after the moving, A would never get notified that the mouse left.
   2162      // So we generate a exit event to notify A after the move.
   2163      // XXXedgar, if the synthesized mouse events could deliver to the correct
   2164      // process directly (see
   2165      // https://bugzilla.mozilla.org/show_bug.cgi?id=1549355), we probably
   2166      // don't need to check mReason then.
   2167      if (mouseEvent->mReason == WidgetMouseEvent::eReal &&
   2168          remote != oldRemote) {
   2169        MOZ_ASSERT(mouseEvent->mMessage != eMouseExitFromWidget);
   2170        if (oldRemote) {
   2171          BrowserParent* commonAncestor =
   2172              nsContentUtils::GetCommonBrowserParentAncestor(remote, oldRemote);
   2173          if (commonAncestor == oldRemote) {
   2174            // Mouse moves to the inner OOP frame, it is not a really exit.
   2175            DispatchCrossProcessMouseExitEvents(
   2176                mouseEvent, GetBrowserParentAncestor(remote),
   2177                GetBrowserParentAncestor(commonAncestor), false);
   2178          } else if (commonAncestor == remote) {
   2179            // Mouse moves to the outer OOP frame, it is a really exit.
   2180            DispatchCrossProcessMouseExitEvents(mouseEvent, oldRemote,
   2181                                                commonAncestor, true);
   2182          } else {
   2183            // Mouse moves to OOP frame in other subtree, it is a really exit,
   2184            // need to notify all its ancestors before common ancestor about the
   2185            // exit.
   2186            DispatchCrossProcessMouseExitEvents(mouseEvent, oldRemote,
   2187                                                commonAncestor, true);
   2188            if (commonAncestor) {
   2189              UniquePtr<WidgetMouseEvent> mouseExitEvent =
   2190                  CreateMouseOrPointerWidgetEvent(mouseEvent,
   2191                                                  eMouseExitFromWidget,
   2192                                                  mouseEvent->mRelatedTarget);
   2193              mouseExitEvent->mExitFrom =
   2194                  Some(WidgetMouseEvent::ePuppetParentToPuppetChild);
   2195              commonAncestor->SendRealMouseEvent(*mouseExitEvent);
   2196            }
   2197          }
   2198        }
   2199 
   2200        if (mouseEvent->mMessage != eMouseExitFromWidget &&
   2201            mouseEvent->mMessage != eMouseEnterIntoWidget) {
   2202          // This is to make cursor would be updated correctly.
   2203          remote->MouseEnterIntoWidget();
   2204        }
   2205      }
   2206 
   2207      remote->SendRealMouseEvent(*mouseEvent);
   2208      return;
   2209    }
   2210    case eKeyboardEventClass: {
   2211      auto* keyboardEvent = aEvent->AsKeyboardEvent();
   2212      if (aEvent->mMessage == eKeyUp) {
   2213        HandleKeyUpInteraction(keyboardEvent);
   2214      }
   2215      remote->SendRealKeyEvent(*keyboardEvent);
   2216      return;
   2217    }
   2218    case eWheelEventClass: {
   2219      if (BrowserParent* pointerLockedRemote =
   2220              PointerLockManager::GetLockedRemoteTarget()) {
   2221        remote = pointerLockedRemote;
   2222      }
   2223      remote->SendMouseWheelEvent(*aEvent->AsWheelEvent());
   2224      return;
   2225    }
   2226    case eTouchEventClass: {
   2227      // Let the child process synthesize a mouse event if needed, and
   2228      // ensure we don't synthesize one in this process.
   2229      *aStatus = nsEventStatus_eConsumeNoDefault;
   2230      remote->SendRealTouchEvent(*aEvent->AsTouchEvent());
   2231      return;
   2232    }
   2233    case eDragEventClass: {
   2234      RefPtr<BrowserParent> browserParent = remote;
   2235      browserParent->MaybeInvokeDragSession(aEvent->mMessage);
   2236 
   2237      RefPtr<nsIWidget> widget = browserParent->GetTopLevelWidget();
   2238      nsCOMPtr<nsIDragSession> dragSession =
   2239          nsContentUtils::GetDragSession(widget);
   2240      uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
   2241      uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
   2242      nsCOMPtr<nsIPrincipal> principal;
   2243      nsCOMPtr<nsIPolicyContainer> policyContainer;
   2244 
   2245      if (dragSession) {
   2246        dragSession->DragEventDispatchedToChildProcess();
   2247        dragSession->GetDragAction(&action);
   2248        dragSession->GetTriggeringPrincipal(getter_AddRefs(principal));
   2249        dragSession->GetPolicyContainer(getter_AddRefs(policyContainer));
   2250        RefPtr<DataTransfer> initialDataTransfer =
   2251            dragSession->GetDataTransfer();
   2252        if (initialDataTransfer) {
   2253          dropEffect = initialDataTransfer->DropEffectInt();
   2254        }
   2255      }
   2256 
   2257      browserParent->SendRealDragEvent(*aEvent->AsDragEvent(), action,
   2258                                       dropEffect, principal, policyContainer);
   2259      return;
   2260    }
   2261    default: {
   2262      MOZ_CRASH("Attempt to send non-whitelisted event?");
   2263    }
   2264  }
   2265 }
   2266 
   2267 bool EventStateManager::IsRemoteTarget(nsIContent* target) {
   2268  return BrowserParent::GetFrom(target) || BrowserBridgeChild::GetFrom(target);
   2269 }
   2270 
   2271 bool EventStateManager::IsTopLevelRemoteTarget(nsIContent* target) {
   2272  return !!BrowserParent::GetFrom(target);
   2273 }
   2274 
   2275 bool EventStateManager::HandleCrossProcessEvent(WidgetEvent* aEvent,
   2276                                                nsEventStatus* aStatus) {
   2277  if (!aEvent->CanBeSentToRemoteProcess()) {
   2278    return false;
   2279  }
   2280 
   2281  MOZ_ASSERT(!aEvent->HasBeenPostedToRemoteProcess(),
   2282             "Why do we need to post same event to remote processes again?");
   2283 
   2284  // Collect the remote event targets we're going to forward this
   2285  // event to.
   2286  //
   2287  // NB: the elements of |remoteTargets| must be unique, for correctness.
   2288  AutoTArray<RefPtr<BrowserParent>, 1> remoteTargets;
   2289  if (aEvent->mClass != eTouchEventClass || aEvent->mMessage == eTouchStart) {
   2290    // If this event only has one target, and it's remote, add it to
   2291    // the array.
   2292    nsIFrame* frame = aEvent->mMessage == eDragExit
   2293                          ? sLastDragOverFrame.GetFrame()
   2294                          : GetEventTarget();
   2295    nsIContent* target = frame ? frame->GetContent() : nullptr;
   2296    if (BrowserParent* remoteTarget = BrowserParent::GetFrom(target)) {
   2297      remoteTargets.AppendElement(remoteTarget);
   2298    }
   2299  } else {
   2300    // This is a touch event with possibly multiple touch points.
   2301    // Each touch point may have its own target.  So iterate through
   2302    // all of them and collect the unique set of targets for event
   2303    // forwarding.
   2304    //
   2305    // This loop is similar to the one used in
   2306    // PresShell::DispatchTouchEvent().
   2307    const WidgetTouchEvent::TouchArray& touches =
   2308        aEvent->AsTouchEvent()->mTouches;
   2309    for (uint32_t i = 0; i < touches.Length(); ++i) {
   2310      Touch* touch = touches[i];
   2311      // NB: the |mChanged| check is an optimization, subprocesses can
   2312      // compute this for themselves.  If the touch hasn't changed, we
   2313      // may be able to avoid forwarding the event entirely (which is
   2314      // not free).
   2315      if (!touch || !touch->mChanged) {
   2316        continue;
   2317      }
   2318      nsCOMPtr<EventTarget> targetPtr = touch->mTarget;
   2319      if (!targetPtr) {
   2320        continue;
   2321      }
   2322      nsCOMPtr<nsIContent> target = do_QueryInterface(targetPtr);
   2323      BrowserParent* remoteTarget = BrowserParent::GetFrom(target);
   2324      if (remoteTarget && !remoteTargets.Contains(remoteTarget)) {
   2325        remoteTargets.AppendElement(remoteTarget);
   2326      }
   2327    }
   2328  }
   2329 
   2330  if (remoteTargets.Length() == 0) {
   2331    return false;
   2332  }
   2333 
   2334  // Dispatch the event to the remote target.
   2335  for (uint32_t i = 0; i < remoteTargets.Length(); ++i) {
   2336    DispatchCrossProcessEvent(aEvent, remoteTargets[i], aStatus);
   2337  }
   2338  return aEvent->HasBeenPostedToRemoteProcess();
   2339 }
   2340 
   2341 //
   2342 // CreateClickHoldTimer
   2343 //
   2344 // Fire off a timer for determining if the user wants click-hold. This timer
   2345 // is a one-shot that will be cancelled when the user moves enough to fire
   2346 // a drag.
   2347 //
   2348 void EventStateManager::CreateClickHoldTimer(nsPresContext* inPresContext,
   2349                                             nsIFrame* inDownFrame,
   2350                                             WidgetGUIEvent* inMouseDownEvent) {
   2351  if (!inMouseDownEvent->IsTrusted() ||
   2352      IsTopLevelRemoteTarget(mGestureDownContent) ||
   2353      PointerLockManager::IsLocked()) {
   2354    return;
   2355  }
   2356 
   2357  // just to be anal (er, safe)
   2358  if (mClickHoldTimer) {
   2359    mClickHoldTimer->Cancel();
   2360    mClickHoldTimer = nullptr;
   2361  }
   2362 
   2363  // if content clicked on has a popup, don't even start the timer
   2364  // since we'll end up conflicting and both will show.
   2365  if (mGestureDownContent &&
   2366      nsContentUtils::HasNonEmptyAttr(mGestureDownContent, kNameSpaceID_None,
   2367                                      nsGkAtoms::popup)) {
   2368    return;
   2369  }
   2370 
   2371  int32_t clickHoldDelay = StaticPrefs::ui_click_hold_context_menus_delay();
   2372  NS_NewTimerWithFuncCallback(
   2373      getter_AddRefs(mClickHoldTimer), sClickHoldCallback, this, clickHoldDelay,
   2374      nsITimer::TYPE_ONE_SHOT, "EventStateManager::CreateClickHoldTimer"_ns);
   2375 }  // CreateClickHoldTimer
   2376 
   2377 //
   2378 // KillClickHoldTimer
   2379 //
   2380 // Stop the timer that would show the context menu dead in its tracks
   2381 //
   2382 void EventStateManager::KillClickHoldTimer() {
   2383  if (mClickHoldTimer) {
   2384    mClickHoldTimer->Cancel();
   2385    mClickHoldTimer = nullptr;
   2386  }
   2387 }
   2388 
   2389 //
   2390 // sClickHoldCallback
   2391 //
   2392 // This fires after the mouse has been down for a certain length of time.
   2393 //
   2394 void EventStateManager::sClickHoldCallback(nsITimer* aTimer, void* aESM) {
   2395  RefPtr<EventStateManager> self = static_cast<EventStateManager*>(aESM);
   2396  if (self) {
   2397    self->FireContextClick();
   2398  }
   2399 
   2400  // NOTE: |aTimer| and |self->mAutoHideTimer| are invalid after calling
   2401  // ClosePopup();
   2402 
   2403 }  // sAutoHideCallback
   2404 
   2405 //
   2406 // FireContextClick
   2407 //
   2408 // If we're this far, our timer has fired, which means the mouse has been down
   2409 // for a certain period of time and has not moved enough to generate a
   2410 // dragGesture. We can be certain the user wants a context-click at this stage,
   2411 // so generate a dom event and fire it in.
   2412 //
   2413 // After the event fires, check if PreventDefault() has been set on the event
   2414 // which means that someone either ate the event or put up a context menu. This
   2415 // is our cue to stop tracking the drag gesture. If we always did this,
   2416 // draggable items w/out a context menu wouldn't be draggable after a certain
   2417 // length of time, which is _not_ what we want.
   2418 //
   2419 void EventStateManager::FireContextClick() {
   2420  if (!mGestureDownContent || !mPresContext || PointerLockManager::IsLocked()) {
   2421    return;
   2422  }
   2423 
   2424 #ifdef XP_MACOSX
   2425  // Hack to ensure that we don't show a context menu when the user
   2426  // let go of the mouse after a long cpu-hogging operation prevented
   2427  // us from handling any OS events. See bug 117589.
   2428  if (!CGEventSourceButtonState(kCGEventSourceStateCombinedSessionState,
   2429                                kCGMouseButtonLeft))
   2430    return;
   2431 #endif
   2432 
   2433  nsEventStatus status = nsEventStatus_eIgnore;
   2434 
   2435  // Dispatch to the DOM. We have to fake out the ESM and tell it that the
   2436  // current target frame is actually where the mouseDown occurred, otherwise it
   2437  // will use the frame the mouse is currently over which may or may not be
   2438  // the same. (Note: saari and I have decided that we don't have to reset
   2439  // |mCurrentTarget| when we're through because no one else is doing anything
   2440  // more with this event and it will get reset on the very next event to the
   2441  // correct frame).
   2442  mCurrentTarget = mPresContext->GetPrimaryFrameFor(mGestureDownContent);
   2443  // make sure the widget sticks around
   2444  nsCOMPtr<nsIWidget> targetWidget;
   2445  if (mCurrentTarget && (targetWidget = mCurrentTarget->GetNearestWidget())) {
   2446    NS_ASSERTION(
   2447        mPresContext == mCurrentTarget->PresContext(),
   2448        "a prescontext returned a primary frame that didn't belong to it?");
   2449 
   2450    // before dispatching, check that we're not on something that
   2451    // doesn't get a context menu
   2452    bool allowedToDispatch = true;
   2453 
   2454    if (mGestureDownContent->IsAnyOfXULElements(nsGkAtoms::scrollbar,
   2455                                                nsGkAtoms::scrollbarbutton,
   2456                                                nsGkAtoms::button)) {
   2457      allowedToDispatch = false;
   2458    } else if (mGestureDownContent->IsXULElement(nsGkAtoms::toolbarbutton)) {
   2459      // a <toolbarbutton> that has the container attribute set
   2460      // will already have its own dropdown.
   2461      if (nsContentUtils::HasNonEmptyAttr(
   2462              mGestureDownContent, kNameSpaceID_None, nsGkAtoms::container)) {
   2463        allowedToDispatch = false;
   2464      } else {
   2465        // If the toolbar button has an open menu, don't attempt to open
   2466        // a second menu
   2467        if (mGestureDownContent->IsElement() &&
   2468            mGestureDownContent->AsElement()->AttrValueIs(
   2469                kNameSpaceID_None, nsGkAtoms::open, nsGkAtoms::_true,
   2470                eCaseMatters)) {
   2471          allowedToDispatch = false;
   2472        }
   2473      }
   2474    } else if (mGestureDownContent->IsHTMLElement()) {
   2475      if (const auto* formCtrl =
   2476              nsIFormControl::FromNode(mGestureDownContent)) {
   2477        allowedToDispatch =
   2478            formCtrl->IsTextControl(/*aExcludePassword*/ false) ||
   2479            formCtrl->ControlType() == FormControlType::InputFile;
   2480      } else if (mGestureDownContent->IsAnyOfHTMLElements(
   2481                     nsGkAtoms::embed, nsGkAtoms::object, nsGkAtoms::label)) {
   2482        allowedToDispatch = false;
   2483      }
   2484    }
   2485 
   2486    if (allowedToDispatch) {
   2487      // init the event while mCurrentTarget is still good
   2488      WidgetPointerEvent event(true, eContextMenu, targetWidget);
   2489      event.mClickCount = 1;
   2490      FillInEventFromGestureDown(&event);
   2491 
   2492      // we need to forget the clicking content and click count for the
   2493      // following eMouseUp event when click-holding context menus
   2494      LastMouseDownInfo& mouseDownInfo = GetLastMouseDownInfo(event.mButton);
   2495      mouseDownInfo.mLastMouseDownContent = nullptr;
   2496      mouseDownInfo.mClickCount = 0;
   2497      mouseDownInfo.mLastMouseDownInputControlType = Nothing();
   2498 
   2499      // stop selection tracking, we're in control now
   2500      if (mCurrentTarget) {
   2501        RefPtr<nsFrameSelection> frameSel = mCurrentTarget->GetFrameSelection();
   2502 
   2503        if (frameSel && frameSel->GetDragState()) {
   2504          // note that this can cause selection changed events to fire if we're
   2505          // in a text field, which will null out mCurrentTarget
   2506          frameSel->SetDragState(false);
   2507        }
   2508      }
   2509 
   2510      AutoHandlingUserInputStatePusher userInpStatePusher(true, &event);
   2511 
   2512      // dispatch to DOM
   2513      RefPtr<nsPresContext> presContext = mPresContext;
   2514 
   2515      // The contextmenu event handled by PresShell will apply to elements (not
   2516      // all nodes) correctly and will be dispatched to EventStateManager for
   2517      // further handling preventing click event and stopping tracking drag
   2518      // gesture.
   2519      if (RefPtr<PresShell> presShell = presContext->GetPresShell()) {
   2520        presShell->HandleEvent(mCurrentTarget, &event, false, &status);
   2521      }
   2522 
   2523      // We don't need to dispatch to frame handling because no frames
   2524      // watch eContextMenu except for nsMenuFrame and that's only for
   2525      // dismissal. That's just as well since we don't really know
   2526      // which frame to send it to.
   2527    }
   2528  }
   2529 
   2530  // stop tracking a drag whatever the event has been handled or not.
   2531  StopTrackingDragGesture(true);
   2532 
   2533  KillClickHoldTimer();
   2534 
   2535 }  // FireContextClick
   2536 
   2537 //
   2538 // BeginTrackingDragGesture
   2539 //
   2540 // Record that the mouse has gone down and that we should move to TRACKING state
   2541 // of d&d gesture tracker.
   2542 //
   2543 // We also use this to track click-hold context menus. When the mouse goes down,
   2544 // fire off a short timer. If the timer goes off and we have yet to fire the
   2545 // drag gesture (ie, the mouse hasn't moved a certain distance), then we can
   2546 // assume the user wants a click-hold, so fire a context-click event. We only
   2547 // want to cancel the drag gesture if the context-click event is handled.
   2548 //
   2549 void EventStateManager::BeginTrackingDragGesture(nsPresContext* aPresContext,
   2550                                                 WidgetMouseEvent* inDownEvent,
   2551                                                 nsIFrame* inDownFrame) {
   2552  if (!inDownEvent->mWidget) {
   2553    return;
   2554  }
   2555 
   2556  // Note that |inDownEvent| could be either a mouse down event or a
   2557  // synthesized mouse move event.
   2558  SetGestureDownPoint(inDownEvent);
   2559 
   2560  if (inDownFrame) {
   2561    mGestureDownContent = inDownFrame->GetContentForEvent(inDownEvent);
   2562    mGestureDownFrameOwner = inDownFrame->GetContent();
   2563    if (!mGestureDownFrameOwner) {
   2564      mGestureDownFrameOwner = mGestureDownContent;
   2565    }
   2566  }
   2567  mGestureModifiers = inDownEvent->mModifiers;
   2568  mGestureDownButtons = inDownEvent->mButtons;
   2569  mGestureDownButton = inDownEvent->mButton;
   2570 
   2571  if (inDownEvent->mMessage != eMouseTouchDrag &&
   2572      StaticPrefs::ui_click_hold_context_menus()) {
   2573    // fire off a timer to track click-hold
   2574    CreateClickHoldTimer(aPresContext, inDownFrame, inDownEvent);
   2575  }
   2576 }
   2577 
   2578 void EventStateManager::SetGestureDownPoint(WidgetGUIEvent* aEvent) {
   2579  mGestureDownPoint =
   2580      GetEventRefPoint(aEvent) + aEvent->mWidget->WidgetToScreenOffset();
   2581 }
   2582 
   2583 LayoutDeviceIntPoint EventStateManager::GetEventRefPoint(
   2584    WidgetEvent* aEvent) const {
   2585  auto touchEvent = aEvent->AsTouchEvent();
   2586  return (touchEvent && !touchEvent->mTouches.IsEmpty())
   2587             ? aEvent->AsTouchEvent()->mTouches[0]->mRefPoint
   2588             : aEvent->mRefPoint;
   2589 }
   2590 
   2591 void EventStateManager::BeginTrackingRemoteDragGesture(
   2592    nsIContent* aContent, RemoteDragStartData* aDragStartData) {
   2593  UpdateGestureContent(aContent);
   2594  mGestureDownDragStartData = aDragStartData;
   2595 }
   2596 
   2597 //
   2598 // StopTrackingDragGesture
   2599 //
   2600 // Record that the mouse has gone back up so that we should leave the TRACKING
   2601 // state of d&d gesture tracker and return to the START state.
   2602 //
   2603 void EventStateManager::StopTrackingDragGesture(bool aClearInChildProcesses) {
   2604  mGestureDownContent = nullptr;
   2605  mGestureDownFrameOwner = nullptr;
   2606  mGestureDownInTextControl = false;
   2607  mGestureDownDragStartData = nullptr;
   2608 
   2609  // If a content process starts a drag but the mouse is released before the
   2610  // parent starts the actual drag, the content process will think a drag is
   2611  // still happening. Inform any child processes with active drags that the drag
   2612  // should be stopped.
   2613  if (!aClearInChildProcesses || !XRE_IsParentProcess()) {
   2614    return;
   2615  }
   2616 
   2617  // Only notify if there is NOT a drag session active in the parent.
   2618  RefPtr<nsIDragSession> dragSession =
   2619      nsContentUtils::GetDragSession(mPresContext);
   2620  if (dragSession) {
   2621    return;
   2622  }
   2623  nsCOMPtr<nsIDragService> dragService =
   2624      do_GetService("@mozilla.org/widget/dragservice;1");
   2625  if (!dragService) {
   2626    return;
   2627  }
   2628  dragService->RemoveAllBrowsers();
   2629 }
   2630 
   2631 void EventStateManager::FillInEventFromGestureDown(WidgetMouseEvent* aEvent) {
   2632  NS_ASSERTION(aEvent->mWidget == mCurrentTarget->GetNearestWidget(),
   2633               "Incorrect widget in event");
   2634 
   2635  // Set the coordinates in the new event to the coordinates of
   2636  // the old event, adjusted for the fact that the widget might be
   2637  // different
   2638  aEvent->mRefPoint =
   2639      mGestureDownPoint - aEvent->mWidget->WidgetToScreenOffset();
   2640  aEvent->mModifiers = mGestureModifiers;
   2641  aEvent->mButtons = mGestureDownButtons;
   2642  if (aEvent->mMessage == eContextMenu) {
   2643    aEvent->mButton = mGestureDownButton;
   2644  }
   2645 }
   2646 
   2647 void EventStateManager::MaybeDispatchPointerCancel(
   2648    const WidgetInputEvent& aSourceEvent, nsIContent& aTargetContent) {
   2649  // Dispatching ePointerCancel clears out mCurrentTarget, which may be used in
   2650  // the caller GenerateDragGesture. We have to restore mCurrentTarget.
   2651  AutoWeakFrame targetFrame = mCurrentTarget;
   2652  const auto restoreCurrentTarget =
   2653      MakeScopeExit([&]() { mCurrentTarget = targetFrame; });
   2654 
   2655  const RefPtr<Element> targetElement =
   2656      aTargetContent.GetAsElementOrParentElement();
   2657  // XXX If there is no proper event target, should we retarget ePointerCancel
   2658  // somewhere else?
   2659  if (NS_WARN_IF(!targetElement)) {
   2660    return;
   2661  }
   2662 
   2663  if (const WidgetMouseEvent* const mouseEvent = aSourceEvent.AsMouseEvent()) {
   2664    PointerEventHandler::DispatchPointerEventWithTarget(
   2665        ePointerCancel, *mouseEvent, AutoWeakFrame{}, targetElement);
   2666  } else if (const WidgetTouchEvent* const touchEvent =
   2667                 aSourceEvent.AsTouchEvent()) {
   2668    PointerEventHandler::DispatchPointerEventWithTarget(
   2669        ePointerCancel, *touchEvent, 0, AutoWeakFrame{}, targetElement);
   2670  } else {
   2671    MOZ_ASSERT_UNREACHABLE(
   2672        "MaybeDispatchPointerCancel() should be called with a mouse event or a "
   2673        "touch event");
   2674  }
   2675 }
   2676 
   2677 bool EventStateManager::IsEventOutsideDragThreshold(
   2678    WidgetInputEvent* aEvent) const {
   2679  static int32_t sPixelThresholdX = 0;
   2680  static int32_t sPixelThresholdY = 0;
   2681 
   2682  if (!sPixelThresholdX) {
   2683    sPixelThresholdX =
   2684        LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdX, 0);
   2685    sPixelThresholdY =
   2686        LookAndFeel::GetInt(LookAndFeel::IntID::DragThresholdY, 0);
   2687    if (sPixelThresholdX <= 0) {
   2688      sPixelThresholdX = 5;
   2689    }
   2690    if (sPixelThresholdY <= 0) {
   2691      sPixelThresholdY = 5;
   2692    }
   2693  }
   2694 
   2695  LayoutDeviceIntPoint pt =
   2696      aEvent->mWidget->WidgetToScreenOffset() + GetEventRefPoint(aEvent);
   2697  LayoutDeviceIntPoint distance = pt - mGestureDownPoint;
   2698  return Abs(distance.x) > sPixelThresholdX ||
   2699         Abs(distance.y) > sPixelThresholdY;
   2700 }
   2701 
   2702 //
   2703 // GenerateDragGesture
   2704 //
   2705 // If we're in the TRACKING state of the d&d gesture tracker, check the current
   2706 // position of the mouse in relation to the old one. If we've moved a sufficient
   2707 // amount from the mouse down, then fire off a drag gesture event.
   2708 void EventStateManager::GenerateDragGesture(nsPresContext* aPresContext,
   2709                                            WidgetInputEvent* aEvent) {
   2710  NS_ASSERTION(aPresContext, "This shouldn't happen.");
   2711  MOZ_ASSERT_IF(aEvent->AsMouseEvent(), aEvent->AsMouseEvent()->IsReal());
   2712  if (!IsTrackingDragGesture()) {
   2713    return;
   2714  }
   2715 
   2716  AutoWeakFrame targetFrameBefore = mCurrentTarget;
   2717  auto autoRestore = MakeScopeExit([&] { mCurrentTarget = targetFrameBefore; });
   2718 
   2719  mCurrentTarget = nullptr;
   2720  // Try to find a suitable frame by looping through the ancestors chain.
   2721  for (auto* content :
   2722       mGestureDownFrameOwner->InclusiveFlatTreeAncestorsOfType<nsIContent>()) {
   2723    if (nsIFrame* target = content->GetPrimaryFrame()) {
   2724      mCurrentTarget = target;
   2725 
   2726      if (content != mGestureDownFrameOwner) {
   2727        UpdateGestureContent(content);
   2728      }
   2729      break;
   2730    }
   2731  }
   2732 
   2733  if (!mCurrentTarget || !mCurrentTarget->GetNearestWidget()) {
   2734    StopTrackingDragGesture(true);
   2735    return;
   2736  }
   2737 
   2738  // Check if selection is tracking drag gestures, if so
   2739  // don't interfere!
   2740  if (mCurrentTarget) {
   2741    RefPtr<nsFrameSelection> frameSel = mCurrentTarget->GetFrameSelection();
   2742    if (frameSel && frameSel->GetDragState()) {
   2743      StopTrackingDragGesture(true);
   2744      return;
   2745    }
   2746  }
   2747 
   2748  // If non-native code is capturing the mouse don't start a drag.
   2749  if (PresShell::IsMouseCapturePreventingDrag()) {
   2750    StopTrackingDragGesture(true);
   2751    return;
   2752  }
   2753 
   2754  if (!IsEventOutsideDragThreshold(aEvent)) {
   2755    // To keep the old behavior, flush layout even if we don't start dnd.
   2756    FlushLayout(aPresContext);
   2757    return;
   2758  }
   2759 
   2760  if (StaticPrefs::ui_click_hold_context_menus()) {
   2761    // stop the click-hold before we fire off the drag gesture, in case
   2762    // it takes a long time
   2763    KillClickHoldTimer();
   2764  }
   2765 
   2766  nsCOMPtr<nsIDocShell> docshell = aPresContext->GetDocShell();
   2767  if (!docshell) {
   2768    return;
   2769  }
   2770 
   2771  nsCOMPtr<nsPIDOMWindowOuter> window = docshell->GetWindow();
   2772  if (!window) return;
   2773 
   2774  RefPtr<DataTransfer> dataTransfer =
   2775      new DataTransfer(window, eDragStart, /* aIsExternal */ false,
   2776                       /* aClipboardType */ Nothing());
   2777  auto protectDataTransfer = MakeScopeExit([&] {
   2778    if (dataTransfer) {
   2779      dataTransfer->Disconnect();
   2780    }
   2781  });
   2782 
   2783  RefPtr<Selection> selection;
   2784  RefPtr<RemoteDragStartData> remoteDragStartData;
   2785  nsCOMPtr<nsIPrincipal> principal;
   2786  nsCOMPtr<nsIPolicyContainer> policyContainer;
   2787  nsCOMPtr<nsICookieJarSettings> cookieJarSettings;
   2788  nsCOMPtr<nsIContent> eventContent =
   2789      mCurrentTarget->GetContentForEvent(aEvent);
   2790  nsCOMPtr<nsIContent> targetContent;
   2791  bool allowEmptyDataTransfer = false;
   2792  if (eventContent) {
   2793    // If the content is a text node in a password field, we shouldn't
   2794    // allow to drag its raw text.  Note that we've supported drag from
   2795    // password fields but dragging data was masked text.  So, it doesn't
   2796    // make sense anyway.
   2797    if (eventContent->IsText() && eventContent->HasFlag(NS_MAYBE_MASKED)) {
   2798      // However, it makes sense to allow to drag selected password text
   2799      // when copying selected password is allowed because users may want
   2800      // to use drag and drop rather than copy and paste when web apps
   2801      // request to input password twice for conforming new password but
   2802      // they used password generator.
   2803      const TextEditor* const textEditor =
   2804          nsContentUtils::GetExtantTextEditorFromAnonymousNode(eventContent);
   2805      if (!textEditor || !textEditor->IsCopyToClipboardAllowed()) {
   2806        StopTrackingDragGesture(true);
   2807        return;
   2808      }
   2809    }
   2810    DetermineDragTargetAndDefaultData(
   2811        window, eventContent, dataTransfer, &allowEmptyDataTransfer,
   2812        getter_AddRefs(selection), getter_AddRefs(remoteDragStartData),
   2813        getter_AddRefs(targetContent), getter_AddRefs(principal),
   2814        getter_AddRefs(policyContainer), getter_AddRefs(cookieJarSettings));
   2815  }
   2816 
   2817  // Stop tracking the drag gesture now. This should stop us from
   2818  // reentering GenerateDragGesture inside DOM event processing.
   2819  // Pass false to avoid clearing the child process state since a real
   2820  // drag should be starting.
   2821  StopTrackingDragGesture(false);
   2822 
   2823  if (MOZ_UNLIKELY(!targetContent)) {
   2824    return;
   2825  }
   2826 
   2827  // Use our targetContent, now that we've determined it, as the
   2828  // parent object of the DataTransfer.
   2829  nsCOMPtr<nsIContent> parentContent =
   2830      targetContent->FindFirstNonChromeOnlyAccessContent();
   2831  dataTransfer->SetParentObject(parentContent);
   2832 
   2833  sLastDragOverFrame = nullptr;
   2834  nsCOMPtr<nsIWidget> widget = mCurrentTarget->GetNearestWidget();
   2835 
   2836  // get the widget from the target frame
   2837  WidgetDragEvent startEvent(aEvent->IsTrusted(), eDragStart, widget);
   2838  startEvent.mFlags.mIsSynthesizedForTests =
   2839      aEvent->mFlags.mIsSynthesizedForTests;
   2840  FillInEventFromGestureDown(&startEvent);
   2841 
   2842  startEvent.mDataTransfer = dataTransfer;
   2843  if (aEvent->AsMouseEvent()) {
   2844    startEvent.mInputSource = aEvent->AsMouseEvent()->mInputSource;
   2845  } else if (aEvent->AsTouchEvent()) {
   2846    startEvent.mInputSource = MouseEvent_Binding::MOZ_SOURCE_TOUCH;
   2847  } else {
   2848    MOZ_ASSERT(false);
   2849  }
   2850 
   2851  // Dispatch to the DOM. By setting mCurrentTarget we are faking
   2852  // out the ESM and telling it that the current target frame is
   2853  // actually where the mouseDown occurred, otherwise it will use
   2854  // the frame the mouse is currently over which may or may not be
   2855  // the same.
   2856 
   2857  // Hold onto old target content through the event and reset after.
   2858  nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
   2859 
   2860  {
   2861    AutoConnectedAncestorTracker trackTargetContent(*targetContent);
   2862    // Set the current target to the content for the mouse down
   2863    mCurrentTargetContent = targetContent;
   2864 
   2865    // Dispatch the dragstart event to the DOM.
   2866    nsEventStatus status = nsEventStatus_eIgnore;
   2867    EventDispatcher::Dispatch(targetContent, aPresContext, &startEvent, nullptr,
   2868                              &status);
   2869 
   2870    WidgetDragEvent* event = &startEvent;
   2871 
   2872    // Emit observer event to allow addons to modify the DataTransfer
   2873    // object.
   2874    if (nsCOMPtr<nsIObserverService> observerService =
   2875            mozilla::services::GetObserverService()) {
   2876      observerService->NotifyObservers(dataTransfer,
   2877                                       "on-datatransfer-available", nullptr);
   2878    }
   2879 
   2880    if (status != nsEventStatus_eConsumeNoDefault) {
   2881      bool dragStarted = DoDefaultDragStart(
   2882          aPresContext, event, dataTransfer, allowEmptyDataTransfer,
   2883          targetContent, selection, remoteDragStartData, principal,
   2884          policyContainer, cookieJarSettings);
   2885      if (dragStarted) {
   2886        sActiveESM = nullptr;
   2887        aEvent->StopPropagation();
   2888        // XXX If all elements were removed from the document, we may need to
   2889        // dispatch ePointerCancel on the Document node.
   2890        if ((targetContent = trackTargetContent.GetConnectedContent())) {
   2891          MaybeDispatchPointerCancel(*aEvent, *targetContent);
   2892        }
   2893      }
   2894    }
   2895  }
   2896 
   2897  // Reset mCurretTargetContent to what it was
   2898  mCurrentTargetContent = targetBeforeEvent;
   2899 
   2900  // Now flush all pending notifications, for better responsiveness
   2901  // while dragging.
   2902  FlushLayout(aPresContext);
   2903 }  // GenerateDragGesture
   2904 
   2905 void EventStateManager::DetermineDragTargetAndDefaultData(
   2906    nsPIDOMWindowOuter* aWindow, nsIContent* aSelectionTarget,
   2907    DataTransfer* aDataTransfer, bool* aAllowEmptyDataTransfer,
   2908    Selection** aSelection, RemoteDragStartData** aRemoteDragStartData,
   2909    nsIContent** aTargetNode, nsIPrincipal** aPrincipal,
   2910    nsIPolicyContainer** aPolicyContainer,
   2911    nsICookieJarSettings** aCookieJarSettings) {
   2912  *aTargetNode = nullptr;
   2913  *aAllowEmptyDataTransfer = false;
   2914  nsCOMPtr<nsIContent> dragDataNode;
   2915 
   2916  nsIContent* editingElement = aSelectionTarget->IsEditable()
   2917                                   ? aSelectionTarget->GetEditingHost()
   2918                                   : nullptr;
   2919 
   2920  // In chrome, only allow dragging inside editable areas.
   2921  bool isChromeContext = !aWindow->GetBrowsingContext()->IsContent();
   2922  if (isChromeContext && !editingElement) {
   2923    if (mGestureDownDragStartData) {
   2924      // A child process started a drag so use any data it assigned for the dnd
   2925      // session.
   2926      mGestureDownDragStartData->AddInitialDnDDataTo(
   2927          aDataTransfer, aPrincipal, aPolicyContainer, aCookieJarSettings);
   2928      mGestureDownDragStartData.forget(aRemoteDragStartData);
   2929      *aAllowEmptyDataTransfer = true;
   2930    }
   2931  } else {
   2932    mGestureDownDragStartData = nullptr;
   2933 
   2934    // GetDragData determines if a selection, link or image in the content
   2935    // should be dragged, and places the data associated with the drag in the
   2936    // data transfer.
   2937    // mGestureDownContent is the node where the mousedown event for the drag
   2938    // occurred, and aSelectionTarget is the node to use when a selection is
   2939    // used
   2940    bool canDrag;
   2941    bool wasAlt = (mGestureModifiers & MODIFIER_ALT) != 0;
   2942    nsresult rv = nsContentAreaDragDrop::GetDragData(
   2943        aWindow, mGestureDownContent, aSelectionTarget, wasAlt, aDataTransfer,
   2944        &canDrag, aSelection, getter_AddRefs(dragDataNode), aPolicyContainer,
   2945        aCookieJarSettings);
   2946    if (NS_FAILED(rv) || !canDrag) {
   2947      return;
   2948    }
   2949  }
   2950 
   2951  // if GetDragData returned a node, use that as the node being dragged.
   2952  // Otherwise, if a selection is being dragged, use the node within the
   2953  // selection that was dragged. Otherwise, just use the mousedown target.
   2954  nsIContent* dragContent = mGestureDownContent;
   2955  if (dragDataNode)
   2956    dragContent = dragDataNode;
   2957  else if (*aSelection)
   2958    dragContent = aSelectionTarget;
   2959 
   2960  nsIContent* originalDragContent = dragContent;
   2961 
   2962  // If a selection isn't being dragged, look for an ancestor with the
   2963  // draggable property set. If one is found, use that as the target of the
   2964  // drag instead of the node that was clicked on. If a draggable node wasn't
   2965  // found, just use the clicked node.
   2966  if (!*aSelection) {
   2967    while (dragContent) {
   2968      if (auto htmlElement = nsGenericHTMLElement::FromNode(dragContent)) {
   2969        if (htmlElement->Draggable()) {
   2970          // We let draggable elements to trigger dnd even if there is no data
   2971          // in the DataTransfer.
   2972          *aAllowEmptyDataTransfer = true;
   2973          break;
   2974        }
   2975      } else {
   2976        if (dragContent->IsXULElement()) {
   2977          // All XUL elements are draggable, so if a XUL element is
   2978          // encountered, stop looking for draggable nodes and just use the
   2979          // original clicked node instead.
   2980          // XXXndeakin
   2981          // In the future, we will want to improve this so that XUL has a
   2982          // better way to specify whether something is draggable than just
   2983          // on/off.
   2984          dragContent = mGestureDownContent;
   2985          break;
   2986        }
   2987        // otherwise, it's not an HTML or XUL element, so just keep looking
   2988      }
   2989      dragContent = dragContent->GetFlattenedTreeParent();
   2990    }
   2991  }
   2992 
   2993  // if no node in the hierarchy was found to drag, but the GetDragData method
   2994  // returned a node, use that returned node. Otherwise, nothing is draggable.
   2995  if (!dragContent && dragDataNode) dragContent = dragDataNode;
   2996 
   2997  if (dragContent) {
   2998    // if an ancestor node was used instead, clear the drag data
   2999    // XXXndeakin rework this a bit. Find a way to just not call GetDragData if
   3000    // we don't need to.
   3001    if (dragContent != originalDragContent) aDataTransfer->ClearAll();
   3002    *aTargetNode = dragContent;
   3003    NS_ADDREF(*aTargetNode);
   3004  }
   3005 }
   3006 
   3007 bool EventStateManager::DoDefaultDragStart(
   3008    nsPresContext* aPresContext, WidgetDragEvent* aDragEvent,
   3009    DataTransfer* aDataTransfer, bool aAllowEmptyDataTransfer,
   3010    nsIContent* aDragTarget, Selection* aSelection,
   3011    RemoteDragStartData* aDragStartData, nsIPrincipal* aPrincipal,
   3012    nsIPolicyContainer* aPolicyContainer,
   3013    nsICookieJarSettings* aCookieJarSettings) {
   3014  nsCOMPtr<nsIDragService> dragService =
   3015      do_GetService("@mozilla.org/widget/dragservice;1");
   3016  if (!dragService) return false;
   3017 
   3018  // Default handling for the dragstart event.
   3019  //
   3020  // First, check if a drag session already exists. This means that the drag
   3021  // service was called directly within a draggesture handler. In this case,
   3022  // don't do anything more, as it is assumed that the handler is managing
   3023  // drag and drop manually. Make sure to return true to indicate that a drag
   3024  // began.  However, if we're handling drag session for synthesized events,
   3025  // we need to initialize some information of the session.  Therefore, we
   3026  // need to keep going for synthesized case.
   3027  if (MOZ_UNLIKELY(!mPresContext)) {
   3028    return true;
   3029  }
   3030  nsCOMPtr<nsIDragSession> dragSession =
   3031      dragService->GetCurrentSession(mPresContext->GetRootWidget());
   3032  if (dragSession && !dragSession->IsSynthesizedForTests()) {
   3033    return true;
   3034  }
   3035 
   3036  // No drag session is currently active, so check if a handler added
   3037  // any items to be dragged. If not, there isn't anything to drag.
   3038  uint32_t count = 0;
   3039  if (aDataTransfer) {
   3040    count = aDataTransfer->MozItemCount();
   3041  }
   3042  if (!aAllowEmptyDataTransfer && !count) {
   3043    return false;
   3044  }
   3045 
   3046  // Get the target being dragged, which may not be the same as the
   3047  // target of the mouse event. If one wasn't set in the
   3048  // aDataTransfer during the event handler, just use the original
   3049  // target instead.
   3050  nsCOMPtr<nsIContent> dragTarget = aDataTransfer->GetDragTarget();
   3051  if (!dragTarget) {
   3052    dragTarget = aDragTarget;
   3053    if (!dragTarget) {
   3054      return false;
   3055    }
   3056  }
   3057 
   3058  // check which drag effect should initially be used. If the effect was not
   3059  // set, just use all actions, otherwise Windows won't allow a drop.
   3060  uint32_t action = aDataTransfer->EffectAllowedInt();
   3061  if (action == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED) {
   3062    action = nsIDragService::DRAGDROP_ACTION_COPY |
   3063             nsIDragService::DRAGDROP_ACTION_MOVE |
   3064             nsIDragService::DRAGDROP_ACTION_LINK;
   3065  }
   3066 
   3067  // get any custom drag image that was set
   3068  int32_t imageX, imageY;
   3069  RefPtr<Element> dragImage = aDataTransfer->GetDragImage(&imageX, &imageY);
   3070 
   3071  nsCOMPtr<nsIArray> transArray = aDataTransfer->GetTransferables(dragTarget);
   3072  if (!transArray) {
   3073    return false;
   3074  }
   3075 
   3076  RefPtr<DataTransfer> dataTransfer;
   3077  if (!dragSession) {
   3078    // After this function returns, the DataTransfer will be cleared so it
   3079    // appears empty to content. We need to pass a DataTransfer into the Drag
   3080    // Session, so we need to make a copy.
   3081    aDataTransfer->Clone(aDragTarget, eDrop, aDataTransfer->MozUserCancelled(),
   3082                         false, getter_AddRefs(dataTransfer));
   3083 
   3084    // Copy over the drop effect, as Clone doesn't copy it for us.
   3085    dataTransfer->SetDropEffectInt(aDataTransfer->DropEffectInt());
   3086  } else {
   3087    MOZ_ASSERT(dragSession->IsSynthesizedForTests());
   3088    MOZ_ASSERT(aDragEvent->mFlags.mIsSynthesizedForTests);
   3089    // If we're initializing synthesized drag session, we should use given
   3090    // DataTransfer as is because it'll be used with following drag events
   3091    // in any tests, therefore it should be set to nsIDragSession.dataTransfer
   3092    // because it and DragEvent.dataTransfer should be same instance.
   3093    dataTransfer = aDataTransfer;
   3094  }
   3095 
   3096  // XXXndeakin don't really want to create a new drag DOM event
   3097  // here, but we need something to pass to the InvokeDragSession
   3098  // methods.
   3099  RefPtr<DragEvent> event =
   3100      NS_NewDOMDragEvent(dragTarget, aPresContext, aDragEvent);
   3101 
   3102  // Use InvokeDragSessionWithSelection if a selection is being dragged,
   3103  // such that the image can be generated from the selected text. However,
   3104  // use InvokeDragSessionWithImage if a custom image was set or something
   3105  // other than a selection is being dragged.
   3106  if (!dragImage && aSelection) {
   3107    dragService->InvokeDragSessionWithSelection(
   3108        aSelection, aPrincipal, aPolicyContainer, aCookieJarSettings,
   3109        transArray, action, event, dataTransfer, dragTarget);
   3110  } else if (aDragStartData) {
   3111    MOZ_ASSERT(XRE_IsParentProcess());
   3112    dragService->InvokeDragSessionWithRemoteImage(
   3113        dragTarget, aPrincipal, aPolicyContainer, aCookieJarSettings,
   3114        transArray, action, aDragStartData, event, dataTransfer);
   3115  } else {
   3116    dragService->InvokeDragSessionWithImage(
   3117        dragTarget, aPrincipal, aPolicyContainer, aCookieJarSettings,
   3118        transArray, action, dragImage, imageX, imageY, event, dataTransfer);
   3119  }
   3120 
   3121  return true;
   3122 }
   3123 
   3124 void EventStateManager::ChangeZoom(bool aIncrease) {
   3125  // Send the zoom change to the top level browser so it will be handled by the
   3126  // front end in the same way as other zoom actions.
   3127  nsIDocShell* docShell = mDocument->GetDocShell();
   3128  if (!docShell) {
   3129    return;
   3130  }
   3131 
   3132  BrowsingContext* bc = docShell->GetBrowsingContext();
   3133  if (!bc) {
   3134    return;
   3135  }
   3136 
   3137  if (XRE_IsParentProcess()) {
   3138    bc->Canonical()->DispatchWheelZoomChange(aIncrease);
   3139  } else if (BrowserChild* child = BrowserChild::GetFrom(docShell)) {
   3140    child->SendWheelZoomChange(aIncrease);
   3141  }
   3142 }
   3143 
   3144 void EventStateManager::DoScrollHistory(int32_t direction) {
   3145  nsCOMPtr<nsISupports> pcContainer(mPresContext->GetContainerWeak());
   3146  if (pcContainer) {
   3147    nsCOMPtr<nsIWebNavigation> webNav(do_QueryInterface(pcContainer));
   3148    if (webNav) {
   3149      // positive direction to go back one step, nonpositive to go forward
   3150      // This is doing user-initiated history traversal, hence we want
   3151      // to require that history entries we navigate to have user interaction.
   3152      if (direction > 0)
   3153        webNav->GoBack(StaticPrefs::browser_navigation_requireUserInteraction(),
   3154                       true);
   3155      else
   3156        webNav->GoForward(
   3157            StaticPrefs::browser_navigation_requireUserInteraction(), true);
   3158    }
   3159  }
   3160 }
   3161 
   3162 void EventStateManager::DoScrollZoom(nsIFrame* aTargetFrame,
   3163                                     int32_t adjustment) {
   3164  // Exclude content in chrome docshells.
   3165  nsIContent* content = aTargetFrame->GetContent();
   3166  if (content && !nsContentUtils::IsInChromeDocshell(content->OwnerDoc())) {
   3167    // Positive adjustment to decrease zoom, negative to increase
   3168    const bool increase = adjustment <= 0;
   3169    EnsureDocument(mPresContext);
   3170    ChangeZoom(increase);
   3171  }
   3172 }
   3173 
   3174 static nsIFrame* GetParentFrameToScroll(nsIFrame* aFrame) {
   3175  if (!aFrame) return nullptr;
   3176 
   3177  if (aFrame->StyleDisplay()->mPosition == StylePositionProperty::Fixed &&
   3178      nsLayoutUtils::IsReallyFixedPos(aFrame)) {
   3179    return aFrame->PresShell()->GetRootScrollContainerFrame();
   3180  }
   3181  return aFrame->GetParent();
   3182 }
   3183 
   3184 void EventStateManager::DispatchLegacyMouseScrollEvents(
   3185    nsIFrame* aTargetFrame, WidgetWheelEvent* aEvent, nsEventStatus* aStatus) {
   3186  MOZ_ASSERT(aEvent);
   3187  MOZ_ASSERT(aStatus);
   3188 
   3189  if (!aTargetFrame || *aStatus == nsEventStatus_eConsumeNoDefault) {
   3190    return;
   3191  }
   3192 
   3193  // Ignore mouse wheel transaction for computing legacy mouse wheel
   3194  // events' delta value.
   3195  // DOM event's delta vales are computed from CSS pixels.
   3196  auto scrollAmountInCSSPixels =
   3197      CSSIntSize::FromAppUnitsRounded(aEvent->mScrollAmount);
   3198 
   3199  // XXX We don't deal with fractional amount in legacy event, though the
   3200  //     default action handler (DoScrollText()) deals with it.
   3201  //     If we implemented such strict computation, we would need additional
   3202  //     accumulated delta values. It would made the code more complicated.
   3203  //     And also it would computes different delta values from older version.
   3204  //     It doesn't make sense to implement such code for legacy events and
   3205  //     rare cases.
   3206  int32_t scrollDeltaX, scrollDeltaY, pixelDeltaX, pixelDeltaY;
   3207  switch (aEvent->mDeltaMode) {
   3208    case WheelEvent_Binding::DOM_DELTA_PAGE:
   3209      scrollDeltaX = !aEvent->mLineOrPageDeltaX
   3210                         ? 0
   3211                         : (aEvent->mLineOrPageDeltaX > 0
   3212                                ? UIEvent_Binding::SCROLL_PAGE_DOWN
   3213                                : UIEvent_Binding::SCROLL_PAGE_UP);
   3214      scrollDeltaY = !aEvent->mLineOrPageDeltaY
   3215                         ? 0
   3216                         : (aEvent->mLineOrPageDeltaY > 0
   3217                                ? UIEvent_Binding::SCROLL_PAGE_DOWN
   3218                                : UIEvent_Binding::SCROLL_PAGE_UP);
   3219      pixelDeltaX = RoundDown(aEvent->mDeltaX * scrollAmountInCSSPixels.width);
   3220      pixelDeltaY = RoundDown(aEvent->mDeltaY * scrollAmountInCSSPixels.height);
   3221      break;
   3222 
   3223    case WheelEvent_Binding::DOM_DELTA_LINE:
   3224      scrollDeltaX = aEvent->mLineOrPageDeltaX;
   3225      scrollDeltaY = aEvent->mLineOrPageDeltaY;
   3226      pixelDeltaX = RoundDown(aEvent->mDeltaX * scrollAmountInCSSPixels.width);
   3227      pixelDeltaY = RoundDown(aEvent->mDeltaY * scrollAmountInCSSPixels.height);
   3228      break;
   3229 
   3230    case WheelEvent_Binding::DOM_DELTA_PIXEL:
   3231      scrollDeltaX = aEvent->mLineOrPageDeltaX;
   3232      scrollDeltaY = aEvent->mLineOrPageDeltaY;
   3233      pixelDeltaX = RoundDown(aEvent->mDeltaX);
   3234      pixelDeltaY = RoundDown(aEvent->mDeltaY);
   3235      break;
   3236 
   3237    default:
   3238      MOZ_CRASH("Invalid deltaMode value comes");
   3239  }
   3240 
   3241  // Send the legacy events in following order:
   3242  // 1. Vertical scroll
   3243  // 2. Vertical pixel scroll (even if #1 isn't consumed)
   3244  // 3. Horizontal scroll (even if #1 and/or #2 are consumed)
   3245  // 4. Horizontal pixel scroll (even if #3 isn't consumed)
   3246 
   3247  AutoWeakFrame targetFrame(aTargetFrame);
   3248 
   3249  MOZ_ASSERT(*aStatus != nsEventStatus_eConsumeNoDefault &&
   3250                 !aEvent->DefaultPrevented(),
   3251             "If you make legacy events dispatched for default prevented wheel "
   3252             "event, you need to initialize stateX and stateY");
   3253  EventState stateX, stateY;
   3254  if (scrollDeltaY) {
   3255    SendLineScrollEvent(aTargetFrame, aEvent, stateY, scrollDeltaY,
   3256                        DELTA_DIRECTION_Y);
   3257    if (!targetFrame.IsAlive()) {
   3258      *aStatus = nsEventStatus_eConsumeNoDefault;
   3259      return;
   3260    }
   3261  }
   3262 
   3263  if (pixelDeltaY) {
   3264    SendPixelScrollEvent(aTargetFrame, aEvent, stateY, pixelDeltaY,
   3265                         DELTA_DIRECTION_Y);
   3266    if (!targetFrame.IsAlive()) {
   3267      *aStatus = nsEventStatus_eConsumeNoDefault;
   3268      return;
   3269    }
   3270  }
   3271 
   3272  if (scrollDeltaX) {
   3273    SendLineScrollEvent(aTargetFrame, aEvent, stateX, scrollDeltaX,
   3274                        DELTA_DIRECTION_X);
   3275    if (!targetFrame.IsAlive()) {
   3276      *aStatus = nsEventStatus_eConsumeNoDefault;
   3277      return;
   3278    }
   3279  }
   3280 
   3281  if (pixelDeltaX) {
   3282    SendPixelScrollEvent(aTargetFrame, aEvent, stateX, pixelDeltaX,
   3283                         DELTA_DIRECTION_X);
   3284    if (!targetFrame.IsAlive()) {
   3285      *aStatus = nsEventStatus_eConsumeNoDefault;
   3286      return;
   3287    }
   3288  }
   3289 
   3290  if (stateY.mDefaultPrevented) {
   3291    *aStatus = nsEventStatus_eConsumeNoDefault;
   3292    aEvent->PreventDefault(!stateY.mDefaultPreventedByContent);
   3293  }
   3294 
   3295  if (stateX.mDefaultPrevented) {
   3296    *aStatus = nsEventStatus_eConsumeNoDefault;
   3297    aEvent->PreventDefault(!stateX.mDefaultPreventedByContent);
   3298  }
   3299 }
   3300 
   3301 void EventStateManager::SendLineScrollEvent(nsIFrame* aTargetFrame,
   3302                                            WidgetWheelEvent* aEvent,
   3303                                            EventState& aState, int32_t aDelta,
   3304                                            DeltaDirection aDeltaDirection) {
   3305  nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
   3306  if (!targetContent) {
   3307    targetContent = GetFocusedElement();
   3308    if (!targetContent) {
   3309      return;
   3310    }
   3311  }
   3312 
   3313  while (targetContent->IsText()) {
   3314    targetContent = targetContent->GetFlattenedTreeParent();
   3315  }
   3316 
   3317  WidgetMouseScrollEvent event(aEvent->IsTrusted(),
   3318                               eLegacyMouseLineOrPageScroll, aEvent->mWidget);
   3319  event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
   3320  event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   3321  event.mRefPoint = aEvent->mRefPoint;
   3322  event.mTimeStamp = aEvent->mTimeStamp;
   3323  event.mModifiers = aEvent->mModifiers;
   3324  event.mButtons = aEvent->mButtons;
   3325  event.mIsHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   3326  event.mDelta = aDelta;
   3327  event.mInputSource = aEvent->mInputSource;
   3328 
   3329  RefPtr<nsPresContext> presContext = aTargetFrame->PresContext();
   3330  nsEventStatus status = nsEventStatus_eIgnore;
   3331  EventDispatcher::Dispatch(targetContent, presContext, &event, nullptr,
   3332                            &status);
   3333  aState.mDefaultPrevented =
   3334      event.DefaultPrevented() || status == nsEventStatus_eConsumeNoDefault;
   3335  aState.mDefaultPreventedByContent = event.DefaultPreventedByContent();
   3336 }
   3337 
   3338 void EventStateManager::SendPixelScrollEvent(nsIFrame* aTargetFrame,
   3339                                             WidgetWheelEvent* aEvent,
   3340                                             EventState& aState,
   3341                                             int32_t aPixelDelta,
   3342                                             DeltaDirection aDeltaDirection) {
   3343  nsCOMPtr<nsIContent> targetContent = aTargetFrame->GetContent();
   3344  if (!targetContent) {
   3345    targetContent = GetFocusedElement();
   3346    if (!targetContent) {
   3347      return;
   3348    }
   3349  }
   3350 
   3351  while (targetContent->IsText()) {
   3352    targetContent = targetContent->GetFlattenedTreeParent();
   3353  }
   3354 
   3355  WidgetMouseScrollEvent event(aEvent->IsTrusted(), eLegacyMousePixelScroll,
   3356                               aEvent->mWidget);
   3357  event.mFlags.mDefaultPrevented = aState.mDefaultPrevented;
   3358  event.mFlags.mDefaultPreventedByContent = aState.mDefaultPreventedByContent;
   3359  event.mRefPoint = aEvent->mRefPoint;
   3360  event.mTimeStamp = aEvent->mTimeStamp;
   3361  event.mModifiers = aEvent->mModifiers;
   3362  event.mButtons = aEvent->mButtons;
   3363  event.mIsHorizontal = (aDeltaDirection == DELTA_DIRECTION_X);
   3364  event.mDelta = aPixelDelta;
   3365  event.mInputSource = aEvent->mInputSource;
   3366 
   3367  RefPtr<nsPresContext> presContext = aTargetFrame->PresContext();
   3368  nsEventStatus status = nsEventStatus_eIgnore;
   3369  EventDispatcher::Dispatch(targetContent, presContext, &event, nullptr,
   3370                            &status);
   3371  aState.mDefaultPrevented =
   3372      event.DefaultPrevented() || status == nsEventStatus_eConsumeNoDefault;
   3373  aState.mDefaultPreventedByContent = event.DefaultPreventedByContent();
   3374 }
   3375 
   3376 ScrollContainerFrame*
   3377 EventStateManager::ComputeScrollTargetAndMayAdjustWheelEvent(
   3378    nsIFrame* aTargetFrame, WidgetWheelEvent* aEvent,
   3379    ComputeScrollTargetOptions aOptions) {
   3380  return ComputeScrollTargetAndMayAdjustWheelEvent(
   3381      aTargetFrame, aEvent->mDeltaX, aEvent->mDeltaY, aEvent, aOptions);
   3382 }
   3383 
   3384 // Overload ComputeScrollTargetAndMayAdjustWheelEvent method to allow passing
   3385 // "test" dx and dy when looking for which scrollbarmediators to activate when
   3386 // two finger down on trackpad and before any actual motion
   3387 ScrollContainerFrame*
   3388 EventStateManager::ComputeScrollTargetAndMayAdjustWheelEvent(
   3389    nsIFrame* aTargetFrame, double aDirectionX, double aDirectionY,
   3390    WidgetWheelEvent* aEvent, ComputeScrollTargetOptions aOptions) {
   3391  bool isAutoDir = false;
   3392  bool honoursRoot = false;
   3393  if (MAY_BE_ADJUSTED_BY_AUTO_DIR & aOptions) {
   3394    // If the scroll is respected as auto-dir, aDirection* should always be
   3395    // equivalent to the event's delta vlaues(Currently, there are only one case
   3396    // where aDirection*s have different values from the widget wheel event's
   3397    // original delta values and the only case isn't auto-dir, see
   3398    // ScrollbarsForWheel::TemporarilyActivateAllPossibleScrollTargets).
   3399    MOZ_ASSERT(aDirectionX == aEvent->mDeltaX &&
   3400               aDirectionY == aEvent->mDeltaY);
   3401 
   3402    WheelDeltaAdjustmentStrategy strategy =
   3403        GetWheelDeltaAdjustmentStrategy(*aEvent);
   3404    switch (strategy) {
   3405      case WheelDeltaAdjustmentStrategy::eAutoDir:
   3406        isAutoDir = true;
   3407        honoursRoot = false;
   3408        break;
   3409      case WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour:
   3410        isAutoDir = true;
   3411        honoursRoot = true;
   3412        break;
   3413      default:
   3414        break;
   3415    }
   3416  }
   3417 
   3418  if (aOptions & PREFER_MOUSE_WHEEL_TRANSACTION) {
   3419    // If the user recently scrolled with the mousewheel, then they probably
   3420    // want to scroll the same view as before instead of the view under the
   3421    // cursor.  WheelTransaction tracks the frame currently being
   3422    // scrolled with the mousewheel. We consider the transaction ended when the
   3423    // mouse moves more than "mousewheel.transaction.ignoremovedelay"
   3424    // milliseconds after the last scroll operation, or any time the mouse moves
   3425    // out of the frame, or when more than "mousewheel.transaction.timeout"
   3426    // milliseconds have passed after the last operation, even if the mouse
   3427    // hasn't moved.
   3428    nsIFrame* lastScrollFrame = WheelTransaction::GetScrollTargetFrame();
   3429    if (lastScrollFrame) {
   3430      ScrollContainerFrame* scrollContainerFrame =
   3431          lastScrollFrame->GetScrollTargetFrame();
   3432      if (scrollContainerFrame) {
   3433        if (isAutoDir) {
   3434          ESMAutoDirWheelDeltaAdjuster adjuster(*aEvent, *lastScrollFrame,
   3435                                                honoursRoot);
   3436          // Note that calling this function will not always cause the delta to
   3437          // be adjusted, it only adjusts the delta when it should, because
   3438          // Adjust() internally calls ShouldBeAdjusted() before making
   3439          // adjustment.
   3440          adjuster.Adjust();
   3441        }
   3442        return scrollContainerFrame;
   3443      }
   3444    }
   3445  }
   3446 
   3447  // If the event doesn't cause scroll actually, we cannot find scroll target
   3448  // because we check if the event can cause scroll actually on each found
   3449  // scrollable frame.
   3450  if (!aDirectionX && !aDirectionY) {
   3451    return nullptr;
   3452  }
   3453 
   3454  bool checkIfScrollableX;
   3455  bool checkIfScrollableY;
   3456  if (isAutoDir) {
   3457    // Always check the frame's scrollability in both the two directions for an
   3458    // auto-dir scroll. That is, for an auto-dir scroll,
   3459    // PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS and
   3460    // PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS should be ignored.
   3461    checkIfScrollableX = true;
   3462    checkIfScrollableY = true;
   3463  } else {
   3464    checkIfScrollableX =
   3465        aDirectionX &&
   3466        (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_X_AXIS);
   3467    checkIfScrollableY =
   3468        aDirectionY &&
   3469        (aOptions & PREFER_ACTUAL_SCROLLABLE_TARGET_ALONG_Y_AXIS);
   3470  }
   3471 
   3472  nsIFrame* scrollFrame = !(aOptions & START_FROM_PARENT)
   3473                              ? aTargetFrame
   3474                              : GetParentFrameToScroll(aTargetFrame);
   3475  for (; scrollFrame; scrollFrame = GetParentFrameToScroll(scrollFrame)) {
   3476    // Check whether the frame wants to provide us with a scrollable view.
   3477    ScrollContainerFrame* scrollContainerFrame =
   3478        scrollFrame->GetScrollTargetFrame();
   3479    if (!scrollContainerFrame) {
   3480      nsMenuPopupFrame* menuPopupFrame = do_QueryFrame(scrollFrame);
   3481      if (menuPopupFrame) {
   3482        return nullptr;
   3483      }
   3484      continue;
   3485    }
   3486 
   3487    if (!checkIfScrollableX && !checkIfScrollableY) {
   3488      return scrollContainerFrame;
   3489    }
   3490 
   3491    // If the frame disregards the direction the user is trying to scroll, then
   3492    // it should just bubbles the scroll event up to its parental scroll frame
   3493 
   3494    Maybe<layers::ScrollDirection> disregardedDirection =
   3495        WheelHandlingUtils::GetDisregardedWheelScrollDirection(scrollFrame);
   3496    if (disregardedDirection) {
   3497      switch (disregardedDirection.ref()) {
   3498        case layers::ScrollDirection::eHorizontal:
   3499          if (checkIfScrollableX) {
   3500            continue;
   3501          }
   3502          break;
   3503        case layers::ScrollDirection::eVertical:
   3504          if (checkIfScrollableY) {
   3505            continue;
   3506          }
   3507          break;
   3508      }
   3509    }
   3510 
   3511    layers::ScrollDirections directions =
   3512        scrollContainerFrame
   3513            ->GetAvailableScrollingDirectionsForUserInputEvents();
   3514    if ((!(directions.contains(layers::ScrollDirection::eVertical)) &&
   3515         !(directions.contains(layers::ScrollDirection::eHorizontal))) ||
   3516        (checkIfScrollableY && !checkIfScrollableX &&
   3517         !(directions.contains(layers::ScrollDirection::eVertical))) ||
   3518        (checkIfScrollableX && !checkIfScrollableY &&
   3519         !(directions.contains(layers::ScrollDirection::eHorizontal)))) {
   3520      continue;
   3521    }
   3522 
   3523    // Computes whether the currently checked frame is scrollable by this wheel
   3524    // event.
   3525    bool canScroll = false;
   3526    if (isAutoDir) {
   3527      ESMAutoDirWheelDeltaAdjuster adjuster(*aEvent, *scrollFrame, honoursRoot);
   3528      if (adjuster.ShouldBeAdjusted()) {
   3529        adjuster.Adjust();
   3530        canScroll = true;
   3531      } else if (WheelHandlingUtils::CanScrollOn(scrollContainerFrame,
   3532                                                 aDirectionX, aDirectionY)) {
   3533        canScroll = true;
   3534      }
   3535    } else if (WheelHandlingUtils::CanScrollOn(scrollContainerFrame,
   3536                                               aDirectionX, aDirectionY)) {
   3537      canScroll = true;
   3538    }
   3539 
   3540    if (canScroll) {
   3541      return scrollContainerFrame;
   3542    }
   3543 
   3544    // Where we are at is the block ending in a for loop.
   3545    // The current frame has been checked to be unscrollable by this wheel
   3546    // event, continue the loop to check its parent, if any.
   3547  }
   3548 
   3549  nsIFrame* newFrame = nsLayoutUtils::GetCrossDocParentFrameInProcess(
   3550      aTargetFrame->PresShell()->GetRootFrame());
   3551  aOptions =
   3552      static_cast<ComputeScrollTargetOptions>(aOptions & ~START_FROM_PARENT);
   3553  if (!newFrame) {
   3554    return nullptr;
   3555  }
   3556  return ComputeScrollTargetAndMayAdjustWheelEvent(newFrame, aEvent, aOptions);
   3557 }
   3558 
   3559 nsSize EventStateManager::GetScrollAmount(
   3560    nsPresContext* aPresContext, WidgetWheelEvent* aEvent,
   3561    ScrollContainerFrame* aScrollContainerFrame) {
   3562  MOZ_ASSERT(aPresContext);
   3563  MOZ_ASSERT(aEvent);
   3564 
   3565  const bool isPage = aEvent->mDeltaMode == WheelEvent_Binding::DOM_DELTA_PAGE;
   3566  if (!aScrollContainerFrame) {
   3567    // If there is no scrollable frame, we should use root, see below.
   3568    aScrollContainerFrame =
   3569        aPresContext->PresShell()->GetRootScrollContainerFrame();
   3570  }
   3571 
   3572  if (aScrollContainerFrame) {
   3573    return isPage ? aScrollContainerFrame->GetPageScrollAmount()
   3574                  : aScrollContainerFrame->GetLineScrollAmount();
   3575  }
   3576 
   3577  // If there is no scrollable frame and page scrolling, use viewport size.
   3578  if (isPage) {
   3579    return aPresContext->GetVisibleArea().Size();
   3580  }
   3581 
   3582  // Otherwise use root frame's font metrics.
   3583  //
   3584  // FIXME(emilio): Should this use the root element's style frame? The root
   3585  // frame will always have the initial font. Then again it should never matter
   3586  // for content, we should always have a root scrollable frame in html
   3587  // documents.
   3588  nsIFrame* rootFrame = aPresContext->PresShell()->GetRootFrame();
   3589  if (!rootFrame) {
   3590    return nsSize(0, 0);
   3591  }
   3592  RefPtr<nsFontMetrics> fm =
   3593      nsLayoutUtils::GetInflatedFontMetricsForFrame(rootFrame);
   3594  NS_ENSURE_TRUE(fm, nsSize(0, 0));
   3595  return nsSize(fm->AveCharWidth(), fm->MaxHeight());
   3596 }
   3597 
   3598 void EventStateManager::DoScrollText(
   3599    ScrollContainerFrame* aScrollContainerFrame, WidgetWheelEvent* aEvent) {
   3600  MOZ_ASSERT(aScrollContainerFrame);
   3601  MOZ_ASSERT(aEvent);
   3602 
   3603  AutoWeakFrame scrollFrameWeak(aScrollContainerFrame);
   3604  AutoWeakFrame eventFrameWeak(mCurrentTarget);
   3605  if (!WheelTransaction::WillHandleDefaultAction(aEvent, scrollFrameWeak,
   3606                                                 eventFrameWeak)) {
   3607    return;
   3608  }
   3609 
   3610  // Default action's actual scroll amount should be computed from device
   3611  // pixels.
   3612  nsPresContext* pc = aScrollContainerFrame->PresContext();
   3613  nsSize scrollAmount = GetScrollAmount(pc, aEvent, aScrollContainerFrame);
   3614  nsIntSize scrollAmountInDevPixels(
   3615      pc->AppUnitsToDevPixels(scrollAmount.width),
   3616      pc->AppUnitsToDevPixels(scrollAmount.height));
   3617  nsIntPoint actualDevPixelScrollAmount =
   3618      DeltaAccumulator::GetInstance()->ComputeScrollAmountForDefaultAction(
   3619          aEvent, scrollAmountInDevPixels);
   3620 
   3621  // Don't scroll around the axis whose overflow style is hidden.
   3622  ScrollStyles overflowStyle = aScrollContainerFrame->GetScrollStyles();
   3623  if (overflowStyle.mHorizontal == StyleOverflow::Hidden) {
   3624    actualDevPixelScrollAmount.x = 0;
   3625  }
   3626  if (overflowStyle.mVertical == StyleOverflow::Hidden) {
   3627    actualDevPixelScrollAmount.y = 0;
   3628  }
   3629 
   3630  ScrollSnapFlags snapFlags = ScrollSnapFlags::Disabled;
   3631  mozilla::ScrollOrigin origin = mozilla::ScrollOrigin::NotSpecified;
   3632  switch (aEvent->mDeltaMode) {
   3633    case WheelEvent_Binding::DOM_DELTA_LINE:
   3634      origin = mozilla::ScrollOrigin::MouseWheel;
   3635      snapFlags = ScrollSnapFlags::IntendedDirection;
   3636      break;
   3637    case WheelEvent_Binding::DOM_DELTA_PAGE:
   3638      origin = mozilla::ScrollOrigin::Pages;
   3639      snapFlags = ScrollSnapFlags::IntendedDirection |
   3640                  ScrollSnapFlags::IntendedEndPosition;
   3641      break;
   3642    case WheelEvent_Binding::DOM_DELTA_PIXEL:
   3643      origin = mozilla::ScrollOrigin::Pixels;
   3644      break;
   3645    default:
   3646      MOZ_CRASH("Invalid deltaMode value comes");
   3647  }
   3648 
   3649  // We shouldn't scroll more one page at once except when over one page scroll
   3650  // is allowed for the event.
   3651  nsSize pageSize = aScrollContainerFrame->GetPageScrollAmount();
   3652  nsIntSize devPixelPageSize(pc->AppUnitsToDevPixels(pageSize.width),
   3653                             pc->AppUnitsToDevPixels(pageSize.height));
   3654  if (!WheelPrefs::GetInstance()->IsOverOnePageScrollAllowedX(aEvent) &&
   3655      DeprecatedAbs(actualDevPixelScrollAmount.x.value) >
   3656          devPixelPageSize.width) {
   3657    actualDevPixelScrollAmount.x = (actualDevPixelScrollAmount.x >= 0)
   3658                                       ? devPixelPageSize.width
   3659                                       : -devPixelPageSize.width;
   3660  }
   3661 
   3662  if (!WheelPrefs::GetInstance()->IsOverOnePageScrollAllowedY(aEvent) &&
   3663      DeprecatedAbs(actualDevPixelScrollAmount.y.value) >
   3664          devPixelPageSize.height) {
   3665    actualDevPixelScrollAmount.y = (actualDevPixelScrollAmount.y >= 0)
   3666                                       ? devPixelPageSize.height
   3667                                       : -devPixelPageSize.height;
   3668  }
   3669 
   3670  bool isDeltaModePixel =
   3671      (aEvent->mDeltaMode == WheelEvent_Binding::DOM_DELTA_PIXEL);
   3672 
   3673  ScrollMode mode;
   3674  switch (aEvent->mScrollType) {
   3675    case WidgetWheelEvent::SCROLL_DEFAULT:
   3676      if (isDeltaModePixel) {
   3677        mode = ScrollMode::Normal;
   3678      } else {
   3679        mode = ScrollMode::Smooth;
   3680      }
   3681      break;
   3682    case WidgetWheelEvent::SCROLL_SYNCHRONOUSLY:
   3683      mode = ScrollMode::Instant;
   3684      break;
   3685    case WidgetWheelEvent::SCROLL_ASYNCHRONOUSLY:
   3686      mode = ScrollMode::Normal;
   3687      break;
   3688    case WidgetWheelEvent::SCROLL_SMOOTHLY:
   3689      mode = ScrollMode::Smooth;
   3690      break;
   3691    default:
   3692      MOZ_CRASH("Invalid mScrollType value comes");
   3693  }
   3694 
   3695  ScrollContainerFrame::ScrollMomentum momentum =
   3696      aEvent->mIsMomentum ? ScrollContainerFrame::SYNTHESIZED_MOMENTUM_EVENT
   3697                          : ScrollContainerFrame::NOT_MOMENTUM;
   3698 
   3699  nsIntPoint overflow;
   3700  aScrollContainerFrame->ScrollBy(actualDevPixelScrollAmount,
   3701                                  ScrollUnit::DEVICE_PIXELS, mode, &overflow,
   3702                                  origin, momentum, snapFlags);
   3703 
   3704  if (!scrollFrameWeak.IsAlive()) {
   3705    // If the scroll causes changing the layout, we can think that the event
   3706    // has been completely consumed by the content.  Then, users probably don't
   3707    // want additional action.
   3708    aEvent->mOverflowDeltaX = aEvent->mOverflowDeltaY = 0;
   3709  } else if (isDeltaModePixel) {
   3710    aEvent->mOverflowDeltaX = overflow.x;
   3711    aEvent->mOverflowDeltaY = overflow.y;
   3712  } else {
   3713    aEvent->mOverflowDeltaX =
   3714        static_cast<double>(overflow.x) / scrollAmountInDevPixels.width;
   3715    aEvent->mOverflowDeltaY =
   3716        static_cast<double>(overflow.y) / scrollAmountInDevPixels.height;
   3717  }
   3718 
   3719  // If CSS overflow properties caused not to scroll, the overflowDelta* values
   3720  // should be same as delta* values since they may be used as gesture event by
   3721  // widget.  However, if there is another scrollable element in the ancestor
   3722  // along the axis, probably users don't want the operation to cause
   3723  // additional action such as moving history.  In such case, overflowDelta
   3724  // values should stay zero.
   3725  if (scrollFrameWeak.IsAlive()) {
   3726    if (aEvent->mDeltaX && overflowStyle.mHorizontal == StyleOverflow::Hidden &&
   3727        !ComputeScrollTargetAndMayAdjustWheelEvent(
   3728            aScrollContainerFrame, aEvent,
   3729            COMPUTE_SCROLLABLE_ANCESTOR_ALONG_X_AXIS_WITH_AUTO_DIR)) {
   3730      aEvent->mOverflowDeltaX = aEvent->mDeltaX;
   3731    }
   3732    if (aEvent->mDeltaY && overflowStyle.mVertical == StyleOverflow::Hidden &&
   3733        !ComputeScrollTargetAndMayAdjustWheelEvent(
   3734            aScrollContainerFrame, aEvent,
   3735            COMPUTE_SCROLLABLE_ANCESTOR_ALONG_Y_AXIS_WITH_AUTO_DIR)) {
   3736      aEvent->mOverflowDeltaY = aEvent->mDeltaY;
   3737    }
   3738  }
   3739 
   3740  NS_ASSERTION(
   3741      aEvent->mOverflowDeltaX == 0 ||
   3742          (aEvent->mOverflowDeltaX > 0) == (aEvent->mDeltaX > 0),
   3743      "The sign of mOverflowDeltaX is different from the scroll direction");
   3744  NS_ASSERTION(
   3745      aEvent->mOverflowDeltaY == 0 ||
   3746          (aEvent->mOverflowDeltaY > 0) == (aEvent->mDeltaY > 0),
   3747      "The sign of mOverflowDeltaY is different from the scroll direction");
   3748 
   3749  WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(aEvent);
   3750 }
   3751 
   3752 void EventStateManager::DecideGestureEvent(WidgetGestureNotifyEvent* aEvent,
   3753                                           nsIFrame* targetFrame) {
   3754  NS_ASSERTION(aEvent->mMessage == eGestureNotify,
   3755               "DecideGestureEvent called with a non-gesture event");
   3756 
   3757  /* Check the ancestor tree to decide if any frame is willing* to receive
   3758   * a MozPixelScroll event. If that's the case, the current touch gesture
   3759   * will be used as a pan gesture; otherwise it will be a regular
   3760   * mousedown/mousemove/click event.
   3761   *
   3762   * *willing: determine if it makes sense to pan the element using scroll
   3763   * events:
   3764   *  - For web content: if there are any visible scrollbars on the touch point
   3765   *  - For XUL: if it's an scrollable element that can currently scroll in some
   3766   *    direction.
   3767   *
   3768   * Note: we'll have to one-off various cases to ensure a good usable behavior
   3769   */
   3770  WidgetGestureNotifyEvent::PanDirection panDirection =
   3771      WidgetGestureNotifyEvent::ePanNone;
   3772  bool displayPanFeedback = false;
   3773  for (nsIFrame* current = targetFrame; current;
   3774       current = nsLayoutUtils::GetCrossDocParentFrame(current)) {
   3775    // e10s - mark remote content as pannable. This is a work around since
   3776    // we don't have access to remote frame scroll info here. Apz data may
   3777    // assist is solving this.
   3778    if (current && IsTopLevelRemoteTarget(current->GetContent())) {
   3779      panDirection = WidgetGestureNotifyEvent::ePanBoth;
   3780      // We don't know when we reach bounds, so just disable feedback for now.
   3781      displayPanFeedback = false;
   3782      break;
   3783    }
   3784 
   3785    LayoutFrameType currentFrameType = current->Type();
   3786 
   3787    // Scrollbars should always be draggable
   3788    if (currentFrameType == LayoutFrameType::Scrollbar) {
   3789      panDirection = WidgetGestureNotifyEvent::ePanNone;
   3790      break;
   3791    }
   3792 
   3793    // Special check for trees
   3794    if (nsTreeBodyFrame* treeFrame = do_QueryFrame(current)) {
   3795      if (treeFrame->GetVerticalOverflow()) {
   3796        panDirection = WidgetGestureNotifyEvent::ePanVertical;
   3797      }
   3798      break;
   3799    }
   3800 
   3801    if (ScrollContainerFrame* scrollContainerFrame = do_QueryFrame(current)) {
   3802      layers::ScrollDirections scrollbarVisibility =
   3803          scrollContainerFrame->GetScrollbarVisibility();
   3804 
   3805      // Check if we have visible scrollbars
   3806      if (scrollbarVisibility.contains(layers::ScrollDirection::eVertical)) {
   3807        panDirection = WidgetGestureNotifyEvent::ePanVertical;
   3808        displayPanFeedback = true;
   3809        break;
   3810      }
   3811 
   3812      if (scrollbarVisibility.contains(layers::ScrollDirection::eHorizontal)) {
   3813        panDirection = WidgetGestureNotifyEvent::ePanHorizontal;
   3814        displayPanFeedback = true;
   3815      }
   3816    }
   3817  }  // ancestor chain
   3818  aEvent->mDisplayPanFeedback = displayPanFeedback;
   3819  aEvent->mPanDirection = panDirection;
   3820 }
   3821 
   3822 #ifdef XP_MACOSX
   3823 static nsINode* GetCrossDocParentNode(nsINode* aChild) {
   3824  MOZ_ASSERT(aChild, "The child is null!");
   3825  MOZ_ASSERT(XRE_IsParentProcess());
   3826 
   3827  nsINode* parent = aChild->GetParentNode();
   3828  if (parent && parent->IsContent() && aChild->IsContent()) {
   3829    parent = aChild->AsContent()->GetFlattenedTreeParent();
   3830  }
   3831 
   3832  if (parent || !aChild->IsDocument()) {
   3833    return parent;
   3834  }
   3835 
   3836  return aChild->AsDocument()->GetEmbedderElement();
   3837 }
   3838 
   3839 static bool NodeAllowsClickThrough(nsINode* aNode) {
   3840  while (aNode) {
   3841    if (aNode->IsAnyOfXULElements(nsGkAtoms::browser, nsGkAtoms::tree)) {
   3842      return false;
   3843    }
   3844    if (aNode->IsAnyOfXULElements(nsGkAtoms::scrollbar, nsGkAtoms::resizer)) {
   3845      return true;
   3846    }
   3847    aNode = GetCrossDocParentNode(aNode);
   3848  }
   3849  return true;
   3850 }
   3851 #endif
   3852 
   3853 void EventStateManager::PostHandleKeyboardEvent(
   3854    WidgetKeyboardEvent* aKeyboardEvent, nsIFrame* aTargetFrame,
   3855    nsEventStatus& aStatus) {
   3856  if (aStatus == nsEventStatus_eConsumeNoDefault) {
   3857    return;
   3858  }
   3859 
   3860  RefPtr<nsPresContext> presContext = mPresContext;
   3861 
   3862  if (!aKeyboardEvent->HasBeenPostedToRemoteProcess()) {
   3863    if (aKeyboardEvent->IsWaitingReplyFromRemoteProcess()) {
   3864      RefPtr<BrowserParent> remote =
   3865          aTargetFrame ? BrowserParent::GetFrom(aTargetFrame->GetContent())
   3866                       : nullptr;
   3867      if (remote) {
   3868        // remote is null-checked above in order to let pre-existing event
   3869        // targeting code's chrome vs. content decision override in case of
   3870        // disagreement in order not to disrupt non-Fission e10s mode in case
   3871        // there are still bugs in the Fission-mode code. That is, if remote
   3872        // is nullptr, the pre-existing event targeting code has deemed this
   3873        // event to belong to chrome rather than content.
   3874        BrowserParent* preciseRemote = BrowserParent::GetFocused();
   3875        if (preciseRemote) {
   3876          remote = preciseRemote;
   3877        }
   3878        // else there was a race between layout and focus tracking
   3879      }
   3880      if (remote && !remote->IsReadyToHandleInputEvents()) {
   3881        // We need to dispatch the event to the browser element again if we were
   3882        // waiting for the key reply but the event wasn't sent to the content
   3883        // process due to the remote browser wasn't ready.
   3884        WidgetKeyboardEvent keyEvent(*aKeyboardEvent);
   3885        aKeyboardEvent->MarkAsHandledInRemoteProcess();
   3886        RefPtr<Element> ownerElement = remote->GetOwnerElement();
   3887        EventDispatcher::Dispatch(ownerElement, presContext, &keyEvent);
   3888        if (keyEvent.DefaultPrevented()) {
   3889          aKeyboardEvent->PreventDefault(!keyEvent.DefaultPreventedByContent());
   3890          aStatus = nsEventStatus_eConsumeNoDefault;
   3891          return;
   3892        }
   3893      }
   3894    }
   3895    // The widget expects a reply for every keyboard event. If the event wasn't
   3896    // dispatched to a content process (non-e10s or no content process
   3897    // running), we need to short-circuit here. Otherwise, we need to wait for
   3898    // the content process to handle the event.
   3899    if (aKeyboardEvent->mWidget) {
   3900      aKeyboardEvent->mWidget->PostHandleKeyEvent(aKeyboardEvent);
   3901    }
   3902    if (aKeyboardEvent->DefaultPrevented()) {
   3903      aStatus = nsEventStatus_eConsumeNoDefault;
   3904      return;
   3905    }
   3906  }
   3907 
   3908  // XXX Currently, our automated tests don't support mKeyNameIndex.
   3909  //     Therefore, we still need to handle this with keyCode.
   3910  switch (aKeyboardEvent->mKeyCode) {
   3911    case NS_VK_TAB:
   3912    case NS_VK_F6:
   3913      // This is to prevent keyboard scrolling while alt modifier in use.
   3914      if (!aKeyboardEvent->IsAlt()) {
   3915        aStatus = nsEventStatus_eConsumeNoDefault;
   3916 
   3917        // Handling the tab event after it was sent to content is bad,
   3918        // because to the FocusManager the remote-browser looks like one
   3919        // element, so we would just move the focus to the next element
   3920        // in chrome, instead of handling it in content.
   3921        if (aKeyboardEvent->HasBeenPostedToRemoteProcess()) {
   3922          break;
   3923        }
   3924 
   3925        EnsureDocument(presContext);
   3926        nsFocusManager* fm = nsFocusManager::GetFocusManager();
   3927        if (fm && mDocument) {
   3928          // Shift focus forward or back depending on shift key
   3929          bool isDocMove = aKeyboardEvent->IsControl() ||
   3930                           aKeyboardEvent->mKeyCode == NS_VK_F6;
   3931          uint32_t dir =
   3932              aKeyboardEvent->IsShift()
   3933                  ? (isDocMove ? static_cast<uint32_t>(
   3934                                     nsIFocusManager::MOVEFOCUS_BACKWARDDOC)
   3935                               : static_cast<uint32_t>(
   3936                                     nsIFocusManager::MOVEFOCUS_BACKWARD))
   3937                  : (isDocMove ? static_cast<uint32_t>(
   3938                                     nsIFocusManager::MOVEFOCUS_FORWARDDOC)
   3939                               : static_cast<uint32_t>(
   3940                                     nsIFocusManager::MOVEFOCUS_FORWARD));
   3941          RefPtr<Element> result;
   3942          fm->MoveFocus(mDocument->GetWindow(), nullptr, dir,
   3943                        nsIFocusManager::FLAG_BYKEY, getter_AddRefs(result));
   3944        }
   3945      }
   3946      return;
   3947    case 0:
   3948      // We handle keys with no specific keycode value below.
   3949      break;
   3950    default:
   3951      return;
   3952  }
   3953 
   3954  switch (aKeyboardEvent->mKeyNameIndex) {
   3955    case KEY_NAME_INDEX_ZoomIn:
   3956    case KEY_NAME_INDEX_ZoomOut:
   3957      ChangeZoom(aKeyboardEvent->mKeyNameIndex == KEY_NAME_INDEX_ZoomIn);
   3958      aStatus = nsEventStatus_eConsumeNoDefault;
   3959      break;
   3960    default:
   3961      break;
   3962  }
   3963 }
   3964 
   3965 static bool NeedsActiveContentChange(const WidgetMouseEvent* aMouseEvent) {
   3966  // If the mouse event is a synthesized mouse event due to a touch, do
   3967  // not set/clear the activation state. Element activation is handled by APZ.
   3968  return !aMouseEvent ||
   3969         aMouseEvent->mInputSource != MouseEvent_Binding::MOZ_SOURCE_TOUCH;
   3970 }
   3971 
   3972 nsresult EventStateManager::PostHandleEvent(nsPresContext* aPresContext,
   3973                                            WidgetEvent* aEvent,
   3974                                            nsIFrame* aTargetFrame,
   3975                                            nsEventStatus* aStatus,
   3976                                            nsIContent* aOverrideClickTarget) {
   3977  AUTO_PROFILER_LABEL("EventStateManager::PostHandleEvent", DOM);
   3978  NS_ENSURE_ARG(aPresContext);
   3979  NS_ENSURE_ARG_POINTER(aStatus);
   3980 
   3981  mCurrentTarget = aTargetFrame;
   3982  mCurrentTargetContent = nullptr;
   3983 
   3984  HandleCrossProcessEvent(aEvent, aStatus);
   3985  // NOTE: the above call may have destroyed aTargetFrame, please use
   3986  // mCurrentTarget henceforth.  This is to avoid using it accidentally:
   3987  aTargetFrame = nullptr;
   3988 
   3989  // Most of the events we handle below require a frame.
   3990  // Add special cases here.
   3991  if (!mCurrentTarget && aEvent->mMessage != eMouseUp &&
   3992      aEvent->mMessage != eMouseDown && aEvent->mMessage != eDragEnter &&
   3993      aEvent->mMessage != eDragOver && aEvent->mMessage != ePointerUp &&
   3994      aEvent->mMessage != ePointerCancel) {
   3995    return NS_OK;
   3996  }
   3997 
   3998  // Keep the prescontext alive, we might need it after event dispatch
   3999  RefPtr<nsPresContext> presContext = aPresContext;
   4000  nsresult ret = NS_OK;
   4001 
   4002  switch (aEvent->mMessage) {
   4003    case eMouseDown: {
   4004      WidgetMouseEvent* mouseEvent = aEvent->AsMouseEvent();
   4005      if (mouseEvent->mButton == MouseButton::ePrimary &&
   4006          !sNormalLMouseEventInProcess) {
   4007        // We got a mouseup event while a mousedown event was being processed.
   4008        // Make sure that the capturing content is cleared.
   4009        PresShell::ReleaseCapturingContent();
   4010        break;
   4011      }
   4012 
   4013      // For remote content, capture the event in the parent process at the
   4014      // <xul:browser remote> element. This will ensure that subsequent
   4015      // mousemove/mouseup events will continue to be dispatched to this element
   4016      // and therefore forwarded to the child.
   4017      if (aEvent->HasBeenPostedToRemoteProcess() &&
   4018          !PresShell::GetCapturingContent()) {
   4019        if (nsIContent* content =
   4020                mCurrentTarget ? mCurrentTarget->GetContent() : nullptr) {
   4021          PresShell::SetCapturingContent(content, CaptureFlags::None, aEvent);
   4022        } else {
   4023          PresShell::ReleaseCapturingContent();
   4024        }
   4025      }
   4026 
   4027      // If MouseEvent::PreventClickEvent() was called by chrome script,
   4028      // we need to forget the clicking content and click count for the
   4029      // following eMouseUp event.
   4030      if (mouseEvent->mClickEventPrevented) {
   4031        switch (mouseEvent->mButton) {
   4032          case MouseButton::ePrimary:
   4033          case MouseButton::eSecondary:
   4034          case MouseButton::eMiddle: {
   4035            LastMouseDownInfo& mouseDownInfo =
   4036                GetLastMouseDownInfo(mouseEvent->mButton);
   4037            mouseDownInfo.mLastMouseDownContent = nullptr;
   4038            mouseDownInfo.mClickCount = 0;
   4039            mouseDownInfo.mLastMouseDownInputControlType = Nothing();
   4040            break;
   4041          }
   4042 
   4043          default:
   4044            break;
   4045        }
   4046      }
   4047 
   4048      nsCOMPtr<nsIContent> activeContent;
   4049      // When content calls PreventDefault on pointerdown, we also call
   4050      // PreventDefault on the subsequent mouse events to suppress default
   4051      // behaviors. Normally, aStatus should be nsEventStatus_eConsumeNoDefault
   4052      // when the event is DefaultPrevented but it's reset to
   4053      // nsEventStatus_eIgnore in EventStateManager::PreHandleEvent. So we also
   4054      // check if the event is DefaultPrevented.
   4055      if (nsEventStatus_eConsumeNoDefault != *aStatus &&
   4056          !aEvent->DefaultPrevented()) {
   4057        nsCOMPtr<nsIContent> newFocus;
   4058        bool suppressBlur = false;
   4059        if (mCurrentTarget) {
   4060          newFocus = mCurrentTarget->GetContentForEvent(aEvent);
   4061          activeContent = mCurrentTarget->GetContent();
   4062 
   4063          // In some cases, we do not want to even blur the current focused
   4064          // element. Those cases are:
   4065          // 1. -moz-user-focus CSS property is set to 'ignore';
   4066          // 2. XUL control element has the disabled property set to 'true'.
   4067          //
   4068          // We can't use nsIFrame::IsFocusable() because we want to blur when
   4069          // we click on a visibility: none element.
   4070          // We can't use nsIContent::IsFocusable() because we want to blur when
   4071          // we click on a non-focusable element like a <div>.
   4072          // We have to use |aEvent->mTarget| to not make sure we do not check
   4073          // an anonymous node of the targeted element.
   4074          suppressBlur =
   4075              mCurrentTarget->StyleUI()->UserFocus() == StyleUserFocus::Ignore;
   4076 
   4077          if (!suppressBlur) {
   4078            if (Element* element =
   4079                    Element::FromEventTargetOrNull(aEvent->mTarget)) {
   4080              if (nsCOMPtr<nsIDOMXULControlElement> xulControl =
   4081                      element->AsXULControl()) {
   4082                bool disabled = false;
   4083                xulControl->GetDisabled(&disabled);
   4084                suppressBlur = disabled;
   4085              }
   4086            }
   4087          }
   4088        }
   4089 
   4090        // When a root content which isn't editable but has an editable HTML
   4091        // <body> element is clicked, we should redirect the focus to the
   4092        // the <body> element.  E.g., when an user click bottom of the editor
   4093        // where is outside of the <body> element, the <body> should be focused
   4094        // and the user can edit immediately after that.
   4095        //
   4096        // NOTE: The newFocus isn't editable that also means it's not in
   4097        // designMode.  In designMode, all contents are not focusable.
   4098        if (newFocus && !newFocus->IsEditable()) {
   4099          Document* doc = newFocus->GetComposedDoc();
   4100          if (doc && newFocus == doc->GetRootElement()) {
   4101            nsIContent* bodyContent =
   4102                nsLayoutUtils::GetEditableRootContentByContentEditable(doc);
   4103            if (bodyContent && bodyContent->GetPrimaryFrame()) {
   4104              newFocus = bodyContent;
   4105            }
   4106          }
   4107        }
   4108 
   4109        // When the mouse is pressed, the default action is to focus the
   4110        // target. Look for the nearest enclosing focusable frame.
   4111        //
   4112        // TODO: Probably this should be moved to Element::PostHandleEvent.
   4113        for (; newFocus; newFocus = newFocus->GetFlattenedTreeParent()) {
   4114          if (!newFocus->IsElement()) {
   4115            continue;
   4116          }
   4117 
   4118          nsIFrame* frame = newFocus->GetPrimaryFrame();
   4119          if (!frame) {
   4120            continue;
   4121          }
   4122 
   4123          // If the mousedown happened inside a popup, don't try to set focus on
   4124          // one of its containing elements
   4125          if (frame->IsMenuPopupFrame()) {
   4126            newFocus = nullptr;
   4127            break;
   4128          }
   4129 
   4130          auto flags = IsFocusableFlags::WithMouse;
   4131          if (frame->IsFocusable(flags)) {
   4132            break;
   4133          }
   4134 
   4135          if (ShadowRoot* root = newFocus->GetShadowRoot()) {
   4136            if (root->DelegatesFocus()) {
   4137              if (Element* firstFocusable = root->GetFocusDelegate(flags)) {
   4138                newFocus = firstFocusable;
   4139                break;
   4140              }
   4141            }
   4142          }
   4143        }
   4144 
   4145        MOZ_ASSERT_IF(newFocus, newFocus->IsElement());
   4146 
   4147        if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   4148          // if something was found to focus, focus it. Otherwise, if the
   4149          // element that was clicked doesn't have -moz-user-focus: ignore,
   4150          // clear the existing focus. For -moz-user-focus: ignore, the focus
   4151          // is just left as is.
   4152          // Another effect of mouse clicking, handled in Selection, is that
   4153          // it should update the caret position to where the mouse was
   4154          // clicked. Because the focus is cleared when clicking on a
   4155          // non-focusable node, the next press of the tab key will cause
   4156          // focus to be shifted from the caret position instead of the root.
   4157          if (newFocus) {
   4158            // use the mouse flag and the noscroll flag so that the content
   4159            // doesn't unexpectedly scroll when clicking an element that is
   4160            // only half visible
   4161            uint32_t flags =
   4162                nsIFocusManager::FLAG_BYMOUSE | nsIFocusManager::FLAG_NOSCROLL;
   4163            // If this was a touch-generated event, pass that information:
   4164            if (mouseEvent->mInputSource ==
   4165                MouseEvent_Binding::MOZ_SOURCE_TOUCH) {
   4166              flags |= nsIFocusManager::FLAG_BYTOUCH;
   4167            }
   4168            fm->SetFocus(MOZ_KnownLive(newFocus->AsElement()), flags);
   4169          } else if (!suppressBlur) {
   4170            // clear the focus within the frame and then set it as the
   4171            // focused frame
   4172            EnsureDocument(mPresContext);
   4173            if (mDocument) {
   4174              nsCOMPtr<nsPIDOMWindowOuter> outerWindow = mDocument->GetWindow();
   4175 #ifdef XP_MACOSX
   4176              if (!activeContent || !activeContent->IsXULElement())
   4177 #endif
   4178                fm->ClearFocus(outerWindow);
   4179              // Prevent switch frame if we're already not in the foreground tab
   4180              // and we're in a content process.
   4181              // TODO: If we were inactive frame in this tab, and now in
   4182              //       background tab, we shouldn't make the tab foreground, but
   4183              //       we should set focus to clicked document in the background
   4184              //       tab.  However, nsFocusManager does not have proper method
   4185              //       for doing this.  Therefore, we should skip setting focus
   4186              //       to clicked document for now.
   4187              if (XRE_IsParentProcess() || IsInActiveTab(mDocument)) {
   4188                fm->SetFocusedWindow(outerWindow);
   4189              }
   4190            }
   4191          }
   4192        }
   4193 
   4194        // The rest is left button-specific.
   4195        if (mouseEvent->mButton != MouseButton::ePrimary) {
   4196          break;
   4197        }
   4198 
   4199        // The nearest enclosing element goes into the :active state.  If we're
   4200        // not an element (so we're text or something) we need to obtain
   4201        // our parent element and put it into :active instead.
   4202        if (activeContent && !activeContent->IsElement()) {
   4203          if (nsIContent* par = activeContent->GetFlattenedTreeParent()) {
   4204            activeContent = par;
   4205          }
   4206        }
   4207      } else {
   4208        // if we're here, the event handler returned false, so stop
   4209        // any of our own processing of a drag. Workaround for bug 43258.
   4210        StopTrackingDragGesture(true);
   4211      }
   4212      // XXX Why do we always set this is active?  Active window may be changed
   4213      //     by a mousedown event listener.
   4214      if (NeedsActiveContentChange(mouseEvent)) {
   4215        SetActiveManager(this, activeContent);
   4216      }
   4217    } break;
   4218    case ePointerCancel:
   4219    case ePointerUp: {
   4220      WidgetPointerEvent* pointerEvent = aEvent->AsPointerEvent();
   4221      MOZ_ASSERT(pointerEvent);
   4222      // Implicitly releasing capture for given pointer. ePointerLostCapture
   4223      // should be send after ePointerUp or ePointerCancel.
   4224      PointerEventHandler::ImplicitlyReleasePointerCapture(pointerEvent);
   4225      PointerEventHandler::UpdatePointerActiveState(pointerEvent);
   4226 
   4227      if (
   4228          // After pointercancel, pointer becomes invalid so we can remove
   4229          // relevant helper from table.
   4230          pointerEvent->mMessage == ePointerCancel ||
   4231          // pointerup for non-hoverable pointer needs to dispatch pointerout
   4232          // and pointerleave events because the pointer is valid only while the
   4233          // pointer is "down".
   4234          !pointerEvent->InputSourceSupportsHover()) {
   4235        GenerateMouseEnterExit(pointerEvent);
   4236        mPointersEnterLeaveHelper.Remove(pointerEvent->pointerId);
   4237      }
   4238 
   4239      break;
   4240    }
   4241    case eMouseUp: {
   4242      // We can unconditionally stop capturing because
   4243      // we should never be capturing when the mouse button is up
   4244      PresShell::ReleaseCapturingContent();
   4245 
   4246      WidgetMouseEvent* mouseUpEvent = aEvent->AsMouseEvent();
   4247      if (NeedsActiveContentChange(mouseUpEvent)) {
   4248        ClearGlobalActiveContent(this);
   4249      }
   4250      if (mouseUpEvent && EventCausesClickEvents(*mouseUpEvent)) {
   4251        // Make sure to dispatch the click even if there is no frame for
   4252        // the current target element. This is required for Web compatibility.
   4253        RefPtr<EventStateManager> esm =
   4254            ESMFromContentOrThis(aOverrideClickTarget);
   4255        ret =
   4256            esm->PostHandleMouseUp(mouseUpEvent, aStatus, aOverrideClickTarget);
   4257      }
   4258 
   4259      // After dispatching click events for this eMouseUp, nobody needs to refer
   4260      // to the preceding ePointerUp event target anymore because it was
   4261      // required by the click event dispatcher to consider the target.
   4262      // Therefore, PointerEventHandler should forget the target now.
   4263      PointerEventHandler::ReleasePointerCapturingElementAtLastPointerUp();
   4264 
   4265      if (PresShell* presShell = presContext->GetPresShell()) {
   4266        RefPtr<nsFrameSelection> frameSelection = presShell->FrameSelection();
   4267        frameSelection->SetDragState(false);
   4268      }
   4269    } break;
   4270    case eWheelOperationEnd: {
   4271      MOZ_ASSERT(aEvent->IsTrusted());
   4272      ScrollbarsForWheel::MayInactivate();
   4273      WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
   4274      ScrollContainerFrame* scrollTarget =
   4275          ComputeScrollTargetAndMayAdjustWheelEvent(
   4276              mCurrentTarget, wheelEvent,
   4277              COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR);
   4278      // If the wheel event was handled by APZ, APZ will perform the scroll
   4279      // snap.
   4280      if (scrollTarget && !WheelTransaction::HandledByApz()) {
   4281        scrollTarget->ScrollSnap();
   4282      }
   4283    } break;
   4284    case eWheel:
   4285    case eWheelOperationStart: {
   4286      MOZ_ASSERT(aEvent->IsTrusted());
   4287 
   4288      if (*aStatus == nsEventStatus_eConsumeNoDefault) {
   4289        ScrollbarsForWheel::Inactivate();
   4290        break;
   4291      }
   4292 
   4293      WidgetWheelEvent* wheelEvent = aEvent->AsWheelEvent();
   4294      MOZ_ASSERT(wheelEvent);
   4295 
   4296      // When APZ is enabled, the actual scroll animation might be handled by
   4297      // the compositor.
   4298      WheelPrefs::Action action =
   4299          wheelEvent->mFlags.mHandledByAPZ
   4300              ? WheelPrefs::ACTION_NONE
   4301              : WheelPrefs::GetInstance()->ComputeActionFor(wheelEvent);
   4302 
   4303      WheelDeltaAdjustmentStrategy strategy =
   4304          GetWheelDeltaAdjustmentStrategy(*wheelEvent);
   4305      // Adjust the delta values of the wheel event if the current default
   4306      // action is to horizontalize scrolling. I.e., deltaY values are set to
   4307      // deltaX and deltaY and deltaZ values are set to 0.
   4308      // If horizontalized, the delta values will be restored and its overflow
   4309      // deltaX will become 0 when the WheelDeltaHorizontalizer instance is
   4310      // being destroyed.
   4311      WheelDeltaHorizontalizer horizontalizer(*wheelEvent);
   4312      if (WheelDeltaAdjustmentStrategy::eHorizontalize == strategy) {
   4313        horizontalizer.Horizontalize();
   4314      }
   4315 
   4316      // Since ComputeScrollTargetAndMayAdjustWheelEvent() may adjust the delta
   4317      // if the event is auto-dir. So we use |ESMAutoDirWheelDeltaRestorer|
   4318      // here.
   4319      // An instance of |ESMAutoDirWheelDeltaRestorer| is used to monitor
   4320      // auto-dir adjustment which may happen during its lifetime. If the delta
   4321      // values is adjusted during its lifetime, the instance will restore the
   4322      // adjusted delta when it's being destrcuted.
   4323      ESMAutoDirWheelDeltaRestorer restorer(*wheelEvent);
   4324      ScrollContainerFrame* scrollTarget =
   4325          ComputeScrollTargetAndMayAdjustWheelEvent(
   4326              mCurrentTarget, wheelEvent,
   4327              COMPUTE_DEFAULT_ACTION_TARGET_WITH_AUTO_DIR);
   4328 
   4329      switch (action) {
   4330        case WheelPrefs::ACTION_SCROLL:
   4331        case WheelPrefs::ACTION_HORIZONTALIZED_SCROLL: {
   4332          // For scrolling of default action, we should honor the mouse wheel
   4333          // transaction.
   4334 
   4335          ScrollbarsForWheel::PrepareToScrollText(this, mCurrentTarget,
   4336                                                  wheelEvent);
   4337 
   4338          if (aEvent->mMessage != eWheel ||
   4339              (!wheelEvent->mDeltaX && !wheelEvent->mDeltaY)) {
   4340            break;
   4341          }
   4342 
   4343          ScrollbarsForWheel::SetActiveScrollTarget(scrollTarget);
   4344 
   4345          ScrollContainerFrame* rootScrollContainerFrame =
   4346              !mCurrentTarget
   4347                  ? nullptr
   4348                  : mCurrentTarget->PresShell()->GetRootScrollContainerFrame();
   4349          if (!scrollTarget || scrollTarget == rootScrollContainerFrame) {
   4350            wheelEvent->mViewPortIsOverscrolled = true;
   4351          }
   4352          wheelEvent->mOverflowDeltaX = wheelEvent->mDeltaX;
   4353          wheelEvent->mOverflowDeltaY = wheelEvent->mDeltaY;
   4354          WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(
   4355              wheelEvent);
   4356          if (scrollTarget) {
   4357            DoScrollText(scrollTarget, wheelEvent);
   4358          } else {
   4359            WheelTransaction::EndTransaction();
   4360            ScrollbarsForWheel::Inactivate();
   4361          }
   4362          break;
   4363        }
   4364        case WheelPrefs::ACTION_HISTORY: {
   4365          // If this event doesn't cause eLegacyMouseLineOrPageScroll event or
   4366          // the direction is oblique, don't perform history back/forward.
   4367          int32_t intDelta = wheelEvent->GetPreferredIntDelta();
   4368          if (!intDelta) {
   4369            break;
   4370          }
   4371          DoScrollHistory(intDelta);
   4372          break;
   4373        }
   4374        case WheelPrefs::ACTION_ZOOM: {
   4375          // If this event doesn't cause eLegacyMouseLineOrPageScroll event or
   4376          // the direction is oblique, don't perform zoom in/out.
   4377          int32_t intDelta = wheelEvent->GetPreferredIntDelta();
   4378          if (!intDelta) {
   4379            break;
   4380          }
   4381          DoScrollZoom(mCurrentTarget, intDelta);
   4382          break;
   4383        }
   4384        case WheelPrefs::ACTION_NONE:
   4385        default:
   4386          bool allDeltaOverflown = false;
   4387          if (StaticPrefs::dom_event_wheel_event_groups_enabled() &&
   4388              (wheelEvent->mDeltaX != 0.0 || wheelEvent->mDeltaY != 0.0)) {
   4389            if (scrollTarget) {
   4390              WheelTransaction::WillHandleDefaultAction(
   4391                  wheelEvent, scrollTarget, mCurrentTarget);
   4392            } else {
   4393              WheelTransaction::EndTransaction();
   4394            }
   4395          }
   4396          if (wheelEvent->mFlags.mHandledByAPZ) {
   4397            if (wheelEvent->mCanTriggerSwipe) {
   4398              // For events that can trigger swipes, APZ needs to know whether
   4399              // scrolling is possible in the requested direction. It does this
   4400              // by looking at the scroll overflow values on mCanTriggerSwipe
   4401              // events after they have been processed. When determining if
   4402              // a swipe should occur, we should not prefer the current wheel
   4403              // transaction.
   4404              nsIFrame* lastScrollFrame =
   4405                  WheelTransaction::GetScrollTargetFrame();
   4406              bool wheelTransactionHandlesInput = false;
   4407              if (lastScrollFrame) {
   4408                ScrollContainerFrame* scrollContainerFrame =
   4409                    lastScrollFrame->GetScrollTargetFrame();
   4410                if (scrollContainerFrame->IsRootScrollFrameOfDocument()) {
   4411                  // If the current wheel transaction target is the root scroll
   4412                  // frame and is not scrollable on the x-axis, all delta is
   4413                  // overflown and swipe-to-nav may occur.
   4414                  wheelTransactionHandlesInput = true;
   4415                  allDeltaOverflown = !WheelHandlingUtils::CanScrollOn(
   4416                      scrollContainerFrame, wheelEvent->mDeltaX, 0.0);
   4417                } else if (WheelHandlingUtils::CanScrollOn(
   4418                               scrollContainerFrame, wheelEvent->mDeltaX,
   4419                               wheelEvent->mDeltaY)) {
   4420                  // If the current wheel transaction target is not the root
   4421                  // scroll frame, ensure that swipe to nav does not occur if
   4422                  // the scroll frame is scrollable on the x or y axis. If the
   4423                  // scroll frame cannot scroll, all delta _may_ be overflown.
   4424                  wheelTransactionHandlesInput = true;
   4425                  allDeltaOverflown = false;
   4426                }
   4427              }
   4428              if (!wheelTransactionHandlesInput) {
   4429                allDeltaOverflown = !ComputeScrollTarget(
   4430                    mCurrentTarget, wheelEvent,
   4431                    COMPUTE_DEFAULT_ACTION_TARGET_WITHOUT_WHEEL_TRANSACTION);
   4432              }
   4433            }
   4434          } else {
   4435            // The event was processed neither by APZ nor by us, so all of the
   4436            // delta values must be overflown delta values.
   4437            allDeltaOverflown = true;
   4438          }
   4439 
   4440          if (!allDeltaOverflown) {
   4441            break;
   4442          }
   4443          wheelEvent->mOverflowDeltaX = wheelEvent->mDeltaX;
   4444          wheelEvent->mOverflowDeltaY = wheelEvent->mDeltaY;
   4445          WheelPrefs::GetInstance()->CancelApplyingUserPrefsFromOverflowDelta(
   4446              wheelEvent);
   4447          wheelEvent->mViewPortIsOverscrolled = true;
   4448          break;
   4449      }
   4450      *aStatus = nsEventStatus_eConsumeNoDefault;
   4451    } break;
   4452 
   4453    case eGestureNotify: {
   4454      if (nsEventStatus_eConsumeNoDefault != *aStatus) {
   4455        DecideGestureEvent(aEvent->AsGestureNotifyEvent(), mCurrentTarget);
   4456      }
   4457    } break;
   4458 
   4459    case eDragEnter:
   4460    case eDragOver: {
   4461      NS_ASSERTION(aEvent->mClass == eDragEventClass, "Expected a drag event");
   4462 
   4463      // Check if the drag is occurring inside a scrollable area. If so, scroll
   4464      // the area when the mouse is near the edges.
   4465      if (mCurrentTarget && aEvent->mMessage == eDragOver) {
   4466        nsIFrame* checkFrame = mCurrentTarget;
   4467        while (checkFrame) {
   4468          ScrollContainerFrame* scrollFrame = do_QueryFrame(checkFrame);
   4469          // Break out so only the innermost scrollframe is scrolled.
   4470          if (scrollFrame && scrollFrame->DragScroll(aEvent)) {
   4471            break;
   4472          }
   4473          checkFrame = checkFrame->GetParent();
   4474        }
   4475      }
   4476 
   4477      nsCOMPtr<nsIDragSession> dragSession =
   4478          nsContentUtils::GetDragSession(mPresContext);
   4479      if (!dragSession) break;
   4480 
   4481      // Reset the flag.
   4482      dragSession->SetOnlyChromeDrop(false);
   4483      if (mPresContext) {
   4484        EnsureDocument(mPresContext);
   4485      }
   4486      bool isChromeDoc = nsContentUtils::IsChromeDoc(mDocument);
   4487 
   4488      // the initial dataTransfer is the one from the dragstart event that
   4489      // was set on the dragSession when the drag began.
   4490      RefPtr<DataTransfer> dataTransfer;
   4491      RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
   4492 
   4493      WidgetDragEvent* dragEvent = aEvent->AsDragEvent();
   4494 
   4495      // collect any changes to moz cursor settings stored in the event's
   4496      // data transfer.
   4497      UpdateDragDataTransfer(dragEvent);
   4498 
   4499      // cancelling a dragenter or dragover event means that a drop should be
   4500      // allowed, so update the dropEffect and the canDrop state to indicate
   4501      // that a drag is allowed. If the event isn't cancelled, a drop won't be
   4502      // allowed. Essentially, to allow a drop somewhere, specify the effects
   4503      // using the effectAllowed and dropEffect properties in a dragenter or
   4504      // dragover event and cancel the event. To not allow a drop somewhere,
   4505      // don't cancel the event or set the effectAllowed or dropEffect to
   4506      // "none". This way, if the event is just ignored, no drop will be
   4507      // allowed.
   4508      uint32_t dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
   4509      uint32_t action = nsIDragService::DRAGDROP_ACTION_NONE;
   4510      if (nsEventStatus_eConsumeNoDefault == *aStatus) {
   4511        // If the event has initialized its mDataTransfer, use it.
   4512        // Or the event has not been initialized its mDataTransfer, but
   4513        // it's set before dispatch because of synthesized, but without
   4514        // testing session (e.g., emulating drag from another app), use it
   4515        // coming from outside.
   4516        // XXX Perhaps, for the latter case, we need new API because we don't
   4517        //     have a chance to initialize allowed effects of the session.
   4518        if (dragEvent->mDataTransfer) {
   4519          // get the dataTransfer and the dropEffect that was set on it
   4520          dataTransfer = dragEvent->mDataTransfer;
   4521          dropEffect = dataTransfer->DropEffectInt();
   4522        } else {
   4523          // if dragEvent->mDataTransfer is null, it means that no attempt was
   4524          // made to access the dataTransfer during the event, yet the event
   4525          // was cancelled. Instead, use the initial data transfer available
   4526          // from the drag session. The drop effect would not have been
   4527          // initialized (which is done in DragEvent::GetDataTransfer),
   4528          // so set it from the drag action. We'll still want to filter it
   4529          // based on the effectAllowed below.
   4530          dataTransfer = initialDataTransfer;
   4531 
   4532          dragSession->GetDragAction(&action);
   4533 
   4534          // filter the drop effect based on the action. Use UNINITIALIZED as
   4535          // any effect is allowed.
   4536          dropEffect = nsContentUtils::FilterDropEffect(
   4537              action, nsIDragService::DRAGDROP_ACTION_UNINITIALIZED);
   4538        }
   4539 
   4540        // At this point, if the dataTransfer is null, it means that the
   4541        // drag was originally started by directly calling the drag service.
   4542        // Just assume that all effects are allowed.
   4543        uint32_t effectAllowed = nsIDragService::DRAGDROP_ACTION_UNINITIALIZED;
   4544        if (dataTransfer) {
   4545          effectAllowed = dataTransfer->EffectAllowedInt();
   4546        }
   4547 
   4548        // set the drag action based on the drop effect and effect allowed.
   4549        // The drop effect field on the drag transfer object specifies the
   4550        // desired current drop effect. However, it cannot be used if the
   4551        // effectAllowed state doesn't include that type of action. If the
   4552        // dropEffect is "none", then the action will be 'none' so a drop will
   4553        // not be allowed.
   4554        if (effectAllowed == nsIDragService::DRAGDROP_ACTION_UNINITIALIZED ||
   4555            dropEffect & effectAllowed)
   4556          action = dropEffect;
   4557 
   4558        if (action == nsIDragService::DRAGDROP_ACTION_NONE)
   4559          dropEffect = nsIDragService::DRAGDROP_ACTION_NONE;
   4560 
   4561        // inform the drag session that a drop is allowed on this node.
   4562        dragSession->SetDragAction(action);
   4563        dragSession->SetCanDrop(action != nsIDragService::DRAGDROP_ACTION_NONE);
   4564 
   4565        // For now, do this only for dragover.
   4566        // XXXsmaug dragenter needs some more work.
   4567        if (aEvent->mMessage == eDragOver && !isChromeDoc) {
   4568          // Someone has called preventDefault(), check whether is was on
   4569          // content or chrome.
   4570          dragSession->SetOnlyChromeDrop(
   4571              !dragEvent->mDefaultPreventedOnContent);
   4572        }
   4573      } else if (aEvent->mMessage == eDragOver && !isChromeDoc) {
   4574        // No one called preventDefault(), so handle drop only in chrome.
   4575        dragSession->SetOnlyChromeDrop(true);
   4576      }
   4577      if (auto* bc = BrowserChild::GetFrom(presContext->GetDocShell())) {
   4578        bc->SendUpdateDropEffect(action, dropEffect);
   4579      }
   4580      if (aEvent->HasBeenPostedToRemoteProcess()) {
   4581        dragSession->SetCanDrop(true);
   4582      } else if (initialDataTransfer) {
   4583        // Now set the drop effect in the initial dataTransfer. This ensures
   4584        // that we can get the desired drop effect in the drop event. For events
   4585        // dispatched to content, the content process will take care of setting
   4586        // this.
   4587        initialDataTransfer->SetDropEffectInt(dropEffect);
   4588      }
   4589    } break;
   4590 
   4591    case eDrop: {
   4592      if (aEvent->mFlags.mIsSynthesizedForTests) {
   4593        nsCOMPtr<nsIDragService> dragService =
   4594            do_GetService("@mozilla.org/widget/dragservice;1");
   4595        nsCOMPtr<nsIDragSession> dragSession =
   4596            nsContentUtils::GetDragSession(mPresContext);
   4597        if (dragSession && dragService &&
   4598            !dragService->GetNeverAllowSessionIsSynthesizedForTests()) {
   4599          MOZ_ASSERT(dragSession->IsSynthesizedForTests());
   4600          RefPtr<WindowContext> sourceWC;
   4601          DebugOnly<nsresult> rvIgnored =
   4602              dragSession->GetSourceWindowContext(getter_AddRefs(sourceWC));
   4603          NS_WARNING_ASSERTION(
   4604              NS_SUCCEEDED(rvIgnored),
   4605              "nsIDragSession::GetSourceDocument() failed, but ignored");
   4606          // If the drag source hasn't been initialized, i.e., dragstart was
   4607          // consumed by the test, the test needs to dispatch "dragend" event
   4608          // instead of the drag session.  Therefore, it does not make sense
   4609          // to set drag end point in such case (you hit assersion if you do
   4610          // it).
   4611          if (sourceWC) {
   4612            const CSSIntPoint dropPointInScreen = RoundedToInt(
   4613                Event::GetScreenCoords(aPresContext, aEvent, aEvent->mRefPoint)
   4614                    .extract());
   4615            dragSession->SetDragEndPointForTests(dropPointInScreen.x,
   4616                                                 dropPointInScreen.y);
   4617          }
   4618        }
   4619      }
   4620      sLastDragOverFrame = nullptr;
   4621      ClearGlobalActiveContent(this);
   4622      break;
   4623    }
   4624    case eDragExit: {
   4625      // make sure to fire the enter and exit_synth events after the
   4626      // eDragExit event, otherwise we'll clean up too early
   4627      GenerateDragDropEnterExit(presContext, aEvent->AsDragEvent());
   4628      if (auto* bc = BrowserChild::GetFrom(presContext->GetDocShell())) {
   4629        // SendUpdateDropEffect to prevent nsIDragService from waiting for
   4630        // response of forwarded dragexit event.
   4631        bc->SendUpdateDropEffect(nsIDragService::DRAGDROP_ACTION_NONE,
   4632                                 nsIDragService::DRAGDROP_ACTION_NONE);
   4633      }
   4634      break;
   4635    }
   4636    case eKeyUp:
   4637      // If space key is released, we need to inactivate the element which was
   4638      // activated by preceding space key down.
   4639      // XXX Currently, we don't store the reason of activation.  Therefore,
   4640      //     this may cancel what is activated by a mousedown, but it must not
   4641      //     cause actual problem in web apps in the wild since it must be
   4642      //     rare case that users release space key during a mouse click/drag.
   4643      if (aEvent->AsKeyboardEvent()->ShouldWorkAsSpaceKey()) {
   4644        ClearGlobalActiveContent(this);
   4645      }
   4646      break;
   4647 
   4648    case eKeyPress: {
   4649      WidgetKeyboardEvent* keyEvent = aEvent->AsKeyboardEvent();
   4650      PostHandleKeyboardEvent(keyEvent, mCurrentTarget, *aStatus);
   4651    } break;
   4652 
   4653    case eMouseEnterIntoWidget:
   4654      if (mCurrentTarget) {
   4655        nsCOMPtr<nsIContent> targetContent =
   4656            mCurrentTarget->GetContentForEvent(aEvent);
   4657        SetContentState(targetContent, ElementState::HOVER);
   4658      }
   4659      break;
   4660 
   4661    case eMouseExitFromWidget:
   4662      MOZ_ASSERT_UNREACHABLE(
   4663          "Should've already been handled in PreHandleEvent()");
   4664      break;
   4665 
   4666 #ifdef XP_MACOSX
   4667    case eMouseActivate:
   4668      if (mCurrentTarget) {
   4669        nsCOMPtr<nsIContent> targetContent =
   4670            mCurrentTarget->GetContentForEvent(aEvent);
   4671        if (!NodeAllowsClickThrough(targetContent)) {
   4672          *aStatus = nsEventStatus_eConsumeNoDefault;
   4673        }
   4674      }
   4675      break;
   4676 #endif
   4677 
   4678    default:
   4679      break;
   4680  }
   4681 
   4682  // Reset target frame to null to avoid mistargeting after reentrant event
   4683  mCurrentTarget = nullptr;
   4684  mCurrentTargetContent = nullptr;
   4685 
   4686  return ret;
   4687 }
   4688 
   4689 BrowserParent* EventStateManager::GetCrossProcessTarget() {
   4690  return IMEStateManager::GetActiveBrowserParent();
   4691 }
   4692 
   4693 bool EventStateManager::IsTargetCrossProcess(WidgetGUIEvent* aEvent) {
   4694  // Check to see if there is a focused, editable content in chrome,
   4695  // in that case, do not forward IME events to content
   4696  Element* focusedElement = GetFocusedElement();
   4697  if (focusedElement && focusedElement->IsEditable()) {
   4698    return false;
   4699  }
   4700  return IMEStateManager::GetActiveBrowserParent() != nullptr;
   4701 }
   4702 
   4703 void EventStateManager::NotifyDestroyPresContext(nsPresContext* aPresContext) {
   4704  RefPtr<nsPresContext> presContext = aPresContext;
   4705  if (presContext) {
   4706    IMEStateManager::OnDestroyPresContext(*presContext);
   4707  }
   4708 
   4709  // Bug 70855: Presentation is going away, possibly for a reframe.
   4710  // Reset the hover state so that if we're recreating the presentation,
   4711  // we won't have the old hover state still set in the new presentation,
   4712  // as if the new presentation is resized, a new element may be hovered.
   4713  ResetHoverState();
   4714 
   4715  mMouseEnterLeaveHelper = nullptr;
   4716  mPointersEnterLeaveHelper.Clear();
   4717  PointerEventHandler::NotifyDestroyPresContext(presContext);
   4718 }
   4719 
   4720 void EventStateManager::ResetHoverState() {
   4721  if (mHoverContent) {
   4722    SetContentState(nullptr, ElementState::HOVER);
   4723  }
   4724 }
   4725 
   4726 void EventStateManager::SetPresContext(nsPresContext* aPresContext) {
   4727  mPresContext = aPresContext;
   4728 }
   4729 
   4730 void EventStateManager::ClearFrameRefs(nsIFrame* aFrame) {
   4731  if (aFrame && aFrame == mCurrentTarget) {
   4732    mCurrentTargetContent = aFrame->GetContent();
   4733  }
   4734 }
   4735 
   4736 struct CursorImage {
   4737  gfx::IntPoint mHotspot;
   4738  nsCOMPtr<imgIContainer> mContainer;
   4739  ImageResolution mResolution;
   4740  bool mEarlierCursorLoading = false;
   4741 };
   4742 
   4743 // Given the event that we're processing, and the computed cursor and hotspot,
   4744 // determine whether the custom CSS cursor should be blocked (that is, not
   4745 // honored).
   4746 //
   4747 // We will not honor it all of the following are true:
   4748 //
   4749 //  * the size of the custom cursor is bigger than layout.cursor.block.max-size.
   4750 //  * the bounds of the cursor would end up outside of the viewport of the
   4751 //    top-level content document.
   4752 //
   4753 // This is done in order to prevent hijacking the cursor, see bug 1445844 and
   4754 // co.
   4755 static bool ShouldBlockCustomCursor(nsPresContext* aPresContext,
   4756                                    WidgetEvent* aEvent,
   4757                                    const CursorImage& aCursor) {
   4758  int32_t width = 0;
   4759  int32_t height = 0;
   4760  aCursor.mContainer->GetWidth(&width);
   4761  aCursor.mContainer->GetHeight(&height);
   4762  aCursor.mResolution.ApplyTo(width, height);
   4763 
   4764  int32_t maxSize = StaticPrefs::layout_cursor_block_max_size();
   4765 
   4766  if (width <= maxSize && height <= maxSize) {
   4767    return false;
   4768  }
   4769 
   4770  auto input = DOMIntersectionObserver::ComputeInput(*aPresContext->Document(),
   4771                                                     nullptr, nullptr, nullptr);
   4772 
   4773  if (!input.mRootFrame) {
   4774    return false;
   4775  }
   4776 
   4777  nsPoint point = nsLayoutUtils::GetEventCoordinatesRelativeTo(
   4778      aEvent, RelativeTo{input.mRootFrame});
   4779 
   4780  // The cursor size won't be affected by our full zoom in the parent process,
   4781  // so undo that before checking the rect.
   4782  float zoom = aPresContext->GetFullZoom();
   4783 
   4784  // Also adjust for accessibility cursor scaling factor.
   4785  zoom /= LookAndFeel::GetFloat(LookAndFeel::FloatID::CursorScale, 1.0f);
   4786 
   4787  nsSize size(CSSPixel::ToAppUnits(width / zoom),
   4788              CSSPixel::ToAppUnits(height / zoom));
   4789  nsPoint hotspot(
   4790      CSSPixel::ToAppUnits(ViewAs<CSSPixel>(aCursor.mHotspot.x / zoom)),
   4791      CSSPixel::ToAppUnits(ViewAs<CSSPixel>(aCursor.mHotspot.y / zoom)));
   4792 
   4793  const nsRect cursorRect(point - hotspot, size);
   4794  auto output = DOMIntersectionObserver::Intersect(input, cursorRect);
   4795  return !output.mIntersectionRect ||
   4796         !(*output.mIntersectionRect == cursorRect);
   4797 }
   4798 
   4799 static gfx::IntPoint ComputeHotspot(imgIContainer* aContainer,
   4800                                    const Maybe<gfx::Point>& aHotspot) {
   4801  MOZ_ASSERT(aContainer);
   4802 
   4803  // css3-ui says to use the CSS-specified hotspot if present,
   4804  // otherwise use the intrinsic hotspot, otherwise use the top left
   4805  // corner.
   4806  if (aHotspot) {
   4807    int32_t imgWidth, imgHeight;
   4808    aContainer->GetWidth(&imgWidth);
   4809    aContainer->GetHeight(&imgHeight);
   4810    auto hotspot = gfx::IntPoint::Round(*aHotspot);
   4811    return {std::max(std::min(hotspot.x.value, imgWidth - 1), 0),
   4812            std::max(std::min(hotspot.y.value, imgHeight - 1), 0)};
   4813  }
   4814 
   4815  gfx::IntPoint hotspot;
   4816  aContainer->GetHotspotX(&hotspot.x.value);
   4817  aContainer->GetHotspotY(&hotspot.y.value);
   4818  return hotspot;
   4819 }
   4820 
   4821 static CursorImage ComputeCustomCursor(nsPresContext* aPresContext,
   4822                                       WidgetEvent* aEvent,
   4823                                       const nsIFrame& aFrame,
   4824                                       const nsIFrame::Cursor& aCursor) {
   4825  if (aCursor.mAllowCustomCursor == nsIFrame::AllowCustomCursorImage::No) {
   4826    return {};
   4827  }
   4828  const ComputedStyle& style =
   4829      aCursor.mStyle ? *aCursor.mStyle : *aFrame.Style();
   4830 
   4831  // If we are falling back because any cursor before us is loading, let the
   4832  // consumer know.
   4833  bool loading = false;
   4834  for (const auto& image : style.StyleUI()->Cursor().images.AsSpan()) {
   4835    MOZ_ASSERT(image.image.IsImageRequestType(),
   4836               "Cursor image should only parse url() types");
   4837    uint32_t status;
   4838    imgRequestProxy* req = image.image.GetImageRequest();
   4839    if (!req || NS_FAILED(req->GetImageStatus(&status))) {
   4840      continue;
   4841    }
   4842    if (!(status & imgIRequest::STATUS_LOAD_COMPLETE)) {
   4843      loading = true;
   4844      continue;
   4845    }
   4846    if (status & imgIRequest::STATUS_ERROR) {
   4847      continue;
   4848    }
   4849    nsCOMPtr<imgIContainer> container;
   4850    req->GetImage(getter_AddRefs(container));
   4851    if (!container) {
   4852      continue;
   4853    }
   4854    StyleImageOrientation orientation =
   4855        aFrame.StyleVisibility()->UsedImageOrientation(req);
   4856    container = nsLayoutUtils::OrientImage(container, orientation);
   4857    Maybe<gfx::Point> specifiedHotspot =
   4858        image.has_hotspot ? Some(gfx::Point{image.hotspot_x, image.hotspot_y})
   4859                          : Nothing();
   4860    gfx::IntPoint hotspot = ComputeHotspot(container, specifiedHotspot);
   4861    CursorImage result{hotspot, std::move(container),
   4862                       image.image.GetResolution(&style), loading};
   4863    if (ShouldBlockCustomCursor(aPresContext, aEvent, result)) {
   4864      continue;
   4865    }
   4866    // This is the one we want!
   4867    return result;
   4868  }
   4869  return {{}, nullptr, {}, loading};
   4870 }
   4871 
   4872 void EventStateManager::UpdateCursor(nsPresContext* aPresContext,
   4873                                     WidgetMouseEvent* aEvent,
   4874                                     nsIFrame* aTargetFrame,
   4875                                     nsEventStatus* aStatus) {
   4876  if (!PointerEventHandler::IsLastPointerId(aEvent->pointerId)) {
   4877    MOZ_LOG_DEBUG_ONLY(
   4878        gMouseCursorUpdates, LogLevel::Verbose,
   4879        ("EventStateManager::UpdateCursor(aEvent=${pointerId=%u, source=%s, "
   4880         "message=%s, reason=%s}): Stopped updating cursor for the pointer "
   4881         "because of %s, ESM: %p, in-process root PresShell: %p",
   4882         aEvent->pointerId, InputSourceToString(aEvent->mInputSource).get(),
   4883         ToChar(aEvent->mMessage), aEvent->IsReal() ? "Real" : "Synthesized",
   4884         !PointerEventHandler::GetLastPointerId()
   4885             ? "no last pointerId"
   4886             : nsPrintfCString("different from last pointerId (%u)",
   4887                               *PointerEventHandler::GetLastPointerId())
   4888                   .get(),
   4889         this, GetRootPresShell()));
   4890    return;
   4891  }
   4892 
   4893  // XXX This is still not entirely correct, e.g. when mouse hover over the
   4894  // broder of a cross-origin iframe, we should show the cursor specified on the
   4895  // iframe (see bug 1943530).
   4896  if (nsSubDocumentFrame* f = do_QueryFrame(aTargetFrame)) {
   4897    if (auto* fl = f->FrameLoader();
   4898        fl && fl->IsRemoteFrame() && f->ContentReactsToPointerEvents()) {
   4899      // The sub-frame will update the cursor if needed.
   4900      MOZ_LOG_DEBUG_ONLY(
   4901          gMouseCursorUpdates, LogLevel::Verbose,
   4902          ("EventStateManager::UpdateCursor(aEvent=${pointerId=%u, source=%s, "
   4903           "message=%s, reason=%s}): Stopped updating cursor for the pointer "
   4904           "because of over a remote frame, ESM: %p, in-process root "
   4905           "PresShell: %p",
   4906           aEvent->pointerId, InputSourceToString(aEvent->mInputSource).get(),
   4907           ToChar(aEvent->mMessage), RealOrSynthesized(aEvent->IsReal()), this,
   4908           GetRootPresShell()));
   4909      return;
   4910    }
   4911  }
   4912 
   4913  auto cursor = StyleCursorKind::Default;
   4914  nsCOMPtr<imgIContainer> container;
   4915  ImageResolution resolution;
   4916  Maybe<gfx::IntPoint> hotspot;
   4917 
   4918  if (mHidingCursorWhileTyping && aEvent->IsReal()) {
   4919    // Any non-synthetic mouse event makes us show the cursor again.
   4920    mHidingCursorWhileTyping = false;
   4921  }
   4922 
   4923  if (mHidingCursorWhileTyping) {
   4924    cursor = StyleCursorKind::None;
   4925  } else if (mLockCursor != kInvalidCursorKind) {
   4926    // If cursor is locked just use the locked one
   4927    cursor = mLockCursor;
   4928  } else if (aTargetFrame) {
   4929    // If not locked, look for correct cursor
   4930    nsPoint pt = nsLayoutUtils::GetEventCoordinatesRelativeTo(
   4931        aEvent, RelativeTo{aTargetFrame});
   4932    const nsIFrame::Cursor framecursor = aTargetFrame->GetCursor(pt);
   4933    const CursorImage customCursor =
   4934        ComputeCustomCursor(aPresContext, aEvent, *aTargetFrame, framecursor);
   4935 
   4936    // If the current cursor is from the same frame, and it is now
   4937    // loading some new image for the cursor, we should wait for a
   4938    // while rather than taking its fallback cursor directly.
   4939    if (customCursor.mEarlierCursorLoading &&
   4940        gLastCursorSourceFrame == aTargetFrame &&
   4941        TimeStamp::NowLoRes() - gLastCursorUpdateTime <
   4942            TimeDuration::FromMilliseconds(kCursorLoadingTimeout)) {
   4943      return;
   4944    }
   4945    cursor = framecursor.mCursor;
   4946    container = std::move(customCursor.mContainer);
   4947    resolution = customCursor.mResolution;
   4948    hotspot = Some(customCursor.mHotspot);
   4949  }
   4950 
   4951  if (aTargetFrame) {
   4952    if (cursor == StyleCursorKind::Pointer && IsSelectingLink(aTargetFrame)) {
   4953      cursor = aTargetFrame->GetWritingMode().IsVertical()
   4954                   ? StyleCursorKind::VerticalText
   4955                   : StyleCursorKind::Text;
   4956    }
   4957    SetCursor(cursor, container, resolution, hotspot,
   4958              aTargetFrame->GetNearestWidget(), false);
   4959    gLastCursorSourceFrame = aTargetFrame;
   4960    gLastCursorUpdateTime = TimeStamp::NowLoRes();
   4961    MOZ_LOG_DEBUG_ONLY(
   4962        gMouseCursorUpdates, LogLevel::Info,
   4963        ("EventStateManager::UpdateCursor(aEvent=${pointerId=%u, source=%s, "
   4964         "message=%s, reason=%s}): Updated the cursor to %u, ESM: %p, "
   4965         "in-process root PresShell: %p",
   4966         aEvent->pointerId, InputSourceToString(aEvent->mInputSource).get(),
   4967         ToChar(aEvent->mMessage), aEvent->IsReal() ? "Real" : "Synthesized",
   4968         static_cast<uint32_t>(cursor), this, GetRootPresShell()));
   4969  }
   4970 
   4971  if (mLockCursor != kInvalidCursorKind || StyleCursorKind::Auto != cursor) {
   4972    *aStatus = nsEventStatus_eConsumeDoDefault;
   4973  }
   4974 }
   4975 
   4976 void EventStateManager::ClearCachedWidgetCursor(nsIFrame* aTargetFrame) {
   4977  if (!aTargetFrame) {
   4978    return;
   4979  }
   4980  nsIWidget* aWidget = aTargetFrame->GetNearestWidget();
   4981  if (!aWidget) {
   4982    return;
   4983  }
   4984  aWidget->ClearCachedCursor();
   4985 }
   4986 
   4987 void EventStateManager::StartHidingCursorWhileTyping(nsIWidget* aWidget) {
   4988  if (mHidingCursorWhileTyping || sCursorSettingManager != this) {
   4989    return;
   4990  }
   4991  mHidingCursorWhileTyping = true;
   4992  SetCursor(StyleCursorKind::None, nullptr, {}, {}, aWidget, false);
   4993 }
   4994 
   4995 nsresult EventStateManager::SetCursor(StyleCursorKind aCursor,
   4996                                      imgIContainer* aContainer,
   4997                                      const ImageResolution& aResolution,
   4998                                      const Maybe<gfx::IntPoint>& aHotspot,
   4999                                      nsIWidget* aWidget, bool aLockCursor) {
   5000  EnsureDocument(mPresContext);
   5001  NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
   5002  sCursorSettingManager = this;
   5003 
   5004  NS_ENSURE_TRUE(aWidget, NS_ERROR_FAILURE);
   5005  if (aLockCursor) {
   5006    if (StyleCursorKind::Auto != aCursor) {
   5007      mLockCursor = aCursor;
   5008    } else {
   5009      // If cursor style is set to auto we unlock the cursor again.
   5010      mLockCursor = kInvalidCursorKind;
   5011    }
   5012  }
   5013  nsCursor c;
   5014  switch (aCursor) {
   5015    case StyleCursorKind::Auto:
   5016    case StyleCursorKind::Default:
   5017      c = eCursor_standard;
   5018      break;
   5019    case StyleCursorKind::Pointer:
   5020      c = eCursor_hyperlink;
   5021      break;
   5022    case StyleCursorKind::Crosshair:
   5023      c = eCursor_crosshair;
   5024      break;
   5025    case StyleCursorKind::Move:
   5026      c = eCursor_move;
   5027      break;
   5028    case StyleCursorKind::Text:
   5029      c = eCursor_select;
   5030      break;
   5031    case StyleCursorKind::Wait:
   5032      c = eCursor_wait;
   5033      break;
   5034    case StyleCursorKind::Help:
   5035      c = eCursor_help;
   5036      break;
   5037    case StyleCursorKind::NResize:
   5038      c = eCursor_n_resize;
   5039      break;
   5040    case StyleCursorKind::SResize:
   5041      c = eCursor_s_resize;
   5042      break;
   5043    case StyleCursorKind::WResize:
   5044      c = eCursor_w_resize;
   5045      break;
   5046    case StyleCursorKind::EResize:
   5047      c = eCursor_e_resize;
   5048      break;
   5049    case StyleCursorKind::NwResize:
   5050      c = eCursor_nw_resize;
   5051      break;
   5052    case StyleCursorKind::SeResize:
   5053      c = eCursor_se_resize;
   5054      break;
   5055    case StyleCursorKind::NeResize:
   5056      c = eCursor_ne_resize;
   5057      break;
   5058    case StyleCursorKind::SwResize:
   5059      c = eCursor_sw_resize;
   5060      break;
   5061    case StyleCursorKind::Copy:  // CSS3
   5062      c = eCursor_copy;
   5063      break;
   5064    case StyleCursorKind::Alias:
   5065      c = eCursor_alias;
   5066      break;
   5067    case StyleCursorKind::ContextMenu:
   5068      c = eCursor_context_menu;
   5069      break;
   5070    case StyleCursorKind::Cell:
   5071      c = eCursor_cell;
   5072      break;
   5073    case StyleCursorKind::Grab:
   5074      c = eCursor_grab;
   5075      break;
   5076    case StyleCursorKind::Grabbing:
   5077      c = eCursor_grabbing;
   5078      break;
   5079    case StyleCursorKind::Progress:
   5080      c = eCursor_spinning;
   5081      break;
   5082    case StyleCursorKind::ZoomIn:
   5083      c = eCursor_zoom_in;
   5084      break;
   5085    case StyleCursorKind::ZoomOut:
   5086      c = eCursor_zoom_out;
   5087      break;
   5088    case StyleCursorKind::NotAllowed:
   5089      c = eCursor_not_allowed;
   5090      break;
   5091    case StyleCursorKind::ColResize:
   5092      c = eCursor_col_resize;
   5093      break;
   5094    case StyleCursorKind::RowResize:
   5095      c = eCursor_row_resize;
   5096      break;
   5097    case StyleCursorKind::NoDrop:
   5098      c = eCursor_no_drop;
   5099      break;
   5100    case StyleCursorKind::VerticalText:
   5101      c = eCursor_vertical_text;
   5102      break;
   5103    case StyleCursorKind::AllScroll:
   5104      c = eCursor_all_scroll;
   5105      break;
   5106    case StyleCursorKind::NeswResize:
   5107      c = eCursor_nesw_resize;
   5108      break;
   5109    case StyleCursorKind::NwseResize:
   5110      c = eCursor_nwse_resize;
   5111      break;
   5112    case StyleCursorKind::NsResize:
   5113      c = eCursor_ns_resize;
   5114      break;
   5115    case StyleCursorKind::EwResize:
   5116      c = eCursor_ew_resize;
   5117      break;
   5118    case StyleCursorKind::None:
   5119      c = eCursor_none;
   5120      break;
   5121    default:
   5122      MOZ_ASSERT_UNREACHABLE("Unknown cursor kind");
   5123      c = eCursor_standard;
   5124      break;
   5125  }
   5126 
   5127  uint32_t x = aHotspot ? aHotspot->x.value : 0;
   5128  uint32_t y = aHotspot ? aHotspot->y.value : 0;
   5129  aWidget->SetCursor(nsIWidget::Cursor{c, aContainer, x, y, aResolution});
   5130  return NS_OK;
   5131 }
   5132 
   5133 bool EventStateManager::CursorSettingManagerHasLockedCursor() {
   5134  return sCursorSettingManager &&
   5135         sCursorSettingManager->mLockCursor != kInvalidCursorKind;
   5136 }
   5137 
   5138 class MOZ_STACK_CLASS ESMEventCB : public EventDispatchingCallback {
   5139 public:
   5140  explicit ESMEventCB(nsIContent* aTarget) : mTarget(aTarget) {}
   5141 
   5142  MOZ_CAN_RUN_SCRIPT
   5143  void HandleEvent(EventChainPostVisitor& aVisitor) override {
   5144    if (aVisitor.mPresContext) {
   5145      nsIFrame* frame = aVisitor.mPresContext->GetPrimaryFrameFor(mTarget);
   5146      if (frame) {
   5147        frame->HandleEvent(aVisitor.mPresContext, aVisitor.mEvent->AsGUIEvent(),
   5148                           &aVisitor.mEventStatus);
   5149      }
   5150    }
   5151  }
   5152 
   5153  nsCOMPtr<nsIContent> mTarget;
   5154 };
   5155 
   5156 static UniquePtr<WidgetMouseEvent> CreateMouseOrPointerWidgetEvent(
   5157    WidgetMouseEvent* aMouseEvent, EventMessage aMessage,
   5158    EventTarget* aRelatedTarget) {
   5159  // This method does not support creating a mouse/pointer button change event
   5160  // because of no data about the changing state.
   5161  MOZ_ASSERT(aMessage != eMouseDown);
   5162  MOZ_ASSERT(aMessage != eMouseUp);
   5163  MOZ_ASSERT(aMessage != ePointerDown);
   5164  MOZ_ASSERT(aMessage != ePointerUp);
   5165  // This method is currently designed to create the following events.
   5166  MOZ_ASSERT(aMessage == eMouseOver || aMessage == eMouseEnter ||
   5167             aMessage == eMouseOut || aMessage == eMouseLeave ||
   5168             aMessage == ePointerOver || aMessage == ePointerEnter ||
   5169             aMessage == ePointerOut || aMessage == ePointerLeave ||
   5170             aMessage == eMouseEnterIntoWidget ||
   5171             aMessage == eMouseExitFromWidget);
   5172 
   5173  WidgetPointerEvent* sourcePointer = aMouseEvent->AsPointerEvent();
   5174  UniquePtr<WidgetMouseEvent> newEvent;
   5175  if (sourcePointer) {
   5176    AUTO_PROFILER_LABEL("CreateMouseOrPointerWidgetEvent", OTHER);
   5177 
   5178    WidgetPointerEvent* newPointerEvent = new WidgetPointerEvent(
   5179        aMouseEvent->IsTrusted(), aMessage, aMouseEvent->mWidget);
   5180    newPointerEvent->mIsPrimary = sourcePointer->mIsPrimary;
   5181    newPointerEvent->mWidth = sourcePointer->mWidth;
   5182    newPointerEvent->mHeight = sourcePointer->mHeight;
   5183    newPointerEvent->mInputSource = sourcePointer->mInputSource;
   5184 
   5185    newEvent = WrapUnique(newPointerEvent);
   5186  } else {
   5187    newEvent = MakeUnique<WidgetMouseEvent>(aMouseEvent->IsTrusted(), aMessage,
   5188                                            aMouseEvent->mWidget,
   5189                                            WidgetMouseEvent::eReal);
   5190  }
   5191 
   5192  // Inherit whether the event is synthesized by the test API or not.
   5193  // Then, when the event is synthesized by a test API and handled in a remote
   5194  // process, it won't be ignored.  See PresShell::HandleEvent().
   5195  newEvent->mFlags.mIsSynthesizedForTests =
   5196      aMouseEvent->mFlags.mIsSynthesizedForTests;
   5197 
   5198  newEvent->mRelatedTarget = aRelatedTarget;
   5199  newEvent->mRefPoint = aMouseEvent->mRefPoint;
   5200  newEvent->mModifiers = aMouseEvent->mModifiers;
   5201  // NOTE: If you need to change this if-expression, you need to update
   5202  // WidgetMouseEventBase::ComputeMouseButtonPressure() too.
   5203  if (!aMouseEvent->mFlags.mDispatchedAtLeastOnce &&
   5204      aMouseEvent->InputSourceSupportsHover()) {
   5205    // If we synthesize a pointer event or a mouse event from another event
   5206    // which changes a button state whose input soucre supports hover state and
   5207    // the source event has not been dispatched yet, we should set to the button
   5208    // state of the synthesizing event to previous one.
   5209    // Note that we don't need to do this if the input source does not support
   5210    // hover state because a WPT check the behavior (see below) and the other
   5211    // browsers pass the test even though this is inconsistent behavior.
   5212    newEvent->mButton =
   5213        sourcePointer ? MouseButton::eNotPressed : MouseButton::ePrimary;
   5214    if (aMouseEvent->IsPressingButton()) {
   5215      // If the source event has not been dispatched into the DOM yet, we
   5216      // need to remove the flag which is being pressed.
   5217      newEvent->mButtons = static_cast<decltype(WidgetMouseEvent::mButtons)>(
   5218          aMouseEvent->mButtons &
   5219          ~MouseButtonsFlagToChange(
   5220              static_cast<MouseButton>(aMouseEvent->mButton)));
   5221    } else if (aMouseEvent->IsReleasingButton()) {
   5222      // If the source event has not been dispatched into the DOM yet, we
   5223      // need to add the flag which is being released.
   5224      newEvent->mButtons = static_cast<decltype(WidgetMouseEvent::mButtons)>(
   5225          aMouseEvent->mButtons |
   5226          MouseButtonsFlagToChange(
   5227              static_cast<MouseButton>(aMouseEvent->mButton)));
   5228    } else {
   5229      // The source event does not change the buttons state so that we can
   5230      // set mButtons value as-is.
   5231      newEvent->mButtons = aMouseEvent->mButtons;
   5232    }
   5233    // Adjust pressure if it does not matches with mButtons.
   5234    // FIXME: We may use wrong pressure value if the source event has not been
   5235    // dispatched into the DOM yet.  However, fixing this requires to store the
   5236    // last pressure value somewhere (bug 1953669).
   5237    newEvent->mPressure = newEvent->ComputeMouseButtonPressure();
   5238  } else {
   5239    // If the event has already been dispatched into the tree, web apps has
   5240    // already handled the button state change, so the button state of the
   5241    // source event has already synced.
   5242    // If the input source does not have hover state, we don't need to modify
   5243    // the state because the other browsers behave so and tested by
   5244    // pointerevent_attributes_nohover_pointers.html even though this is
   5245    // different expectation from
   5246    // pointerevent_attributes_hoverable_pointers.html, but the other browsers
   5247    // pass both of them.
   5248    newEvent->mButton = aMouseEvent->mButton;
   5249    newEvent->mButtons = aMouseEvent->mButtons;
   5250    newEvent->mPressure = aMouseEvent->mPressure;
   5251  }
   5252 
   5253  newEvent->mInputSource = aMouseEvent->mInputSource;
   5254  newEvent->pointerId = aMouseEvent->pointerId;
   5255 
   5256  return newEvent;
   5257 }
   5258 
   5259 already_AddRefed<nsIWidget>
   5260 EventStateManager::DispatchMouseOrPointerBoundaryEvent(
   5261    WidgetMouseEvent* aMouseEvent, EventMessage aMessage,
   5262    nsIContent* aTargetContent, nsIContent* aRelatedContent) {
   5263  MOZ_ASSERT(aMessage == eMouseEnter || aMessage == ePointerEnter ||
   5264             aMessage == eMouseLeave || aMessage == ePointerLeave ||
   5265             aMessage == eMouseOver || aMessage == ePointerOver ||
   5266             aMessage == eMouseOut || aMessage == ePointerOut);
   5267 
   5268  // https://w3c.github.io/pointerlock/#dom-element-requestpointerlock
   5269  // "[Once in the locked state...E]vents that require the concept
   5270  // of a mouse cursor must not be dispatched (for example: mouseover,
   5271  // mouseout...).
   5272  // XXXedgar should we also block pointer events?
   5273  if (PointerLockManager::IsLocked() &&
   5274      (aMessage == eMouseLeave || aMessage == eMouseEnter ||
   5275       aMessage == eMouseOver || aMessage == eMouseOut)) {
   5276    mCurrentTargetContent = nullptr;
   5277    nsCOMPtr<Element> pointerLockedElement =
   5278        PointerLockManager::GetLockedElement();
   5279    if (!pointerLockedElement) {
   5280      NS_WARNING("Should have pointer locked element, but didn't.");
   5281      return nullptr;
   5282    }
   5283    nsIFrame* const pointerLockedFrame =
   5284        mPresContext->GetPrimaryFrameFor(pointerLockedElement);
   5285    if (NS_WARN_IF(!pointerLockedFrame)) {
   5286      return nullptr;
   5287    }
   5288    return do_AddRef(pointerLockedFrame->GetNearestWidget());
   5289  }
   5290 
   5291  mCurrentTargetContent = nullptr;
   5292 
   5293  if (!aTargetContent) {
   5294    return nullptr;
   5295  }
   5296 
   5297  // Store the widget before dispatching the event because some event listeners
   5298  // of the dispatching event may cause reframe the target or remove the target
   5299  // from the tree.
   5300  nsCOMPtr<nsIWidget> targetWidget;
   5301  if (nsIFrame* const targetFrame =
   5302          mPresContext->GetPrimaryFrameFor(aTargetContent)) {
   5303    targetWidget = targetFrame->GetNearestWidget();
   5304  }
   5305 
   5306  nsCOMPtr<nsIContent> targetContent = aTargetContent;
   5307  nsCOMPtr<nsIContent> relatedContent = aRelatedContent;
   5308 
   5309  UniquePtr<WidgetMouseEvent> dispatchEvent =
   5310      CreateMouseOrPointerWidgetEvent(aMouseEvent, aMessage, relatedContent);
   5311 
   5312  AutoWeakFrame previousTarget = mCurrentTarget;
   5313  mCurrentTargetContent = targetContent;
   5314 
   5315  nsEventStatus status = nsEventStatus_eIgnore;
   5316  ESMEventCB callback(targetContent);
   5317  RefPtr<nsPresContext> presContext = mPresContext;
   5318  EventDispatcher::Dispatch(targetContent, presContext, dispatchEvent.get(),
   5319                            nullptr, &status, &callback);
   5320 
   5321  if (mPresContext) {
   5322    // If we are entering/leaving remote content, dispatch a mouse enter/exit
   5323    // event to the remote frame.
   5324    if (IsTopLevelRemoteTarget(targetContent)) {
   5325      if (aMessage == eMouseOut) {
   5326        // For remote content, send a puppet widget mouse exit event.
   5327        UniquePtr<WidgetMouseEvent> remoteEvent =
   5328            CreateMouseOrPointerWidgetEvent(aMouseEvent, eMouseExitFromWidget,
   5329                                            relatedContent);
   5330        remoteEvent->mExitFrom = Some(WidgetMouseEvent::ePuppet);
   5331 
   5332        // mCurrentTarget is set to the new target, so we must reset it to the
   5333        // old target and then dispatch a cross-process event. (mCurrentTarget
   5334        // will be set back below.) HandleCrossProcessEvent will query for the
   5335        // proper target via GetEventTarget which will return mCurrentTarget.
   5336        mCurrentTarget = mPresContext->GetPrimaryFrameFor(targetContent);
   5337        HandleCrossProcessEvent(remoteEvent.get(), &status);
   5338      } else if (aMessage == eMouseOver) {
   5339        UniquePtr<WidgetMouseEvent> remoteEvent =
   5340            CreateMouseOrPointerWidgetEvent(aMouseEvent, eMouseEnterIntoWidget,
   5341                                            relatedContent);
   5342        HandleCrossProcessEvent(remoteEvent.get(), &status);
   5343      }
   5344    }
   5345  }
   5346 
   5347  mCurrentTargetContent = nullptr;
   5348  mCurrentTarget = previousTarget;
   5349 
   5350  return targetWidget.forget();
   5351 }
   5352 
   5353 static nsIContent* FindCommonAncestor(nsIContent* aNode1, nsIContent* aNode2) {
   5354  if (!aNode1 || !aNode2) {
   5355    return nullptr;
   5356  }
   5357  return nsContentUtils::GetCommonFlattenedTreeAncestor(aNode1, aNode2);
   5358 }
   5359 
   5360 class EnterLeaveDispatcher {
   5361 public:
   5362  EnterLeaveDispatcher(EventStateManager* aESM, nsIContent* aTarget,
   5363                       nsIContent* aRelatedTarget,
   5364                       WidgetMouseEvent* aMouseEvent,
   5365                       EventMessage aEventMessage)
   5366      : mESM(aESM), mMouseEvent(aMouseEvent), mEventMessage(aEventMessage) {
   5367    nsPIDOMWindowInner* win =
   5368        aTarget ? aTarget->OwnerDoc()->GetInnerWindow() : nullptr;
   5369    if (aMouseEvent->AsPointerEvent()
   5370            ? win && win->HasPointerEnterLeaveEventListeners()
   5371            : win && win->HasMouseEnterLeaveEventListeners()) {
   5372      mRelatedTarget =
   5373          aRelatedTarget ? aRelatedTarget->FindFirstNonChromeOnlyAccessContent()
   5374                         : nullptr;
   5375      nsINode* commonParent = FindCommonAncestor(aTarget, aRelatedTarget);
   5376      nsIContent* current = aTarget;
   5377      // Note, it is ok if commonParent is null!
   5378      while (current && current != commonParent) {
   5379        if (!current->ChromeOnlyAccess()) {
   5380          mTargets.AppendObject(current);
   5381        }
   5382        // mouseenter/leave is fired only on elements.
   5383        current = current->GetFlattenedTreeParent();
   5384      }
   5385    }
   5386  }
   5387 
   5388  // TODO: Convert this to MOZ_CAN_RUN_SCRIPT (bug 1415230)
   5389  MOZ_CAN_RUN_SCRIPT_BOUNDARY void Dispatch() {
   5390    if (mEventMessage == eMouseEnter || mEventMessage == ePointerEnter) {
   5391      for (int32_t i = mTargets.Count() - 1; i >= 0; --i) {
   5392        nsCOMPtr<nsIWidget> widget = mESM->DispatchMouseOrPointerBoundaryEvent(
   5393            mMouseEvent, mEventMessage, MOZ_KnownLive(mTargets[i]),
   5394            mRelatedTarget);
   5395      }
   5396    } else {
   5397      for (int32_t i = 0; i < mTargets.Count(); ++i) {
   5398        nsCOMPtr<nsIWidget> widget = mESM->DispatchMouseOrPointerBoundaryEvent(
   5399            mMouseEvent, mEventMessage, MOZ_KnownLive(mTargets[i]),
   5400            mRelatedTarget);
   5401      }
   5402    }
   5403  }
   5404 
   5405  // Nothing overwrites anything after constructor. Please remove MOZ_KnownLive
   5406  // and MOZ_KNOWN_LIVE if anything marked as such becomes mutable.
   5407  const RefPtr<EventStateManager> mESM;
   5408  nsCOMArray<nsIContent> mTargets;
   5409  MOZ_KNOWN_LIVE nsCOMPtr<nsIContent> mRelatedTarget;
   5410  WidgetMouseEvent* mMouseEvent;
   5411  EventMessage mEventMessage;
   5412 };
   5413 
   5414 void EventStateManager::NotifyMouseOut(WidgetMouseEvent* aMouseEvent,
   5415                                       nsIContent* aMovingInto) {
   5416  const bool isPointer = aMouseEvent->mClass == ePointerEventClass;
   5417  LogModule* const logModule =
   5418      isPointer ? sPointerBoundaryLog : sMouseBoundaryLog;
   5419 
   5420  RefPtr<OverOutElementsWrapper> wrapper = GetWrapperByEventID(aMouseEvent);
   5421 
   5422  // If there is no deepest "leave" event target, that means the last "over"
   5423  // target has already been removed from the tree.  Therefore, checking only
   5424  // the "leave" event target is enough.
   5425  if (!wrapper || !wrapper->GetDeepestLeaveEventTarget()) {
   5426    return;
   5427  }
   5428  // Before firing "out" and/or "leave" events, check for recursion
   5429  if (wrapper->IsDispatchingOutEventOnLastOverEventTarget()) {
   5430    return;
   5431  }
   5432 
   5433  MOZ_LOG(logModule, LogLevel::Info,
   5434          ("NotifyMouseOut: the source event is %s (IsReal()=%s)",
   5435           ToChar(aMouseEvent->mMessage),
   5436           aMouseEvent->IsReal() ? "true" : "false"));
   5437 
   5438  // XXX If a content node is a container of remove content, it should be
   5439  // replaced with them and its children should not be visible.  Therefore,
   5440  // if the deepest "enter" target is not the last "over" target, i.e., the
   5441  // last "over" target has been removed from the DOM tree, it means that the
   5442  // child/descendant was not replaced by remote content.  So,
   5443  // wrapper->GetOutEventTaget() may be enough here.
   5444  if (RefPtr<nsFrameLoaderOwner> flo =
   5445          do_QueryObject(wrapper->GetDeepestLeaveEventTarget())) {
   5446    if (BrowsingContext* bc = flo->GetExtantBrowsingContext()) {
   5447      if (nsIDocShell* docshell = bc->GetDocShell()) {
   5448        if (RefPtr<nsPresContext> presContext = docshell->GetPresContext()) {
   5449          EventStateManager* kidESM = presContext->EventStateManager();
   5450          // Not moving into any element in this subdocument
   5451          MOZ_LOG(logModule, LogLevel::Info,
   5452                  ("Notifying child EventStateManager (%p) of \"out\" "
   5453                   "event...",
   5454                   kidESM));
   5455          kidESM->NotifyMouseOut(aMouseEvent, nullptr);
   5456        }
   5457      }
   5458    }
   5459  }
   5460  // That could have caused DOM events which could wreak havoc. Reverify
   5461  // things and be careful.
   5462  if (!wrapper->GetDeepestLeaveEventTarget()) {
   5463    return;
   5464  }
   5465 
   5466  wrapper->WillDispatchOutAndOrLeaveEvent();
   5467 
   5468  // Don't touch hover state if aMovingInto is non-null.  Caller will update
   5469  // hover state itself, and we have optimizations for hover switching between
   5470  // two nearby elements both deep in the DOM tree that would be defeated by
   5471  // switching the hover state to null here.
   5472  if (!aMovingInto && !isPointer) {
   5473    // Unset :hover
   5474    SetContentState(nullptr, ElementState::HOVER);
   5475  }
   5476 
   5477  EnterLeaveDispatcher leaveDispatcher(
   5478      this, wrapper->GetDeepestLeaveEventTarget(), aMovingInto, aMouseEvent,
   5479      isPointer ? ePointerLeave : eMouseLeave);
   5480 
   5481  // "out" events hould be fired only when the deepest "leave" event target
   5482  // is the last "over" event target.
   5483  if (nsCOMPtr<nsIContent> outEventTarget = wrapper->GetOutEventTarget()) {
   5484    MOZ_LOG(logModule, LogLevel::Info,
   5485            ("Dispatching %s event to %s (%p)",
   5486             isPointer ? "ePointerOut" : "eMouseOut",
   5487             outEventTarget ? ToString(*outEventTarget).c_str() : "nullptr",
   5488             outEventTarget.get()));
   5489    nsCOMPtr<nsIWidget> widget = DispatchMouseOrPointerBoundaryEvent(
   5490        aMouseEvent, isPointer ? ePointerOut : eMouseOut, outEventTarget,
   5491        aMovingInto);
   5492  }
   5493 
   5494  MOZ_LOG(logModule, LogLevel::Info,
   5495          ("Dispatching %s event to %s (%p) and its ancestors",
   5496           isPointer ? "ePointerLeave" : "eMouseLeave",
   5497           wrapper->GetDeepestLeaveEventTarget()
   5498               ? ToString(*wrapper->GetDeepestLeaveEventTarget()).c_str()
   5499               : "nullptr",
   5500           wrapper->GetDeepestLeaveEventTarget()));
   5501  leaveDispatcher.Dispatch();
   5502 
   5503  MOZ_LOG(logModule, LogLevel::Info,
   5504          ("Dispatched \"out\" and/or \"leave\" events"));
   5505  wrapper->DidDispatchOutAndOrLeaveEvent();
   5506 }
   5507 
   5508 void EventStateManager::RecomputeMouseEnterStateForRemoteFrame(
   5509    Element& aElement) {
   5510  if (!mMouseEnterLeaveHelper ||
   5511      mMouseEnterLeaveHelper->GetDeepestLeaveEventTarget() != &aElement) {
   5512    return;
   5513  }
   5514 
   5515  if (BrowserParent* remote = BrowserParent::GetFrom(&aElement)) {
   5516    remote->MouseEnterIntoWidget();
   5517  }
   5518 }
   5519 
   5520 void EventStateManager::NotifyMouseOver(WidgetMouseEvent* aMouseEvent,
   5521                                        nsIContent* aContent) {
   5522  NS_ASSERTION(aContent, "Mouse must be over something");
   5523 
   5524  const bool isPointer = aMouseEvent->mClass == ePointerEventClass;
   5525  LogModule* const logModule =
   5526      isPointer ? sPointerBoundaryLog : sMouseBoundaryLog;
   5527 
   5528  RefPtr<OverOutElementsWrapper> wrapper = GetWrapperByEventID(aMouseEvent);
   5529 
   5530  // If we have next "out" event target and it's the new "over" target, we don't
   5531  // need to dispatch "out" nor "enter" event.
   5532  if (!wrapper || aContent == wrapper->GetOutEventTarget()) {
   5533    return;
   5534  }
   5535 
   5536  // Before firing "over" and "enter" events, check for recursion
   5537  if (wrapper->IsDispatchingOverEventOn(aContent)) {
   5538    return;
   5539  }
   5540 
   5541  MOZ_LOG(logModule, LogLevel::Info,
   5542          ("NotifyMouseOver: the source event is %s (IsReal()=%s)",
   5543           ToChar(aMouseEvent->mMessage),
   5544           aMouseEvent->IsReal() ? "true" : "false"));
   5545 
   5546  // Check to see if we're a subdocument and if so update the parent
   5547  // document's ESM state to indicate that the mouse is over the
   5548  // content associated with our subdocument.
   5549  EnsureDocument(mPresContext);
   5550  if (Document* parentDoc = mDocument->GetInProcessParentDocument()) {
   5551    if (nsCOMPtr<nsIContent> docContent = mDocument->GetEmbedderElement()) {
   5552      if (PresShell* parentPresShell = parentDoc->GetPresShell()) {
   5553        RefPtr<EventStateManager> parentESM =
   5554            parentPresShell->GetPresContext()->EventStateManager();
   5555        MOZ_LOG(logModule, LogLevel::Info,
   5556                ("Notifying parent EventStateManager (%p) of \"over\" "
   5557                 "event...",
   5558                 parentESM.get()));
   5559        parentESM->NotifyMouseOver(aMouseEvent, docContent);
   5560      }
   5561    }
   5562  }
   5563  // Firing the DOM event in the parent document could cause all kinds
   5564  // of havoc.  Reverify and take care.
   5565  if (aContent == wrapper->GetOutEventTarget()) {
   5566    return;
   5567  }
   5568 
   5569  // Remember the deepest leave event target as the related content for the
   5570  // DispatchMouseOrPointerBoundaryEvent() call below, since NotifyMouseOut()
   5571  // resets it, bug 298477.
   5572  nsCOMPtr<nsIContent> deepestLeaveEventTarget =
   5573      wrapper->GetDeepestLeaveEventTarget();
   5574 
   5575  EnterLeaveDispatcher enterDispatcher(this, aContent, deepestLeaveEventTarget,
   5576                                       aMouseEvent,
   5577                                       isPointer ? ePointerEnter : eMouseEnter);
   5578 
   5579  if (!isPointer) {
   5580    SetContentState(aContent, ElementState::HOVER);
   5581  }
   5582 
   5583  NotifyMouseOut(aMouseEvent, aContent);
   5584 
   5585  wrapper->WillDispatchOverAndEnterEvent(aContent);
   5586 
   5587  // Fire mouseover
   5588  // XXX If aContent has already been removed from the DOM tree, what should we
   5589  // do? At least, dispatching `mouseover` on it is odd.
   5590  MOZ_LOG(logModule, LogLevel::Info,
   5591          ("Dispatching %s event to %s (%p)",
   5592           isPointer ? "ePointerOver" : "eMouseOver",
   5593           aContent ? ToString(*aContent).c_str() : "nullptr", aContent));
   5594  nsCOMPtr<nsIWidget> targetWidget = DispatchMouseOrPointerBoundaryEvent(
   5595      aMouseEvent, isPointer ? ePointerOver : eMouseOver, aContent,
   5596      deepestLeaveEventTarget);
   5597 
   5598  MOZ_LOG(logModule, LogLevel::Info,
   5599          ("Dispatching %s event to %s (%p) and its ancestors",
   5600           isPointer ? "ePointerEnter" : "eMouseEnter",
   5601           aContent ? ToString(*aContent).c_str() : "nullptr", aContent));
   5602  enterDispatcher.Dispatch();
   5603 
   5604  MOZ_LOG(logModule, LogLevel::Info,
   5605          ("Dispatched \"over\" and \"enter\" events (the original \"over\" "
   5606           "event target was in the document %p, and now in %p)",
   5607           aContent->GetComposedDoc(), mDocument.get()));
   5608  wrapper->DidDispatchOverAndEnterEvent(
   5609      aContent->GetComposedDoc() == mDocument ? aContent : nullptr,
   5610      targetWidget);
   5611 }
   5612 
   5613 // Returns the center point of the window's client area. This is
   5614 // in widget coordinates, i.e. relative to the widget's top-left
   5615 // corner, not in screen coordinates, the same units that UIEvent::
   5616 // refpoint is in. It may not be the exact center of the window if
   5617 // the platform requires rounding the coordinate.
   5618 static LayoutDeviceIntPoint GetWindowClientRectCenter(nsIWidget* aWidget) {
   5619  NS_ENSURE_TRUE(aWidget, LayoutDeviceIntPoint(0, 0));
   5620 
   5621  LayoutDeviceIntRect rect = aWidget->GetClientBounds();
   5622  LayoutDeviceIntPoint point(rect.width / 2, rect.height / 2);
   5623  int32_t round = aWidget->RoundsWidgetCoordinatesTo();
   5624  point.x = point.x / round * round;
   5625  point.y = point.y / round * round;
   5626  return point;
   5627 }
   5628 
   5629 void EventStateManager::GeneratePointerEnterExit(EventMessage aMessage,
   5630                                                 WidgetMouseEvent* aEvent) {
   5631  WidgetPointerEvent pointerEvent(*aEvent);
   5632  pointerEvent.mMessage = aMessage;
   5633  GenerateMouseEnterExit(&pointerEvent);
   5634 }
   5635 
   5636 /* static */
   5637 void EventStateManager::UpdateLastRefPointOfMouseEvent(
   5638    WidgetMouseEvent* aMouseEvent) {
   5639  if (aMouseEvent->mMessage != ePointerRawUpdate &&
   5640      aMouseEvent->mMessage != eMouseMove &&
   5641      aMouseEvent->mMessage != ePointerMove) {
   5642    return;
   5643  }
   5644 
   5645  const LayoutDeviceIntPoint& lastRefPoint =
   5646      aMouseEvent->mMessage == ePointerRawUpdate ? sLastRefPointOfRawUpdate
   5647                                                 : sLastRefPoint;
   5648 
   5649  // Mouse movement is reported on the MouseEvent.movement{X,Y} fields.
   5650  // Movement is calculated in UIEvent::GetMovementPoint() as:
   5651  //   previous_mousemove_mRefPoint - current_mousemove_mRefPoint.
   5652  if (PointerLockManager::IsLocked() && aMouseEvent->mWidget) {
   5653    // The pointer is locked. If the pointer is not located at the center of
   5654    // the window, dispatch a synthetic mousemove to return the pointer there.
   5655    // Doing this between "real" pointer moves gives the impression that the
   5656    // (locked) pointer can continue moving and won't stop at the screen
   5657    // boundary. We cancel the synthetic event so that we don't end up
   5658    // dispatching the centering move event to content.
   5659    aMouseEvent->mLastRefPoint =
   5660        GetWindowClientRectCenter(aMouseEvent->mWidget);
   5661 
   5662  } else if (lastRefPoint == kInvalidRefPoint) {
   5663    // We don't have a valid previous mousemove mRefPoint. This is either
   5664    // the first move we've encountered, or the mouse has just re-entered
   5665    // the application window. We should report (0,0) movement for this
   5666    // case, so make the current and previous mRefPoints the same.
   5667    aMouseEvent->mLastRefPoint = aMouseEvent->mRefPoint;
   5668  } else {
   5669    aMouseEvent->mLastRefPoint = lastRefPoint;
   5670  }
   5671 }
   5672 
   5673 /* static */
   5674 void EventStateManager::ResetPointerToWindowCenterWhilePointerLocked(
   5675    WidgetMouseEvent* aMouseEvent) {
   5676  MOZ_ASSERT(PointerLockManager::IsLocked());
   5677  if ((aMouseEvent->mMessage != ePointerRawUpdate &&
   5678       aMouseEvent->mMessage != eMouseMove &&
   5679       aMouseEvent->mMessage != ePointerMove) ||
   5680      !aMouseEvent->mWidget) {
   5681    return;
   5682  }
   5683 
   5684  // We generate pointermove from mousemove event, so only synthesize native
   5685  // mouse move and update sSynthCenteringPoint by mousemove event.
   5686  bool updateSynthCenteringPoint = aMouseEvent->mMessage == eMouseMove;
   5687 
   5688  // The pointer is locked. If the pointer is not located at the center of
   5689  // the window, dispatch a synthetic mousemove to return the pointer there.
   5690  // Doing this between "real" pointer moves gives the impression that the
   5691  // (locked) pointer can continue moving and won't stop at the screen
   5692  // boundary. We cancel the synthetic event so that we don't end up
   5693  // dispatching the centering move event to content.
   5694  LayoutDeviceIntPoint center = GetWindowClientRectCenter(aMouseEvent->mWidget);
   5695 
   5696  if (aMouseEvent->mRefPoint != center && updateSynthCenteringPoint) {
   5697    // Mouse move doesn't finish at the center of the window. Dispatch a
   5698    // synthetic native mouse event to move the pointer back to the center
   5699    // of the window, to faciliate more movement. But first, record that
   5700    // we've dispatched a synthetic mouse movement, so we can cancel it
   5701    // in the other branch here.
   5702    sSynthCenteringPoint = center;
   5703    // XXX Once we fix XXX comments in SetPointerLock about this API, we could
   5704    //     restrict that this API works only in the automation mode or in the
   5705    //     pointer locked situation.
   5706    aMouseEvent->mWidget->SynthesizeNativeMouseMove(
   5707        center + aMouseEvent->mWidget->WidgetToScreenOffset(), nullptr);
   5708  } else if (aMouseEvent->mRefPoint == sSynthCenteringPoint) {
   5709    // This is the "synthetic native" event we dispatched to re-center the
   5710    // pointer. Cancel it so we don't expose the centering move to content.
   5711    aMouseEvent->StopPropagation();
   5712    // Clear sSynthCenteringPoint so we don't cancel other events
   5713    // targeted at the center.
   5714    if (updateSynthCenteringPoint) {
   5715      sSynthCenteringPoint = kInvalidRefPoint;
   5716    }
   5717  }
   5718 }
   5719 
   5720 /* static */
   5721 void EventStateManager::UpdateLastPointerPosition(
   5722    WidgetMouseEvent* aMouseEvent) {
   5723  if (aMouseEvent->IsSynthesized()) {
   5724    return;
   5725  }
   5726  if (aMouseEvent->mMessage == eMouseMove) {
   5727    sLastRefPoint = aMouseEvent->mRefPoint;
   5728  } else if (aMouseEvent->mMessage == ePointerRawUpdate ||
   5729             // FYI: ePointerRawUpdate is handled only when there are some
   5730             // `pointerrawupdate` event listeners.  Therefore, we need to
   5731             // update the last ref point for ePointerRawUpdate when we dispatch
   5732             // ePointerMove too since the first `pointerrawupdate` event
   5733             // listener may be added after the ePointerMove.
   5734             aMouseEvent->mMessage == ePointerMove) {
   5735    // XXX Shouldn't we store last refpoint of PointerEvent per pointerId?
   5736    sLastRefPointOfRawUpdate = aMouseEvent->mRefPoint;
   5737  }
   5738 }
   5739 
   5740 void EventStateManager::GenerateMouseEnterExit(WidgetMouseEvent* aMouseEvent) {
   5741  EnsureDocument(mPresContext);
   5742  if (!mDocument) return;
   5743 
   5744  // Hold onto old target content through the event and reset after.
   5745  nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
   5746 
   5747  switch (aMouseEvent->mMessage) {
   5748    case eMouseMove:
   5749    case ePointerMove:
   5750    case ePointerRawUpdate:
   5751    case ePointerDown:
   5752    case ePointerGotCapture: {
   5753      // Get the target content target (mousemove target == mouseover target)
   5754      nsCOMPtr<nsIContent> targetElement = GetEventTargetContent(aMouseEvent);
   5755      if (!targetElement) {
   5756        // We're always over the document root, even if we're only
   5757        // over dead space in a page (whose frame is not associated with
   5758        // any content) or in print preview dead space
   5759        targetElement = mDocument->GetRootElement();
   5760      }
   5761      if (targetElement) {
   5762        NotifyMouseOver(aMouseEvent, targetElement);
   5763      }
   5764      break;
   5765    }
   5766    case ePointerUp: {
   5767      if (aMouseEvent->mFlags.mDispatchedAtLeastOnce) {
   5768        // If we've already dispatched the pointerup event caused by
   5769        // non-hoverable input device like touch, we need to synthesize
   5770        // pointerout and pointerleave events because the poiner is valid only
   5771        // while it's "down".
   5772        if (!aMouseEvent->InputSourceSupportsHover()) {
   5773          NotifyMouseOut(aMouseEvent, nullptr);
   5774        }
   5775        break;
   5776      }
   5777 
   5778      // If we're going to dispatch the pointerup event and the element under
   5779      // the pointer is changed from the previous pointer event dispatching, we
   5780      // need to dispatch pointer boundary events.  If the pointing device is
   5781      // hoverable, we always need to do it.  Otherwise, an element captures the
   5782      // pointer by default.  If so, we don't need the boundary events, but if
   5783      // the capture has already been released, e.g., by the capturing element
   5784      // is removed, we need to dispatch the pointer boundary event the same
   5785      // way as with hoverable pointer.
   5786      if (aMouseEvent->InputSourceSupportsHover() ||
   5787          !PointerEventHandler::GetPointerCapturingElement(
   5788              aMouseEvent->pointerId)) {
   5789        nsCOMPtr<nsIContent> targetElement = GetEventTargetContent(aMouseEvent);
   5790        if (!targetElement) {
   5791          targetElement = mDocument->GetRootElement();
   5792        }
   5793        if (targetElement) {
   5794          NotifyMouseOver(aMouseEvent, targetElement);
   5795        }
   5796        break;
   5797      }
   5798      break;
   5799    }
   5800    case ePointerLeave:
   5801    case ePointerCancel:
   5802    case eMouseExitFromWidget: {
   5803      // This is actually the window mouse exit or pointer leave event. We're
   5804      // not moving into any new element.
   5805 
   5806      RefPtr<OverOutElementsWrapper> helper = GetWrapperByEventID(aMouseEvent);
   5807      if (helper) {
   5808        nsCOMPtr<nsIWidget> lastOverWidget = helper->GetLastOverWidget();
   5809        if (lastOverWidget &&
   5810            nsContentUtils::GetTopLevelWidget(aMouseEvent->mWidget) !=
   5811                nsContentUtils::GetTopLevelWidget(lastOverWidget)) {
   5812          // the Mouse/PointerOut event widget doesn't have same top widget with
   5813          // the last over event target, it's a spurious event for the frame for
   5814          // the target.
   5815          break;
   5816        }
   5817      }
   5818 
   5819      // Reset sLastRefPoint and sLastRefPointOfRawUpdate, so that we'll know
   5820      // not to report any movement the next time we re-enter the window.
   5821      sLastRefPoint = sLastRefPointOfRawUpdate = kInvalidRefPoint;
   5822 
   5823      NotifyMouseOut(aMouseEvent, nullptr);
   5824      break;
   5825    }
   5826    default:
   5827      break;
   5828  }
   5829 
   5830  // reset mCurretTargetContent to what it was
   5831  mCurrentTargetContent = targetBeforeEvent;
   5832 }
   5833 
   5834 OverOutElementsWrapper* EventStateManager::GetWrapperByEventID(
   5835    WidgetMouseEvent* aMouseEvent) {
   5836  MOZ_ASSERT(aMouseEvent);
   5837  WidgetPointerEvent* pointer = aMouseEvent->AsPointerEvent();
   5838  if (!pointer) {
   5839    if (!mMouseEnterLeaveHelper) {
   5840      mMouseEnterLeaveHelper = new OverOutElementsWrapper(
   5841          OverOutElementsWrapper::BoundaryEventType::Mouse);
   5842    }
   5843    return mMouseEnterLeaveHelper;
   5844  }
   5845  return mPointersEnterLeaveHelper.GetOrInsertNew(
   5846      pointer->pointerId, OverOutElementsWrapper::BoundaryEventType::Pointer);
   5847 }
   5848 
   5849 /* static */
   5850 void EventStateManager::SetPointerLock(nsIWidget* aWidget,
   5851                                       nsPresContext* aPresContext) {
   5852  // Reset mouse wheel transaction
   5853  WheelTransaction::EndTransaction();
   5854 
   5855  // Deal with DnD events
   5856  nsCOMPtr<nsIDragService> dragService =
   5857      do_GetService("@mozilla.org/widget/dragservice;1");
   5858 
   5859  if (PointerLockManager::IsLocked()) {
   5860    MOZ_ASSERT(aWidget, "Locking pointer requires a widget");
   5861    MOZ_ASSERT(aPresContext, "Locking pointer requires a presContext");
   5862 
   5863    // Release all pointer capture when a pointer lock is successfully applied
   5864    // on an element.
   5865    PointerEventHandler::ReleaseAllPointerCapture();
   5866 
   5867    // Store the last known ref point so we can reposition the pointer after
   5868    // unlock.
   5869    sPreLockScreenPoint = LayoutDeviceIntPoint::Round(
   5870        sLastScreenPoint * aPresContext->CSSToDevPixelScale());
   5871 
   5872    // Fire a synthetic mouse move to ensure event state is updated. We first
   5873    // set the mouse to the center of the window, so that the mouse event
   5874    // doesn't report any movement.
   5875    // XXX Cannot we do synthesize the native mousemove in the parent process
   5876    //     with calling LockNativePointer below?  Then, we could make this API
   5877    //     work only in the automation mode.
   5878    sLastRefPoint = sLastRefPointOfRawUpdate =
   5879        GetWindowClientRectCenter(aWidget);
   5880    aWidget->SynthesizeNativeMouseMove(
   5881        sLastRefPoint + aWidget->WidgetToScreenOffset(), nullptr);
   5882 
   5883    // Suppress DnD
   5884    if (dragService) {
   5885      dragService->Suppress();
   5886    }
   5887 
   5888    // Activate native pointer lock on platforms where it is required (Wayland)
   5889    aWidget->LockNativePointer();
   5890  } else {
   5891    if (aWidget) {
   5892      // Deactivate native pointer lock on platforms where it is required
   5893      aWidget->UnlockNativePointer();
   5894    }
   5895 
   5896    // Reset SynthCenteringPoint to invalid so that next time we start
   5897    // locking pointer, it has its initial value.
   5898    sSynthCenteringPoint = kInvalidRefPoint;
   5899    if (aWidget) {
   5900      // Unlocking, so return pointer to the original position by firing a
   5901      // synthetic mouse event. We first reset sLastRefPoint and
   5902      // sLastRefPointOfRawUpdate to its pre-pointerlock position, so that the
   5903      // synthetic mouse event reports no movement.
   5904      sLastRefPoint = sLastRefPointOfRawUpdate =
   5905          sPreLockScreenPoint - aWidget->WidgetToScreenOffset();
   5906      // XXX Cannot we do synthesize the native mousemove in the parent process
   5907      //     with calling `UnlockNativePointer` above?  Then, we could make this
   5908      //     API work only in the automation mode.
   5909      aWidget->SynthesizeNativeMouseMove(sPreLockScreenPoint, nullptr);
   5910    }
   5911 
   5912    // Unsuppress DnD
   5913    if (dragService) {
   5914      dragService->Unsuppress();
   5915    }
   5916  }
   5917 }
   5918 
   5919 void EventStateManager::GenerateDragDropEnterExit(nsPresContext* aPresContext,
   5920                                                  WidgetDragEvent* aDragEvent) {
   5921  // Hold onto old target content through the event and reset after.
   5922  nsCOMPtr<nsIContent> targetBeforeEvent = mCurrentTargetContent;
   5923 
   5924  switch (aDragEvent->mMessage) {
   5925    case eDragOver: {
   5926      // when dragging from one frame to another, events are fired in the
   5927      // order: dragexit, dragenter, dragleave
   5928      if (sLastDragOverFrame != mCurrentTarget) {
   5929        // We'll need the content, too, to check if it changed separately from
   5930        // the frames.
   5931        nsCOMPtr<nsIContent> lastContent;
   5932        nsCOMPtr<nsIContent> targetContent =
   5933            mCurrentTarget->GetContentForEvent(aDragEvent);
   5934        if (targetContent && targetContent->IsText()) {
   5935          targetContent = targetContent->GetFlattenedTreeParent();
   5936        }
   5937 
   5938        if (sLastDragOverFrame) {
   5939          // The frame has changed but the content may not have. Check before
   5940          // dispatching to content
   5941          lastContent = sLastDragOverFrame->GetContentForEvent(aDragEvent);
   5942          if (lastContent && lastContent->IsText()) {
   5943            lastContent = lastContent->GetFlattenedTreeParent();
   5944          }
   5945 
   5946          RefPtr<nsPresContext> presContext = sLastDragOverFrame->PresContext();
   5947          FireDragEnterOrExit(presContext, aDragEvent, eDragExit, targetContent,
   5948                              lastContent, sLastDragOverFrame);
   5949          nsIContent* target = sLastDragOverFrame
   5950                                   ? sLastDragOverFrame.GetFrame()->GetContent()
   5951                                   : nullptr;
   5952          // XXXedgar, look like we need to consider fission OOP iframe, too.
   5953          if (IsTopLevelRemoteTarget(target)) {
   5954            // Dragging something and moving from web content to chrome only
   5955            // fires dragexit and dragleave to xul:browser. We have to forward
   5956            // dragexit to sLastDragOverFrame when its content is a remote
   5957            // target. We don't forward dragleave since it's generated from
   5958            // dragexit.
   5959            WidgetDragEvent remoteEvent(aDragEvent->IsTrusted(), eDragExit,
   5960                                        aDragEvent->mWidget);
   5961            remoteEvent.AssignDragEventData(*aDragEvent, true);
   5962            remoteEvent.mFlags.mIsSynthesizedForTests =
   5963                aDragEvent->mFlags.mIsSynthesizedForTests;
   5964            nsEventStatus remoteStatus = nsEventStatus_eIgnore;
   5965            HandleCrossProcessEvent(&remoteEvent, &remoteStatus);
   5966          }
   5967        }
   5968 
   5969        AutoWeakFrame currentTraget = mCurrentTarget;
   5970        FireDragEnterOrExit(aPresContext, aDragEvent, eDragEnter, lastContent,
   5971                            targetContent, currentTraget);
   5972 
   5973        if (sLastDragOverFrame) {
   5974          RefPtr<nsPresContext> presContext = sLastDragOverFrame->PresContext();
   5975          FireDragEnterOrExit(presContext, aDragEvent, eDragLeave,
   5976                              targetContent, lastContent, sLastDragOverFrame);
   5977        }
   5978 
   5979        sLastDragOverFrame = mCurrentTarget;
   5980      }
   5981    } break;
   5982 
   5983    case eDragExit: {
   5984      // This is actually the window mouse exit event.
   5985      if (sLastDragOverFrame) {
   5986        nsCOMPtr<nsIContent> lastContent =
   5987            sLastDragOverFrame->GetContentForEvent(aDragEvent);
   5988 
   5989        RefPtr<nsPresContext> lastDragOverFramePresContext =
   5990            sLastDragOverFrame->PresContext();
   5991        FireDragEnterOrExit(lastDragOverFramePresContext, aDragEvent, eDragExit,
   5992                            nullptr, lastContent, sLastDragOverFrame);
   5993        FireDragEnterOrExit(lastDragOverFramePresContext, aDragEvent,
   5994                            eDragLeave, nullptr, lastContent,
   5995                            sLastDragOverFrame);
   5996 
   5997        sLastDragOverFrame = nullptr;
   5998      }
   5999    } break;
   6000 
   6001    default:
   6002      break;
   6003  }
   6004 
   6005  // reset mCurretTargetContent to what it was
   6006  mCurrentTargetContent = targetBeforeEvent;
   6007 
   6008  // Now flush all pending notifications, for better responsiveness.
   6009  FlushLayout(aPresContext);
   6010 }
   6011 
   6012 void EventStateManager::FireDragEnterOrExit(nsPresContext* aPresContext,
   6013                                            WidgetDragEvent* aDragEvent,
   6014                                            EventMessage aMessage,
   6015                                            nsIContent* aRelatedTarget,
   6016                                            nsIContent* aTargetContent,
   6017                                            AutoWeakFrame& aTargetFrame) {
   6018  MOZ_ASSERT(aMessage == eDragLeave || aMessage == eDragExit ||
   6019             aMessage == eDragEnter);
   6020  nsEventStatus status = nsEventStatus_eIgnore;
   6021  WidgetDragEvent event(aDragEvent->IsTrusted(), aMessage, aDragEvent->mWidget);
   6022  event.AssignDragEventData(*aDragEvent, false);
   6023  event.mFlags.mIsSynthesizedForTests =
   6024      aDragEvent->mFlags.mIsSynthesizedForTests;
   6025  event.mRelatedTarget = aRelatedTarget;
   6026  if (aMessage == eDragExit && !StaticPrefs::dom_event_dragexit_enabled()) {
   6027    event.mFlags.mOnlyChromeDispatch = true;
   6028  }
   6029 
   6030  mCurrentTargetContent = aTargetContent;
   6031 
   6032  if (aTargetContent != aRelatedTarget) {
   6033    // XXX This event should still go somewhere!!
   6034    if (aTargetContent) {
   6035      EventDispatcher::Dispatch(aTargetContent, aPresContext, &event, nullptr,
   6036                                &status);
   6037    }
   6038 
   6039    // adjust the drag hover if the dragenter event was cancelled or this is a
   6040    // drag exit
   6041    if (status == nsEventStatus_eConsumeNoDefault || aMessage == eDragExit) {
   6042      SetContentState((aMessage == eDragEnter) ? aTargetContent : nullptr,
   6043                      ElementState::DRAGOVER);
   6044    }
   6045 
   6046    // collect any changes to moz cursor settings stored in the event's
   6047    // data transfer.
   6048    UpdateDragDataTransfer(&event);
   6049  }
   6050 
   6051  // Finally dispatch the event to the frame
   6052  if (aTargetFrame) {
   6053    aTargetFrame->HandleEvent(aPresContext, &event, &status);
   6054  }
   6055 }
   6056 
   6057 void EventStateManager::UpdateDragDataTransfer(WidgetDragEvent* dragEvent) {
   6058  NS_ASSERTION(dragEvent, "drag event is null in UpdateDragDataTransfer!");
   6059  if (!dragEvent->mDataTransfer) {
   6060    return;
   6061  }
   6062 
   6063  nsCOMPtr<nsIDragSession> dragSession =
   6064      nsContentUtils::GetDragSession(mPresContext);
   6065 
   6066  if (dragSession) {
   6067    // the initial dataTransfer is the one from the dragstart event that
   6068    // was set on the dragSession when the drag began.
   6069    RefPtr<DataTransfer> initialDataTransfer = dragSession->GetDataTransfer();
   6070    if (initialDataTransfer) {
   6071      // retrieve the current moz cursor setting and save it.
   6072      nsAutoString mozCursor;
   6073      dragEvent->mDataTransfer->GetMozCursor(mozCursor);
   6074      initialDataTransfer->SetMozCursor(mozCursor);
   6075    }
   6076  }
   6077 }
   6078 
   6079 void EventStateManager::PrepareForFollowingClickEvent(
   6080    WidgetMouseEvent& aEvent, nsIContent* aOverrideClickTarget) {
   6081  nsCOMPtr<nsIContent> mouseContent = aOverrideClickTarget;
   6082  if (!mouseContent && mCurrentTarget) {
   6083    mouseContent = mCurrentTarget->GetContentForEvent(&aEvent);
   6084  }
   6085  if (mouseContent && mouseContent->IsText()) {
   6086    nsINode* parent = mouseContent->GetFlattenedTreeParentNode();
   6087    if (parent && parent->IsContent()) {
   6088      mouseContent = parent->AsContent();
   6089    }
   6090  }
   6091 
   6092  LastMouseDownInfo& mouseDownInfo = GetLastMouseDownInfo(aEvent.mButton);
   6093  if (aEvent.mMessage == eMouseDown) {
   6094    mouseDownInfo.mLastMouseDownContent =
   6095        !aEvent.mClickEventPrevented ? mouseContent : nullptr;
   6096 
   6097    if (mouseDownInfo.mLastMouseDownContent) {
   6098      if (HTMLInputElement* input = HTMLInputElement::FromNodeOrNull(
   6099              mouseDownInfo.mLastMouseDownContent)) {
   6100        mouseDownInfo.mLastMouseDownInputControlType =
   6101            Some(input->ControlType());
   6102      } else if (mouseDownInfo.mLastMouseDownContent
   6103                     ->IsInNativeAnonymousSubtree()) {
   6104        if (HTMLInputElement* input = HTMLInputElement::FromNodeOrNull(
   6105                mouseDownInfo.mLastMouseDownContent
   6106                    ->GetFlattenedTreeParent())) {
   6107          mouseDownInfo.mLastMouseDownInputControlType =
   6108              Some(input->ControlType());
   6109        }
   6110      }
   6111    }
   6112  } else {
   6113    MOZ_ASSERT(aEvent.mMessage == eMouseUp);
   6114    aEvent.mClickTarget = [&]() -> EventTarget* {
   6115      if (aEvent.mClickEventPrevented || !mouseDownInfo.mLastMouseDownContent) {
   6116        return nullptr;
   6117      }
   6118      // If an element was capturing the pointer at dispatching ePointerUp, we
   6119      // should dispatch click/auxclick/contextmenu event on it to conform to
   6120      // Pointer Events. https://w3c.github.io/pointerevents/#event-dispatch
   6121      if (PointerEventHandler::ShouldDispatchClickEventOnCapturingElement(
   6122              &aEvent)) {
   6123        const RefPtr<Element> capturingElementAtLastPointerUp =
   6124            PointerEventHandler::GetPointerCapturingElementAtLastPointerUp();
   6125        if (capturingElementAtLastPointerUp &&
   6126            capturingElementAtLastPointerUp->GetPresContext(
   6127                Element::PresContextFor::eForComposedDoc) == mPresContext) {
   6128          return capturingElementAtLastPointerUp;
   6129        }
   6130      }
   6131      return GetCommonAncestorForMouseUp(
   6132          mouseContent, mouseDownInfo.mLastMouseDownContent,
   6133          mouseDownInfo.mLastMouseDownInputControlType);
   6134    }();
   6135    if (aEvent.mClickTarget) {
   6136      aEvent.mClickCount = mouseDownInfo.mClickCount;
   6137      mouseDownInfo.mClickCount = 0;
   6138    } else {
   6139      aEvent.mClickCount = 0;
   6140    }
   6141    mouseDownInfo.mLastMouseDownContent = nullptr;
   6142    mouseDownInfo.mLastMouseDownInputControlType = Nothing();
   6143  }
   6144 }
   6145 
   6146 // static
   6147 bool EventStateManager::EventCausesClickEvents(
   6148    const WidgetMouseEvent& aMouseEvent) {
   6149  if (NS_WARN_IF(aMouseEvent.mMessage != eMouseUp)) {
   6150    return false;
   6151  }
   6152  // If the mouseup event is synthesized event, we don't need to dispatch
   6153  // click events.
   6154  if (!aMouseEvent.IsReal()) {
   6155    return false;
   6156  }
   6157  // If mouse is still over same element, clickcount will be > 1.
   6158  // If it has moved it will be zero, so no click.
   6159  if (!aMouseEvent.mClickCount || !aMouseEvent.mClickTarget) {
   6160    return false;
   6161  }
   6162  // If click event was explicitly prevented, we shouldn't dispatch it.
   6163  if (aMouseEvent.mClickEventPrevented) {
   6164    return false;
   6165  }
   6166  // Check that the window isn't disabled before firing a click
   6167  // (see bug 366544).
   6168  return !(aMouseEvent.mWidget && !aMouseEvent.mWidget->IsEnabled());
   6169 }
   6170 
   6171 nsresult EventStateManager::InitAndDispatchClickEvent(
   6172    WidgetMouseEvent* aMouseUpEvent, nsEventStatus* aStatus,
   6173    EventMessage aMessage, PresShell* aPresShell, nsIContent* aMouseUpContent,
   6174    AutoWeakFrame aCurrentTarget, bool aNoContentDispatch,
   6175    nsIContent* aOverrideClickTarget) {
   6176  MOZ_ASSERT(aMouseUpEvent);
   6177  MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
   6178  MOZ_ASSERT(aMouseUpContent || aCurrentTarget || aOverrideClickTarget);
   6179 
   6180  Maybe<WidgetPointerEvent> pointerEvent;
   6181  Maybe<WidgetMouseEvent> mouseEvent;
   6182  if (IsPointerEventMessage(aMessage)) {
   6183    pointerEvent.emplace(aMouseUpEvent->IsTrusted(), aMessage,
   6184                         aMouseUpEvent->mWidget);
   6185  } else {
   6186    mouseEvent.emplace(aMouseUpEvent->IsTrusted(), aMessage,
   6187                       aMouseUpEvent->mWidget, WidgetMouseEvent::eReal);
   6188  }
   6189 
   6190  WidgetMouseEvent& mouseOrPointerEvent =
   6191      pointerEvent.isSome() ? pointerEvent.ref() : mouseEvent.ref();
   6192 
   6193  mouseOrPointerEvent.mRefPoint = aMouseUpEvent->mRefPoint;
   6194  mouseOrPointerEvent.mClickCount = aMouseUpEvent->mClickCount;
   6195  mouseOrPointerEvent.mModifiers = aMouseUpEvent->mModifiers;
   6196  mouseOrPointerEvent.mButtons = aMouseUpEvent->mButtons;
   6197  mouseOrPointerEvent.mTimeStamp = aMouseUpEvent->mTimeStamp;
   6198  mouseOrPointerEvent.mFlags.mOnlyChromeDispatch = aNoContentDispatch;
   6199  mouseOrPointerEvent.mFlags.mNoContentDispatch = aNoContentDispatch;
   6200  mouseOrPointerEvent.mButton = aMouseUpEvent->mButton;
   6201  mouseOrPointerEvent.pointerId = aMouseUpEvent->pointerId;
   6202  mouseOrPointerEvent.mInputSource = aMouseUpEvent->mInputSource;
   6203  nsIContent* target = aMouseUpContent;
   6204  nsIFrame* targetFrame = aCurrentTarget;
   6205  if (aOverrideClickTarget) {
   6206    target = aOverrideClickTarget;
   6207    targetFrame = aOverrideClickTarget->GetPrimaryFrame();
   6208  }
   6209 
   6210  if (!target->IsInComposedDoc()) {
   6211    return NS_OK;
   6212  }
   6213 
   6214  // Use local event status for each click event dispatching since it'll be
   6215  // cleared by EventStateManager::PreHandleEvent().  Therefore, dispatching
   6216  // an event means that previous event status will be ignored.
   6217  nsEventStatus status = nsEventStatus_eIgnore;
   6218  nsresult rv = aPresShell->HandleEventWithTarget(
   6219      &mouseOrPointerEvent, targetFrame, MOZ_KnownLive(target), &status);
   6220 
   6221  // Copy mMultipleActionsPrevented flag from a click event to the mouseup
   6222  // event only when it's set to true.  It may be set to true if an editor has
   6223  // already handled it.  This is important to avoid two or more default
   6224  // actions handled here.
   6225  aMouseUpEvent->mFlags.mMultipleActionsPrevented |=
   6226      mouseOrPointerEvent.mFlags.mMultipleActionsPrevented;
   6227  // If current status is nsEventStatus_eConsumeNoDefault, we don't need to
   6228  // overwrite it.
   6229  if (*aStatus == nsEventStatus_eConsumeNoDefault) {
   6230    return rv;
   6231  }
   6232  // If new status is nsEventStatus_eConsumeNoDefault or
   6233  // nsEventStatus_eConsumeDoDefault, use it.
   6234  if (status == nsEventStatus_eConsumeNoDefault ||
   6235      status == nsEventStatus_eConsumeDoDefault) {
   6236    *aStatus = status;
   6237    return rv;
   6238  }
   6239  // Otherwise, keep the original status.
   6240  return rv;
   6241 }
   6242 
   6243 nsresult EventStateManager::PostHandleMouseUp(
   6244    WidgetMouseEvent* aMouseUpEvent, nsEventStatus* aStatus,
   6245    nsIContent* aOverrideClickTarget) {
   6246  MOZ_ASSERT(aMouseUpEvent);
   6247  MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
   6248  MOZ_ASSERT(aStatus);
   6249 
   6250  RefPtr<PresShell> presShell = mPresContext->GetPresShell();
   6251  if (!presShell) {
   6252    return NS_OK;
   6253  }
   6254 
   6255  nsCOMPtr<nsIContent> clickTarget =
   6256      nsIContent::FromEventTargetOrNull(aMouseUpEvent->mClickTarget);
   6257  NS_ENSURE_STATE(clickTarget);
   6258 
   6259  // Fire click events if the event target is still available.
   6260  // Note that do not include the eMouseUp event's status since we ignore it
   6261  // for compatibility with the other browsers.
   6262  nsEventStatus status = nsEventStatus_eIgnore;
   6263  nsresult rv = DispatchClickEvents(presShell, aMouseUpEvent, &status,
   6264                                    clickTarget, aOverrideClickTarget);
   6265  if (NS_WARN_IF(NS_FAILED(rv))) {
   6266    return rv;
   6267  }
   6268 
   6269  // Do not do anything if preceding click events are consumed.
   6270  // Note that Chromium dispatches "paste" event and actually pates clipboard
   6271  // text into focused editor even if the preceding click events are consumed.
   6272  // However, this is different from our traditional behavior and does not
   6273  // conform to DOM events.  If we need to keep compatibility with Chromium,
   6274  // we should change it later.
   6275  if (status == nsEventStatus_eConsumeNoDefault) {
   6276    *aStatus = nsEventStatus_eConsumeNoDefault;
   6277    return NS_OK;
   6278  }
   6279 
   6280  // Handle middle click paste if it's enabled and the mouse button is middle.
   6281  if (aMouseUpEvent->mButton != MouseButton::eMiddle ||
   6282      !WidgetMouseEvent::IsMiddleClickPasteEnabled()) {
   6283    return NS_OK;
   6284  }
   6285  DebugOnly<nsresult> rvIgnored =
   6286      HandleMiddleClickPaste(presShell, aMouseUpEvent, &status, nullptr);
   6287  NS_WARNING_ASSERTION(NS_SUCCEEDED(rvIgnored),
   6288                       "Failed to paste for a middle click");
   6289 
   6290  // If new status is nsEventStatus_eConsumeNoDefault or
   6291  // nsEventStatus_eConsumeDoDefault, use it.
   6292  if (*aStatus != nsEventStatus_eConsumeNoDefault &&
   6293      (status == nsEventStatus_eConsumeNoDefault ||
   6294       status == nsEventStatus_eConsumeDoDefault)) {
   6295    *aStatus = status;
   6296  }
   6297 
   6298  // Don't return error even if middle mouse paste fails since we haven't
   6299  // handled it here.
   6300  return NS_OK;
   6301 }
   6302 
   6303 nsresult EventStateManager::DispatchClickEvents(
   6304    PresShell* aPresShell, WidgetMouseEvent* aMouseUpEvent,
   6305    nsEventStatus* aStatus, nsIContent* aClickTarget,
   6306    nsIContent* aOverrideClickTarget) {
   6307  MOZ_ASSERT(aPresShell);
   6308  MOZ_ASSERT(aMouseUpEvent);
   6309  MOZ_ASSERT(EventCausesClickEvents(*aMouseUpEvent));
   6310  MOZ_ASSERT(aStatus);
   6311  MOZ_ASSERT(aClickTarget || aOverrideClickTarget);
   6312 
   6313  bool notDispatchToContents =
   6314      (aMouseUpEvent->mButton == MouseButton::eMiddle ||
   6315       aMouseUpEvent->mButton == MouseButton::eSecondary);
   6316 
   6317  bool fireAuxClick = notDispatchToContents;
   6318 
   6319  AutoWeakFrame currentTarget = aClickTarget->GetPrimaryFrame();
   6320  nsresult rv = InitAndDispatchClickEvent(
   6321      aMouseUpEvent, aStatus, ePointerClick, aPresShell, aClickTarget,
   6322      currentTarget, notDispatchToContents, aOverrideClickTarget);
   6323  if (NS_WARN_IF(NS_FAILED(rv))) {
   6324    return rv;
   6325  }
   6326 
   6327  // Fire auxclick event if necessary.
   6328  if (fireAuxClick && *aStatus != nsEventStatus_eConsumeNoDefault &&
   6329      aClickTarget && aClickTarget->IsInComposedDoc()) {
   6330    rv = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, ePointerAuxClick,
   6331                                   aPresShell, aClickTarget, currentTarget,
   6332                                   false, aOverrideClickTarget);
   6333    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv),
   6334                         "Failed to dispatch ePointerAuxClick");
   6335  }
   6336 
   6337  // Fire double click event if click count is 2.
   6338  if (aMouseUpEvent->mClickCount == 2 && !fireAuxClick && aClickTarget &&
   6339      aClickTarget->IsInComposedDoc()) {
   6340    rv = InitAndDispatchClickEvent(aMouseUpEvent, aStatus, eMouseDoubleClick,
   6341                                   aPresShell, aClickTarget, currentTarget,
   6342                                   notDispatchToContents, aOverrideClickTarget);
   6343    if (NS_WARN_IF(NS_FAILED(rv))) {
   6344      return rv;
   6345    }
   6346  }
   6347 
   6348  return rv;
   6349 }
   6350 
   6351 nsresult EventStateManager::HandleMiddleClickPaste(
   6352    PresShell* aPresShell, WidgetMouseEvent* aMouseEvent,
   6353    nsEventStatus* aStatus, EditorBase* aEditorBase) {
   6354  MOZ_ASSERT(aPresShell);
   6355  MOZ_ASSERT(aMouseEvent);
   6356  MOZ_ASSERT((aMouseEvent->mMessage == ePointerAuxClick &&
   6357              aMouseEvent->mButton == MouseButton::eMiddle) ||
   6358             EventCausesClickEvents(*aMouseEvent));
   6359  MOZ_ASSERT(aStatus);
   6360  MOZ_ASSERT(*aStatus != nsEventStatus_eConsumeNoDefault);
   6361 
   6362  // Even if we're called twice or more for a mouse operation, we should
   6363  // handle only once.  Although mMultipleActionsPrevented may be set to
   6364  // true by different event handler in the future, we can use it for now.
   6365  if (aMouseEvent->mFlags.mMultipleActionsPrevented) {
   6366    return NS_OK;
   6367  }
   6368  aMouseEvent->mFlags.mMultipleActionsPrevented = true;
   6369 
   6370  RefPtr<Selection> selection;
   6371  if (aEditorBase) {
   6372    selection = aEditorBase->GetSelection();
   6373    if (NS_WARN_IF(!selection)) {
   6374      return NS_ERROR_FAILURE;
   6375    }
   6376  } else {
   6377    Document* document = aPresShell->GetDocument();
   6378    if (NS_WARN_IF(!document)) {
   6379      return NS_ERROR_FAILURE;
   6380    }
   6381    selection = nsCopySupport::GetSelectionForCopy(document);
   6382    if (NS_WARN_IF(!selection)) {
   6383      return NS_ERROR_FAILURE;
   6384    }
   6385 
   6386    const nsRange* range = selection->GetRangeAt(0);
   6387    if (range) {
   6388      nsINode* target = range->GetStartContainer();
   6389      if (target && target->OwnerDoc()->IsInChromeDocShell()) {
   6390        // In Chrome document, limit middle-click pasting to only the editor
   6391        // because it looks odd if pasting works in the focused editor when you
   6392        // middle-click toolbar or something which are far from the editor.
   6393        // However, as DevTools especially Web Console module assumes that paste
   6394        // event will be fired when middle-click even on not editor, don't limit
   6395        // it.
   6396        return NS_OK;
   6397      }
   6398    }
   6399  }
   6400 
   6401  // Don't modify selection here because we've already set caret to the point
   6402  // at "mousedown" event.
   6403 
   6404  nsIClipboard::ClipboardType clipboardType = nsIClipboard::kGlobalClipboard;
   6405  nsCOMPtr<nsIClipboard> clipboardService =
   6406      do_GetService("@mozilla.org/widget/clipboard;1");
   6407  if (clipboardService && clipboardService->IsClipboardTypeSupported(
   6408                              nsIClipboard::kSelectionClipboard)) {
   6409    clipboardType = nsIClipboard::kSelectionClipboard;
   6410  }
   6411 
   6412  RefPtr<DataTransfer> dataTransfer;
   6413  if (aEditorBase) {
   6414    // Create the same DataTransfer object here so we can share it between
   6415    // the clipboard event and the call to HandlePaste below. This prevents
   6416    // race conditions with Content Analysis on like we see in bug 1918027.
   6417    dataTransfer =
   6418        aEditorBase->CreateDataTransferForPaste(ePaste, clipboardType);
   6419  }
   6420  const auto clearDataTransfer = MakeScopeExit([&] {
   6421    if (dataTransfer) {
   6422      dataTransfer->ClearForPaste();
   6423    }
   6424  });
   6425 
   6426  // Fire ePaste event by ourselves since we need to dispatch "paste" event
   6427  // even if the middle click event was consumed for compatibility with
   6428  // Chromium.
   6429  if (!nsCopySupport::FireClipboardEvent(ePaste, Some(clipboardType),
   6430                                         aPresShell, selection, dataTransfer)) {
   6431    *aStatus = nsEventStatus_eConsumeNoDefault;
   6432    return NS_OK;
   6433  }
   6434 
   6435  // Although we've fired "paste" event, there is no editor to accept the
   6436  // clipboard content.
   6437  if (!aEditorBase) {
   6438    return NS_OK;
   6439  }
   6440 
   6441  // Check if the editor is still the good target to paste.
   6442  if (aEditorBase->Destroyed() || aEditorBase->IsReadonly()) {
   6443    // XXX Should we consume the event when the editor is readonly and/or
   6444    //     disabled?
   6445    return NS_OK;
   6446  }
   6447 
   6448  // The selection may have been modified during reflow.  Therefore, we
   6449  // should adjust event target to pass IsAcceptableInputEvent().
   6450  const nsRange* range = selection->GetRangeAt(0);
   6451  if (!range) {
   6452    return NS_OK;
   6453  }
   6454  WidgetMouseEvent mouseEvent(*aMouseEvent);
   6455  mouseEvent.mOriginalTarget = range->GetStartContainer();
   6456  if (NS_WARN_IF(!mouseEvent.mOriginalTarget) ||
   6457      !aEditorBase->IsAcceptableInputEvent(&mouseEvent)) {
   6458    return NS_OK;
   6459  }
   6460 
   6461  // If Control key is pressed, we should paste clipboard content as
   6462  // quotation.  Otherwise, paste it as is.
   6463  if (aMouseEvent->IsControl()) {
   6464    DebugOnly<nsresult> rv = aEditorBase->PasteAsQuotationAsAction(
   6465        clipboardType, EditorBase::DispatchPasteEvent::No, dataTransfer);
   6466    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to paste as quotation");
   6467  } else {
   6468    DebugOnly<nsresult> rv = aEditorBase->PasteAsAction(
   6469        clipboardType, EditorBase::DispatchPasteEvent::No, dataTransfer);
   6470    NS_WARNING_ASSERTION(NS_SUCCEEDED(rv), "Failed to paste");
   6471  }
   6472  *aStatus = nsEventStatus_eConsumeNoDefault;
   6473 
   6474  return NS_OK;
   6475 }
   6476 
   6477 void EventStateManager::ConsumeInteractionData(
   6478    Record<nsString, dom::InteractionData>& aInteractions) {
   6479  OnTypingInteractionEnded();
   6480 
   6481  aInteractions.Entries().Clear();
   6482  auto newEntry = aInteractions.Entries().AppendElement();
   6483  newEntry->mKey = u"Typing"_ns;
   6484  newEntry->mValue = gTypingInteraction;
   6485  gTypingInteraction = {};
   6486 }
   6487 
   6488 nsIFrame* EventStateManager::GetEventTarget() {
   6489  PresShell* presShell;
   6490  if (mCurrentTarget || !mPresContext ||
   6491      !(presShell = mPresContext->GetPresShell())) {
   6492    return mCurrentTarget;
   6493  }
   6494 
   6495  if (mCurrentTargetContent) {
   6496    mCurrentTarget = mPresContext->GetPrimaryFrameFor(mCurrentTargetContent);
   6497    if (mCurrentTarget) {
   6498      return mCurrentTarget;
   6499    }
   6500  }
   6501 
   6502  nsIFrame* frame = presShell->GetCurrentEventFrame();
   6503  return (mCurrentTarget = frame);
   6504 }
   6505 
   6506 already_AddRefed<nsIContent> EventStateManager::GetEventTargetContent(
   6507    WidgetEvent* aEvent) {
   6508  if (aEvent && (aEvent->mMessage == eFocus || aEvent->mMessage == eBlur)) {
   6509    nsCOMPtr<nsIContent> content = GetFocusedElement();
   6510    return content.forget();
   6511  }
   6512 
   6513  if (mCurrentTargetContent) {
   6514    nsCOMPtr<nsIContent> content = mCurrentTargetContent;
   6515    return content.forget();
   6516  }
   6517 
   6518  nsCOMPtr<nsIContent> content;
   6519  if (PresShell* presShell = mPresContext->GetPresShell()) {
   6520    content = presShell->GetEventTargetContent(aEvent);
   6521  }
   6522 
   6523  // Some events here may set mCurrentTarget but not set the corresponding
   6524  // event target in the PresShell.
   6525  if (!content && mCurrentTarget) {
   6526    content = mCurrentTarget->GetContentForEvent(aEvent);
   6527  }
   6528 
   6529  return content.forget();
   6530 }
   6531 
   6532 static Element* GetLabelTarget(nsIContent* aPossibleLabel) {
   6533  mozilla::dom::HTMLLabelElement* label =
   6534      mozilla::dom::HTMLLabelElement::FromNode(aPossibleLabel);
   6535  if (!label) return nullptr;
   6536 
   6537  return label->GetLabeledElement();
   6538 }
   6539 
   6540 /* static */
   6541 inline void EventStateManager::DoStateChange(Element* aElement,
   6542                                             ElementState aState,
   6543                                             bool aAddState) {
   6544  if (aAddState) {
   6545    aElement->AddStates(aState);
   6546  } else {
   6547    aElement->RemoveStates(aState);
   6548  }
   6549 }
   6550 
   6551 /* static */
   6552 inline void EventStateManager::DoStateChange(nsIContent* aContent,
   6553                                             ElementState aState,
   6554                                             bool aStateAdded) {
   6555  if (aContent->IsElement()) {
   6556    DoStateChange(aContent->AsElement(), aState, aStateAdded);
   6557  }
   6558 }
   6559 
   6560 /* static */
   6561 void EventStateManager::UpdateAncestorState(nsIContent* aStartNode,
   6562                                            nsIContent* aStopBefore,
   6563                                            ElementState aState,
   6564                                            bool aAddState) {
   6565  for (; aStartNode && aStartNode != aStopBefore;
   6566       aStartNode = aStartNode->GetFlattenedTreeParent()) {
   6567    // We might be starting with a non-element (e.g. a text node) and
   6568    // if someone is doing something weird might be ending with a
   6569    // non-element too (e.g. a document fragment)
   6570    if (!aStartNode->IsElement()) {
   6571      continue;
   6572    }
   6573    Element* element = aStartNode->AsElement();
   6574    DoStateChange(element, aState, aAddState);
   6575    Element* labelTarget = GetLabelTarget(element);
   6576    if (labelTarget) {
   6577      DoStateChange(labelTarget, aState, aAddState);
   6578    }
   6579  }
   6580 
   6581  if (aAddState) {
   6582    // We might be in a situation where a node was in hover both
   6583    // because it was hovered and because the label for it was
   6584    // hovered, and while we stopped hovering the node the label is
   6585    // still hovered.  Or we might have had two nested labels for the
   6586    // same node, and while one is no longer hovered the other still
   6587    // is.  In that situation, the label that's still hovered will be
   6588    // aStopBefore or some ancestor of it, and the call we just made
   6589    // to UpdateAncestorState with aAddState = false would have
   6590    // removed the hover state from the node.  But the node should
   6591    // still be in hover state.  To handle this situation we need to
   6592    // keep walking up the tree and any time we find a label mark its
   6593    // corresponding node as still in our state.
   6594    for (; aStartNode; aStartNode = aStartNode->GetFlattenedTreeParent()) {
   6595      if (!aStartNode->IsElement()) {
   6596        continue;
   6597      }
   6598 
   6599      Element* labelTarget = GetLabelTarget(aStartNode->AsElement());
   6600      if (labelTarget && !labelTarget->State().HasState(aState)) {
   6601        DoStateChange(labelTarget, aState, true);
   6602      }
   6603    }
   6604  }
   6605 }
   6606 
   6607 // static
   6608 bool CanContentHaveActiveState(nsIContent& aContent) {
   6609  // Editable content can never become active since their default actions
   6610  // are disabled.  Watch out for editable content in native anonymous
   6611  // subtrees though, as they belong to text controls.
   6612  return !aContent.IsEditable() || aContent.IsInNativeAnonymousSubtree();
   6613 }
   6614 
   6615 bool EventStateManager::SetContentState(nsIContent* aContent,
   6616                                        ElementState aState) {
   6617  MOZ_ASSERT(ManagesState(aState), "Unexpected state");
   6618 
   6619  nsCOMPtr<nsIContent> notifyContent1;
   6620  nsCOMPtr<nsIContent> notifyContent2;
   6621  bool updateAncestors;
   6622 
   6623  if (aState == ElementState::HOVER || aState == ElementState::ACTIVE) {
   6624    // Hover and active are hierarchical
   6625    updateAncestors = true;
   6626 
   6627    if (aState == ElementState::ACTIVE) {
   6628      if (aContent && !CanContentHaveActiveState(*aContent)) {
   6629        aContent = nullptr;
   6630      }
   6631      if (aContent != mActiveContent) {
   6632        notifyContent1 = aContent;
   6633        notifyContent2 = mActiveContent;
   6634        mActiveContent = aContent;
   6635      }
   6636    } else {
   6637      NS_ASSERTION(aState == ElementState::HOVER, "How did that happen?");
   6638      nsIContent* newHover;
   6639 
   6640      if (mPresContext->IsDynamic()) {
   6641        newHover = aContent;
   6642      } else {
   6643        NS_ASSERTION(!aContent || aContent->GetComposedDoc() ==
   6644                                      mPresContext->PresShell()->GetDocument(),
   6645                     "Unexpected document");
   6646        nsIFrame* frame = aContent ? aContent->GetPrimaryFrame() : nullptr;
   6647        if (frame && nsLayoutUtils::IsViewportScrollbarFrame(frame)) {
   6648          // The scrollbars of viewport should not ignore the hover state.
   6649          // Because they are *not* the content of the web page.
   6650          newHover = aContent;
   6651        } else {
   6652          // All contents of the web page should ignore the hover state.
   6653          newHover = nullptr;
   6654        }
   6655      }
   6656 
   6657      if (newHover != mHoverContent) {
   6658        notifyContent1 = newHover;
   6659        notifyContent2 = mHoverContent;
   6660        mHoverContent = newHover;
   6661      }
   6662    }
   6663  } else {
   6664    updateAncestors = false;
   6665    if (aState == ElementState::DRAGOVER) {
   6666      if (aContent != sDragOverContent) {
   6667        notifyContent1 = aContent;
   6668        notifyContent2 = sDragOverContent;
   6669        sDragOverContent = aContent;
   6670      }
   6671    } else if (aState == ElementState::URLTARGET) {
   6672      if (aContent != mURLTargetContent) {
   6673        notifyContent1 = aContent;
   6674        notifyContent2 = mURLTargetContent;
   6675        mURLTargetContent = aContent;
   6676      }
   6677    }
   6678  }
   6679 
   6680  // We need to keep track of which of notifyContent1 and notifyContent2 is
   6681  // getting the state set and which is getting it unset.  If both are
   6682  // non-null, then notifyContent1 is having the state set and notifyContent2
   6683  // is having it unset.  But if one of them is null, we need to keep track of
   6684  // the right thing for notifyContent1 explicitly.
   6685  bool content1StateSet = true;
   6686  if (!notifyContent1) {
   6687    // This is ok because FindCommonAncestor wouldn't find anything
   6688    // anyway if notifyContent1 is null.
   6689    notifyContent1 = notifyContent2;
   6690    notifyContent2 = nullptr;
   6691    content1StateSet = false;
   6692  }
   6693 
   6694  if (notifyContent1 && mPresContext) {
   6695    EnsureDocument(mPresContext);
   6696    if (mDocument) {
   6697      nsAutoScriptBlocker scriptBlocker;
   6698 
   6699      if (updateAncestors) {
   6700        nsCOMPtr<nsIContent> commonAncestor =
   6701            FindCommonAncestor(notifyContent1, notifyContent2);
   6702        if (notifyContent2) {
   6703          // It's very important to first notify the state removal and
   6704          // then the state addition, because due to labels it's
   6705          // possible that we're removing state from some element but
   6706          // then adding it again (say because mHoverContent changed
   6707          // from a control to its label).
   6708          UpdateAncestorState(notifyContent2, commonAncestor, aState, false);
   6709        }
   6710        UpdateAncestorState(notifyContent1, commonAncestor, aState,
   6711                            content1StateSet);
   6712      } else {
   6713        if (notifyContent2) {
   6714          DoStateChange(notifyContent2, aState, false);
   6715        }
   6716        DoStateChange(notifyContent1, aState, content1StateSet);
   6717      }
   6718    }
   6719  }
   6720 
   6721  return true;
   6722 }
   6723 
   6724 void EventStateManager::RemoveNodeFromChainIfNeeded(ElementState aState,
   6725                                                    nsIContent* aContentRemoved,
   6726                                                    bool aNotify) {
   6727  MOZ_ASSERT(aState == ElementState::HOVER || aState == ElementState::ACTIVE);
   6728  if (!aContentRemoved->IsElement() ||
   6729      !aContentRemoved->AsElement()->State().HasState(aState)) {
   6730    return;
   6731  }
   6732 
   6733  nsCOMPtr<nsIContent>& leaf =
   6734      aState == ElementState::HOVER ? mHoverContent : mActiveContent;
   6735 
   6736  MOZ_ASSERT(leaf);
   6737  // These two NS_ASSERTIONS below can fail for Shadow DOM sometimes, and it's
   6738  // not clear how to best handle it, see
   6739  // https://github.com/whatwg/html/issues/4795 and bug 1551621.
   6740  NS_ASSERTION(
   6741      nsContentUtils::ContentIsFlattenedTreeDescendantOf(leaf, aContentRemoved),
   6742      "Flat tree and active / hover chain got out of sync");
   6743 
   6744  nsIContent* newLeaf = aContentRemoved->GetFlattenedTreeParent();
   6745  MOZ_ASSERT(!newLeaf || newLeaf->IsElement());
   6746  NS_ASSERTION(!newLeaf || newLeaf->AsElement()->State().HasState(aState),
   6747               "State got out of sync because of shadow DOM");
   6748  if (aNotify) {
   6749    SetContentState(newLeaf, aState);
   6750  } else {
   6751    // We don't update the removed content's state here, since removing NAC
   6752    // happens from layout and we don't really want to notify at that point or
   6753    // what not.
   6754    //
   6755    // Also, NAC is not observable and NAC being removed will go away soon.
   6756    leaf = newLeaf;
   6757  }
   6758  MOZ_ASSERT(leaf == newLeaf || (aState == ElementState::ACTIVE && !leaf &&
   6759                                 !CanContentHaveActiveState(*newLeaf)));
   6760 }
   6761 
   6762 void EventStateManager::NativeAnonymousContentRemoved(nsIContent* aContent) {
   6763  MOZ_ASSERT(aContent->IsRootOfNativeAnonymousSubtree());
   6764  RemoveNodeFromChainIfNeeded(ElementState::HOVER, aContent, false);
   6765  RemoveNodeFromChainIfNeeded(ElementState::ACTIVE, aContent, false);
   6766 
   6767  nsCOMPtr<nsIContent>& lastLeftMouseDownContent =
   6768      mLastLeftMouseDownInfo.mLastMouseDownContent;
   6769  if (lastLeftMouseDownContent &&
   6770      nsContentUtils::ContentIsFlattenedTreeDescendantOf(
   6771          lastLeftMouseDownContent, aContent)) {
   6772    lastLeftMouseDownContent = aContent->GetFlattenedTreeParent();
   6773  }
   6774 
   6775  nsCOMPtr<nsIContent>& lastMiddleMouseDownContent =
   6776      mLastMiddleMouseDownInfo.mLastMouseDownContent;
   6777  if (lastMiddleMouseDownContent &&
   6778      nsContentUtils::ContentIsFlattenedTreeDescendantOf(
   6779          lastMiddleMouseDownContent, aContent)) {
   6780    lastMiddleMouseDownContent = aContent->GetFlattenedTreeParent();
   6781  }
   6782 
   6783  nsCOMPtr<nsIContent>& lastRightMouseDownContent =
   6784      mLastRightMouseDownInfo.mLastMouseDownContent;
   6785  if (lastRightMouseDownContent &&
   6786      nsContentUtils::ContentIsFlattenedTreeDescendantOf(
   6787          lastRightMouseDownContent, aContent)) {
   6788    lastRightMouseDownContent = aContent->GetFlattenedTreeParent();
   6789  }
   6790 }
   6791 
   6792 void EventStateManager::ContentInserted(nsIContent* aChild,
   6793                                        const ContentInsertInfo& aInfo) {
   6794  if (nsFocusManager* fm = nsFocusManager::GetFocusManager()) {
   6795    fm->ContentInserted(aChild, aInfo);
   6796  }
   6797 }
   6798 void EventStateManager::ContentAppended(nsIContent* aFirstNewContent,
   6799                                        const ContentAppendInfo& aInfo) {
   6800  if (nsFocusManager* fm = nsFocusManager::GetFocusManager()) {
   6801    fm->ContentAppended(aFirstNewContent, aInfo);
   6802  }
   6803 }
   6804 
   6805 void EventStateManager::ContentRemoved(Document* aDocument,
   6806                                       nsIContent* aContent,
   6807                                       const ContentRemoveInfo& aInfo) {
   6808  /*
   6809   * Anchor and area elements when focused or hovered might make the UI to show
   6810   * the current link. We want to make sure that the UI gets informed when they
   6811   * are actually removed from the DOM.
   6812   */
   6813  if (aContent->IsAnyOfHTMLElements(nsGkAtoms::a, nsGkAtoms::area) &&
   6814      (aContent->AsElement()->State().HasAtLeastOneOfStates(
   6815          ElementState::FOCUS | ElementState::HOVER))) {
   6816    Element* element = aContent->AsElement();
   6817    element->LeaveLink(element->GetPresContext(Element::eForComposedDoc));
   6818  }
   6819 
   6820  if (aContent->IsElement()) {
   6821    if (RefPtr<nsPresContext> presContext = mPresContext) {
   6822      IMEStateManager::OnRemoveContent(*presContext,
   6823                                       MOZ_KnownLive(*aContent->AsElement()));
   6824    }
   6825    WheelTransaction::OnRemoveElement(aContent);
   6826  }
   6827 
   6828  // inform the focus manager that the content is being removed. If this
   6829  // content is focused, the focus will be removed without firing events.
   6830  if (RefPtr<nsFocusManager> fm = nsFocusManager::GetFocusManager()) {
   6831    fm->ContentRemoved(aDocument, aContent, aInfo);
   6832  }
   6833 
   6834  RemoveNodeFromChainIfNeeded(ElementState::HOVER, aContent, true);
   6835  RemoveNodeFromChainIfNeeded(ElementState::ACTIVE, aContent, true);
   6836 
   6837  if (sDragOverContent &&
   6838      sDragOverContent->OwnerDoc() == aContent->OwnerDoc() &&
   6839      nsContentUtils::ContentIsFlattenedTreeDescendantOf(sDragOverContent,
   6840                                                         aContent)) {
   6841    sDragOverContent = nullptr;
   6842  }
   6843 
   6844  if (!aInfo.mNewParent) {
   6845    PointerEventHandler::ReleaseIfCaptureByDescendant(aContent);
   6846  }
   6847 
   6848  if (mMouseEnterLeaveHelper) {
   6849    const bool hadMouseOutTarget =
   6850        mMouseEnterLeaveHelper->GetOutEventTarget() != nullptr;
   6851    mMouseEnterLeaveHelper->ContentRemoved(*aContent);
   6852    // If we lose the mouseout target, we need to dispatch mouseover on an
   6853    // ancestor.  For ensuring the chance to do it before next user input, we
   6854    // need a synthetic mouse move.
   6855    if (hadMouseOutTarget && !mMouseEnterLeaveHelper->GetOutEventTarget()) {
   6856      if (PresShell* presShell =
   6857              mPresContext ? mPresContext->GetPresShell() : nullptr) {
   6858        presShell->SynthesizeMouseMove(false);
   6859      }
   6860    }
   6861  }
   6862  for (const auto& entry : mPointersEnterLeaveHelper) {
   6863    if (entry.GetData()) {
   6864      entry.GetData()->ContentRemoved(*aContent);
   6865    }
   6866  }
   6867 
   6868  NotifyContentWillBeRemovedForGesture(*aContent);
   6869 }
   6870 
   6871 void EventStateManager::TextControlRootWillBeRemoved(
   6872    TextControlElement& aTextControlElement) {
   6873  if (!mGestureDownInTextControl || !mGestureDownFrameOwner ||
   6874      !mGestureDownFrameOwner->IsInNativeAnonymousSubtree()) {
   6875    return;
   6876  }
   6877  // If we track gesture to start drag in aTextControlElement, we should keep
   6878  // tracking it with aTextContrlElement itself for now because this may be
   6879  // caused by reframing aTextControlElement which may not be intended by the
   6880  // user.
   6881  if (&aTextControlElement ==
   6882      mGestureDownFrameOwner
   6883          ->GetClosestNativeAnonymousSubtreeRootParentOrHost()) {
   6884    mGestureDownFrameOwner = &aTextControlElement;
   6885  }
   6886 }
   6887 
   6888 void EventStateManager::TextControlRootAdded(
   6889    Element& aAnonymousDivElement, TextControlElement& aTextControlElement) {
   6890  if (!mGestureDownInTextControl ||
   6891      mGestureDownFrameOwner != &aTextControlElement) {
   6892    return;
   6893  }
   6894  // If we track gesture to start drag in aTextControlElement, but the frame
   6895  // owner is the text control element itself, the anonymous nodes in it are
   6896  // recreated by a reframe.  If so, we should keep tracking it with the
   6897  // recreated native anonymous node.
   6898  mGestureDownFrameOwner =
   6899      aAnonymousDivElement.GetFirstChild()
   6900          ? aAnonymousDivElement.GetFirstChild()
   6901          : static_cast<nsIContent*>(&aAnonymousDivElement);
   6902 }
   6903 
   6904 bool EventStateManager::EventStatusOK(WidgetGUIEvent* aEvent) {
   6905  return !(aEvent->mMessage == eMouseDown &&
   6906           aEvent->AsMouseEvent()->mButton == MouseButton::ePrimary &&
   6907           !sNormalLMouseEventInProcess);
   6908 }
   6909 
   6910 //-------------------------------------------
   6911 // Access Key Registration
   6912 //-------------------------------------------
   6913 void EventStateManager::RegisterAccessKey(Element* aElement, uint32_t aKey) {
   6914  if (aElement && !mAccessKeys.Contains(aElement)) {
   6915    mAccessKeys.AppendObject(aElement);
   6916  }
   6917 }
   6918 
   6919 void EventStateManager::UnregisterAccessKey(Element* aElement, uint32_t aKey) {
   6920  if (aElement) {
   6921    mAccessKeys.RemoveObject(aElement);
   6922  }
   6923 }
   6924 
   6925 uint32_t EventStateManager::GetRegisteredAccessKey(Element* aElement) {
   6926  MOZ_ASSERT(aElement);
   6927 
   6928  if (!mAccessKeys.Contains(aElement)) {
   6929    return 0;
   6930  }
   6931 
   6932  nsAutoString accessKey;
   6933  aElement->GetAttr(nsGkAtoms::accesskey, accessKey);
   6934  return accessKey.First();
   6935 }
   6936 
   6937 PresShell* EventStateManager::GetRootPresShell() const {
   6938  PresShell* const presShell = GetPresShell();
   6939  return presShell ? presShell->GetRootPresShell() : nullptr;
   6940 }
   6941 
   6942 void EventStateManager::EnsureDocument(nsPresContext* aPresContext) {
   6943  if (!mDocument) mDocument = aPresContext->Document();
   6944 }
   6945 
   6946 void EventStateManager::FlushLayout(nsPresContext* aPresContext) {
   6947  MOZ_ASSERT(aPresContext, "nullptr ptr");
   6948  if (RefPtr<PresShell> presShell = aPresContext->GetPresShell()) {
   6949    presShell->FlushPendingNotifications(FlushType::InterruptibleLayout);
   6950  }
   6951 }
   6952 
   6953 Element* EventStateManager::GetFocusedElement() {
   6954  nsFocusManager* fm = nsFocusManager::GetFocusManager();
   6955  EnsureDocument(mPresContext);
   6956  if (!fm || !mDocument) {
   6957    return nullptr;
   6958  }
   6959 
   6960  nsCOMPtr<nsPIDOMWindowOuter> focusedWindow;
   6961  return nsFocusManager::GetFocusedDescendant(
   6962      mDocument->GetWindow(), nsFocusManager::eOnlyCurrentWindow,
   6963      getter_AddRefs(focusedWindow));
   6964 }
   6965 
   6966 //-------------------------------------------------------
   6967 // Return true if the docshell is visible
   6968 
   6969 bool EventStateManager::IsShellVisible(nsIDocShell* aShell) {
   6970  NS_ASSERTION(aShell, "docshell is null");
   6971 
   6972  nsCOMPtr<nsIBaseWindow> basewin = do_QueryInterface(aShell);
   6973  if (!basewin) return true;
   6974 
   6975  bool isVisible = true;
   6976  basewin->GetVisibility(&isVisible);
   6977 
   6978  // We should be doing some additional checks here so that
   6979  // we don't tab into hidden tabs of tabbrowser.  -bryner
   6980 
   6981  return isVisible;
   6982 }
   6983 
   6984 nsresult EventStateManager::DoContentCommandEvent(
   6985    WidgetContentCommandEvent* aEvent) {
   6986  EnsureDocument(mPresContext);
   6987  NS_ENSURE_TRUE(mDocument, NS_ERROR_FAILURE);
   6988  nsCOMPtr<nsPIDOMWindowOuter> window(mDocument->GetWindow());
   6989  NS_ENSURE_TRUE(window, NS_ERROR_FAILURE);
   6990 
   6991  nsCOMPtr<nsPIWindowRoot> root = window->GetTopWindowRoot();
   6992  NS_ENSURE_TRUE(root, NS_ERROR_FAILURE);
   6993  const char* cmd;
   6994  bool maybeNeedToHandleInRemote = false;
   6995  switch (aEvent->mMessage) {
   6996    case eContentCommandCut:
   6997      cmd = "cmd_cut";
   6998      maybeNeedToHandleInRemote = true;
   6999      break;
   7000    case eContentCommandCopy:
   7001      cmd = "cmd_copy";
   7002      maybeNeedToHandleInRemote = true;
   7003      break;
   7004    case eContentCommandPaste:
   7005      cmd = "cmd_paste";
   7006      maybeNeedToHandleInRemote = true;
   7007      break;
   7008    case eContentCommandDelete:
   7009      cmd = "cmd_delete";
   7010      maybeNeedToHandleInRemote = true;
   7011      break;
   7012    case eContentCommandUndo:
   7013      cmd = "cmd_undo";
   7014      maybeNeedToHandleInRemote = true;
   7015      break;
   7016    case eContentCommandRedo:
   7017      cmd = "cmd_redo";
   7018      maybeNeedToHandleInRemote = true;
   7019      break;
   7020    case eContentCommandPasteTransferable:
   7021      cmd = "cmd_pasteTransferable";
   7022      break;
   7023    case eContentCommandLookUpDictionary:
   7024      cmd = "cmd_lookUpDictionary";
   7025      break;
   7026    default:
   7027      return NS_ERROR_NOT_IMPLEMENTED;
   7028  }
   7029  if (XRE_IsParentProcess() && maybeNeedToHandleInRemote) {
   7030    if (BrowserParent* remote = BrowserParent::GetFocused()) {
   7031      if (!aEvent->mOnlyEnabledCheck) {
   7032        remote->SendSimpleContentCommandEvent(*aEvent);
   7033      }
   7034      // XXX The command may be disabled in the parent process.  Perhaps, we
   7035      // should set actual enabled state in the parent process here and there
   7036      // should be another bool flag which indicates whether the content is sent
   7037      // to a remote process.
   7038      aEvent->mIsEnabled = true;
   7039      aEvent->mSucceeded = true;
   7040      return NS_OK;
   7041    }
   7042  }
   7043  // If user tries to do something, user must try to do it in visible window.
   7044  // So, let's retrieve controller of visible window.
   7045  nsCOMPtr<nsIController> controller;
   7046  nsresult rv =
   7047      root->GetControllerForCommand(cmd, true, getter_AddRefs(controller));
   7048  NS_ENSURE_SUCCESS(rv, rv);
   7049  if (!controller) {
   7050    // When GetControllerForCommand succeeded but there is no controller, the
   7051    // command isn't supported.
   7052    aEvent->mIsEnabled = false;
   7053  } else {
   7054    bool canDoIt;
   7055    rv = controller->IsCommandEnabled(cmd, &canDoIt);
   7056    NS_ENSURE_SUCCESS(rv, rv);
   7057    aEvent->mIsEnabled = canDoIt;
   7058    if (canDoIt && !aEvent->mOnlyEnabledCheck) {
   7059      switch (aEvent->mMessage) {
   7060        case eContentCommandPasteTransferable: {
   7061          BrowserParent* remote = BrowserParent::GetFocused();
   7062          if (remote) {
   7063            IPCTransferable ipcTransferable;
   7064            nsContentUtils::TransferableToIPCTransferable(
   7065                aEvent->mTransferable, &ipcTransferable, false,
   7066                remote->Manager());
   7067            remote->SendPasteTransferable(std::move(ipcTransferable));
   7068            rv = NS_OK;
   7069          } else {
   7070            nsCOMPtr<nsICommandController> commandController =
   7071                do_QueryInterface(controller);
   7072            NS_ENSURE_STATE(commandController);
   7073 
   7074            RefPtr<nsCommandParams> params = new nsCommandParams();
   7075            rv = params->SetISupports("transferable", aEvent->mTransferable);
   7076            if (NS_WARN_IF(NS_FAILED(rv))) {
   7077              return rv;
   7078            }
   7079            rv = commandController->DoCommandWithParams(cmd, params);
   7080          }
   7081          break;
   7082        }
   7083 
   7084        case eContentCommandLookUpDictionary: {
   7085          nsCOMPtr<nsICommandController> commandController =
   7086              do_QueryInterface(controller);
   7087          if (NS_WARN_IF(!commandController)) {
   7088            return NS_ERROR_FAILURE;
   7089          }
   7090 
   7091          RefPtr<nsCommandParams> params = new nsCommandParams();
   7092          rv = params->SetInt("x", aEvent->mRefPoint.x);
   7093          if (NS_WARN_IF(NS_FAILED(rv))) {
   7094            return rv;
   7095          }
   7096 
   7097          rv = params->SetInt("y", aEvent->mRefPoint.y);
   7098          if (NS_WARN_IF(NS_FAILED(rv))) {
   7099            return rv;
   7100          }
   7101 
   7102          rv = commandController->DoCommandWithParams(cmd, params);
   7103          break;
   7104        }
   7105 
   7106        default:
   7107          rv = controller->DoCommand(cmd);
   7108          break;
   7109      }
   7110      NS_ENSURE_SUCCESS(rv, rv);
   7111    }
   7112  }
   7113  aEvent->mSucceeded = true;
   7114  return NS_OK;
   7115 }
   7116 
   7117 nsresult EventStateManager::DoContentCommandInsertTextEvent(
   7118    WidgetContentCommandEvent* aEvent) {
   7119  MOZ_ASSERT(aEvent);
   7120  MOZ_ASSERT(aEvent->mMessage == eContentCommandInsertText);
   7121  MOZ_DIAGNOSTIC_ASSERT(aEvent->mString.isSome());
   7122  MOZ_DIAGNOSTIC_ASSERT(!aEvent->mString.ref().IsEmpty());
   7123 
   7124  aEvent->mIsEnabled = false;
   7125  aEvent->mSucceeded = false;
   7126 
   7127  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
   7128 
   7129  if (XRE_IsParentProcess()) {
   7130    // Handle it in focused content process if there is.
   7131    if (BrowserParent* remote = BrowserParent::GetFocused()) {
   7132      if (!aEvent->mOnlyEnabledCheck) {
   7133        remote->SendInsertText(*aEvent);
   7134      }
   7135      // XXX The remote process may be not editable right now.  Therefore, this
   7136      // may be different from actual state in the remote process.
   7137      aEvent->mIsEnabled = true;
   7138      aEvent->mSucceeded = true;
   7139      return NS_OK;
   7140    }
   7141  }
   7142 
   7143  // If there is no active editor in this process, we should treat the command
   7144  // is disabled.
   7145  RefPtr<EditorBase> activeEditor =
   7146      nsContentUtils::GetActiveEditor(mPresContext);
   7147  if (!activeEditor) {
   7148    aEvent->mSucceeded = true;
   7149    return NS_OK;
   7150  }
   7151 
   7152  nsresult rv = activeEditor->InsertTextAsAction(aEvent->mString.ref());
   7153  aEvent->mIsEnabled = rv != NS_SUCCESS_DOM_NO_OPERATION;
   7154  aEvent->mSucceeded = NS_SUCCEEDED(rv);
   7155  return NS_OK;
   7156 }
   7157 
   7158 nsresult EventStateManager::DoContentCommandReplaceTextEvent(
   7159    WidgetContentCommandEvent* aEvent) {
   7160  MOZ_ASSERT(aEvent);
   7161  MOZ_ASSERT(aEvent->mMessage == eContentCommandReplaceText);
   7162  MOZ_DIAGNOSTIC_ASSERT(aEvent->mString.isSome());
   7163  MOZ_DIAGNOSTIC_ASSERT(!aEvent->mString.ref().IsEmpty());
   7164 
   7165  aEvent->mIsEnabled = false;
   7166  aEvent->mSucceeded = false;
   7167 
   7168  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
   7169 
   7170  if (XRE_IsParentProcess()) {
   7171    // Handle it in focused content process if there is.
   7172    if (BrowserParent* remote = BrowserParent::GetFocused()) {
   7173      if (!aEvent->mOnlyEnabledCheck) {
   7174        (void)remote->SendReplaceText(*aEvent);
   7175      }
   7176      // XXX The remote process may be not editable right now.  Therefore, this
   7177      // may be different from actual state in the remote process.
   7178      aEvent->mIsEnabled = true;
   7179      aEvent->mSucceeded = true;
   7180      return NS_OK;
   7181    }
   7182  }
   7183 
   7184  // If there is no active editor in this process, we should treat the command
   7185  // is disabled.
   7186  RefPtr<EditorBase> activeEditor =
   7187      nsContentUtils::GetActiveEditor(mPresContext);
   7188  if (NS_WARN_IF(!activeEditor)) {
   7189    aEvent->mSucceeded = true;
   7190    return NS_OK;
   7191  }
   7192 
   7193  RefPtr<TextComposition> composition =
   7194      IMEStateManager::GetTextCompositionFor(mPresContext);
   7195  if (NS_WARN_IF(composition)) {
   7196    // We don't support replace text action during composition.
   7197    aEvent->mSucceeded = true;
   7198    return NS_OK;
   7199  }
   7200 
   7201  ContentEventHandler handler(mPresContext);
   7202  RefPtr<nsRange> range = handler.GetRangeFromFlatTextOffset(
   7203      aEvent, aEvent->mSelection.mOffset,
   7204      aEvent->mSelection.mReplaceSrcString.Length());
   7205  if (NS_WARN_IF(!range)) {
   7206    aEvent->mSucceeded = false;
   7207    return NS_OK;
   7208  }
   7209 
   7210  // If original replacement text isn't matched with selection text, throws
   7211  // error.
   7212  nsAutoString targetStr;
   7213  nsresult rv = handler.GenerateFlatTextContent(range, targetStr);
   7214  if (NS_WARN_IF(NS_FAILED(rv))) {
   7215    aEvent->mSucceeded = false;
   7216    return NS_OK;
   7217  }
   7218  if (!aEvent->mSelection.mReplaceSrcString.Equals(targetStr)) {
   7219    aEvent->mSucceeded = false;
   7220    return NS_OK;
   7221  }
   7222 
   7223  rv = activeEditor->ReplaceTextAsAction(
   7224      aEvent->mString.ref(), range,
   7225      TextEditor::AllowBeforeInputEventCancelable::Yes,
   7226      aEvent->mSelection.mPreventSetSelection
   7227          ? EditorBase::PreventSetSelection::Yes
   7228          : EditorBase::PreventSetSelection::No);
   7229  if (NS_WARN_IF(NS_FAILED(rv))) {
   7230    aEvent->mSucceeded = false;
   7231    return NS_OK;
   7232  }
   7233 
   7234  aEvent->mIsEnabled = rv != NS_SUCCESS_DOM_NO_OPERATION;
   7235  aEvent->mSucceeded = true;
   7236  return NS_OK;
   7237 }
   7238 
   7239 nsresult EventStateManager::DoContentCommandScrollEvent(
   7240    WidgetContentCommandEvent* aEvent) {
   7241  NS_ENSURE_TRUE(mPresContext, NS_ERROR_NOT_AVAILABLE);
   7242  PresShell* presShell = mPresContext->GetPresShell();
   7243  NS_ENSURE_TRUE(presShell, NS_ERROR_NOT_AVAILABLE);
   7244  NS_ENSURE_TRUE(aEvent->mScroll.mAmount != 0, NS_ERROR_INVALID_ARG);
   7245 
   7246  ScrollUnit scrollUnit;
   7247  switch (aEvent->mScroll.mUnit) {
   7248    case WidgetContentCommandEvent::eCmdScrollUnit_Line:
   7249      scrollUnit = ScrollUnit::LINES;
   7250      break;
   7251    case WidgetContentCommandEvent::eCmdScrollUnit_Page:
   7252      scrollUnit = ScrollUnit::PAGES;
   7253      break;
   7254    case WidgetContentCommandEvent::eCmdScrollUnit_Whole:
   7255      scrollUnit = ScrollUnit::WHOLE;
   7256      break;
   7257    default:
   7258      return NS_ERROR_INVALID_ARG;
   7259  }
   7260 
   7261  aEvent->mSucceeded = true;
   7262 
   7263  ScrollContainerFrame* sf =
   7264      presShell->GetScrollContainerFrameToScroll(layers::EitherScrollDirection);
   7265  aEvent->mIsEnabled =
   7266      sf ? (aEvent->mScroll.mIsHorizontal ? WheelHandlingUtils::CanScrollOn(
   7267                                                sf, aEvent->mScroll.mAmount, 0)
   7268                                          : WheelHandlingUtils::CanScrollOn(
   7269                                                sf, 0, aEvent->mScroll.mAmount))
   7270         : false;
   7271 
   7272  if (!aEvent->mIsEnabled || aEvent->mOnlyEnabledCheck) {
   7273    return NS_OK;
   7274  }
   7275 
   7276  nsIntPoint pt(0, 0);
   7277  if (aEvent->mScroll.mIsHorizontal) {
   7278    pt.x = aEvent->mScroll.mAmount;
   7279  } else {
   7280    pt.y = aEvent->mScroll.mAmount;
   7281  }
   7282 
   7283  // The caller may want synchronous scrolling.
   7284  sf->ScrollBy(pt, scrollUnit, ScrollMode::Instant);
   7285  return NS_OK;
   7286 }
   7287 
   7288 void EventStateManager::SetActiveManager(EventStateManager* aNewESM,
   7289                                         nsIContent* aContent) {
   7290  if (sActiveESM && aNewESM != sActiveESM) {
   7291    sActiveESM->SetContentState(nullptr, ElementState::ACTIVE);
   7292  }
   7293  sActiveESM = aNewESM;
   7294  if (sActiveESM && aContent) {
   7295    sActiveESM->SetContentState(aContent, ElementState::ACTIVE);
   7296  }
   7297 }
   7298 
   7299 void EventStateManager::ClearGlobalActiveContent(EventStateManager* aClearer) {
   7300  if (aClearer) {
   7301    aClearer->SetContentState(nullptr, ElementState::ACTIVE);
   7302    if (sDragOverContent) {
   7303      aClearer->SetContentState(nullptr, ElementState::DRAGOVER);
   7304    }
   7305  }
   7306  if (sActiveESM && aClearer != sActiveESM) {
   7307    sActiveESM->SetContentState(nullptr, ElementState::ACTIVE);
   7308  }
   7309  sActiveESM = nullptr;
   7310 }
   7311 
   7312 /******************************************************************/
   7313 /* mozilla::EventStateManager::DeltaAccumulator                   */
   7314 /******************************************************************/
   7315 
   7316 void EventStateManager::DeltaAccumulator::InitLineOrPageDelta(
   7317    nsIFrame* aTargetFrame, EventStateManager* aESM, WidgetWheelEvent* aEvent) {
   7318  MOZ_ASSERT(aESM);
   7319  MOZ_ASSERT(aEvent);
   7320 
   7321  // Reset if the previous wheel event is too old.
   7322  if (!mLastTime.IsNull()) {
   7323    TimeDuration duration = TimeStamp::Now() - mLastTime;
   7324    if (duration.ToMilliseconds() >
   7325        StaticPrefs::mousewheel_transaction_timeout()) {
   7326      Reset();
   7327    }
   7328  }
   7329  // If we have accumulated delta,  we may need to reset it.
   7330  if (IsInTransaction()) {
   7331    // If wheel event type is changed, reset the values.
   7332    if (mHandlingDeltaMode != aEvent->mDeltaMode ||
   7333        mIsNoLineOrPageDeltaDevice != aEvent->mIsNoLineOrPageDelta) {
   7334      Reset();
   7335    } else {
   7336      // If the delta direction is changed, we should reset only the
   7337      // accumulated values.
   7338      if (mX && aEvent->mDeltaX && ((aEvent->mDeltaX > 0.0) != (mX > 0.0))) {
   7339        mX = mPendingScrollAmountX = 0.0;
   7340      }
   7341      if (mY && aEvent->mDeltaY && ((aEvent->mDeltaY > 0.0) != (mY > 0.0))) {
   7342        mY = mPendingScrollAmountY = 0.0;
   7343      }
   7344    }
   7345  }
   7346 
   7347  mHandlingDeltaMode = aEvent->mDeltaMode;
   7348  mIsNoLineOrPageDeltaDevice = aEvent->mIsNoLineOrPageDelta;
   7349 
   7350  {
   7351    ScrollContainerFrame* scrollTarget = aESM->ComputeScrollTarget(
   7352        aTargetFrame, aEvent, COMPUTE_DEFAULT_ACTION_TARGET);
   7353    nsPresContext* pc = scrollTarget ? scrollTarget->PresContext()
   7354                                     : aTargetFrame->PresContext();
   7355    aEvent->mScrollAmount = aESM->GetScrollAmount(pc, aEvent, scrollTarget);
   7356  }
   7357 
   7358  // If it's handling neither a device that does not provide line or page deltas
   7359  // nor delta values multiplied by prefs, we must not modify lineOrPageDelta
   7360  // values.
   7361  // TODO(emilio): Does this care about overridden scroll speed?
   7362  if (!mIsNoLineOrPageDeltaDevice &&
   7363      !EventStateManager::WheelPrefs::GetInstance()
   7364           ->NeedToComputeLineOrPageDelta(aEvent)) {
   7365    // Set the delta values to mX and mY.  They would be used when above block
   7366    // resets mX/mY/mPendingScrollAmountX/mPendingScrollAmountY if the direction
   7367    // is changed.
   7368    // NOTE: We shouldn't accumulate the delta values, it might could cause
   7369    //       overflow even though it's not a realistic situation.
   7370    if (aEvent->mDeltaX) {
   7371      mX = aEvent->mDeltaX;
   7372    }
   7373    if (aEvent->mDeltaY) {
   7374      mY = aEvent->mDeltaY;
   7375    }
   7376    mLastTime = TimeStamp::Now();
   7377    return;
   7378  }
   7379 
   7380  mX += aEvent->mDeltaX;
   7381  mY += aEvent->mDeltaY;
   7382 
   7383  if (mHandlingDeltaMode == WheelEvent_Binding::DOM_DELTA_PIXEL) {
   7384    // Records pixel delta values and init mLineOrPageDeltaX and
   7385    // mLineOrPageDeltaY for wheel events which are caused by pixel only
   7386    // devices.  Ignore mouse wheel transaction for computing this.  The
   7387    // lineOrPageDelta values will be used by dispatching legacy
   7388    // eMouseScrollEventClass (DOMMouseScroll) but not be used for scrolling
   7389    // of default action.  The transaction should be used only for the default
   7390    // action.
   7391    auto scrollAmountInCSSPixels =
   7392        CSSIntSize::FromAppUnitsRounded(aEvent->mScrollAmount);
   7393 
   7394    aEvent->mLineOrPageDeltaX = RoundDown(mX) / scrollAmountInCSSPixels.width;
   7395    aEvent->mLineOrPageDeltaY = RoundDown(mY) / scrollAmountInCSSPixels.height;
   7396 
   7397    mX -= aEvent->mLineOrPageDeltaX * scrollAmountInCSSPixels.width;
   7398    mY -= aEvent->mLineOrPageDeltaY * scrollAmountInCSSPixels.height;
   7399  } else {
   7400    aEvent->mLineOrPageDeltaX = RoundDown(mX);
   7401    aEvent->mLineOrPageDeltaY = RoundDown(mY);
   7402    mX -= aEvent->mLineOrPageDeltaX;
   7403    mY -= aEvent->mLineOrPageDeltaY;
   7404  }
   7405 
   7406  mLastTime = TimeStamp::Now();
   7407 }
   7408 
   7409 void EventStateManager::DeltaAccumulator::Reset() {
   7410  mX = mY = 0.0;
   7411  mPendingScrollAmountX = mPendingScrollAmountY = 0.0;
   7412  mHandlingDeltaMode = UINT32_MAX;
   7413  mIsNoLineOrPageDeltaDevice = false;
   7414 }
   7415 
   7416 nsIntPoint
   7417 EventStateManager::DeltaAccumulator::ComputeScrollAmountForDefaultAction(
   7418    WidgetWheelEvent* aEvent, const nsIntSize& aScrollAmountInDevPixels) {
   7419  MOZ_ASSERT(aEvent);
   7420 
   7421  DeltaValues acceleratedDelta = WheelTransaction::AccelerateWheelDelta(aEvent);
   7422 
   7423  nsIntPoint result(0, 0);
   7424  if (aEvent->mDeltaMode == WheelEvent_Binding::DOM_DELTA_PIXEL) {
   7425    mPendingScrollAmountX += acceleratedDelta.deltaX;
   7426    mPendingScrollAmountY += acceleratedDelta.deltaY;
   7427  } else {
   7428    mPendingScrollAmountX +=
   7429        aScrollAmountInDevPixels.width * acceleratedDelta.deltaX;
   7430    mPendingScrollAmountY +=
   7431        aScrollAmountInDevPixels.height * acceleratedDelta.deltaY;
   7432  }
   7433  result.x = RoundDown(mPendingScrollAmountX);
   7434  result.y = RoundDown(mPendingScrollAmountY);
   7435  mPendingScrollAmountX -= result.x;
   7436  mPendingScrollAmountY -= result.y;
   7437 
   7438  return result;
   7439 }
   7440 
   7441 /******************************************************************/
   7442 /* mozilla::EventStateManager::WheelPrefs                         */
   7443 /******************************************************************/
   7444 
   7445 // static
   7446 EventStateManager::WheelPrefs* EventStateManager::WheelPrefs::GetInstance() {
   7447  if (!sInstance) {
   7448    sInstance = new WheelPrefs();
   7449  }
   7450  return sInstance;
   7451 }
   7452 
   7453 // static
   7454 void EventStateManager::WheelPrefs::Shutdown() {
   7455  delete sInstance;
   7456  sInstance = nullptr;
   7457 }
   7458 
   7459 // static
   7460 void EventStateManager::WheelPrefs::OnPrefChanged(const char* aPrefName,
   7461                                                  void* aClosure) {
   7462  // forget all prefs, it's not problem for performance.
   7463  sInstance->Reset();
   7464  DeltaAccumulator::GetInstance()->Reset();
   7465 }
   7466 
   7467 EventStateManager::WheelPrefs::WheelPrefs() {
   7468  Reset();
   7469  Preferences::RegisterPrefixCallback(OnPrefChanged, "mousewheel.");
   7470 }
   7471 
   7472 EventStateManager::WheelPrefs::~WheelPrefs() {
   7473  Preferences::UnregisterPrefixCallback(OnPrefChanged, "mousewheel.");
   7474 }
   7475 
   7476 void EventStateManager::WheelPrefs::Reset() { memset(mInit, 0, sizeof(mInit)); }
   7477 
   7478 EventStateManager::WheelPrefs::Index EventStateManager::WheelPrefs::GetIndexFor(
   7479    const WidgetWheelEvent* aEvent) {
   7480  if (!aEvent) {
   7481    return INDEX_DEFAULT;
   7482  }
   7483 
   7484  Modifiers modifiers = (aEvent->mModifiers & (MODIFIER_ALT | MODIFIER_CONTROL |
   7485                                               MODIFIER_META | MODIFIER_SHIFT));
   7486 
   7487  switch (modifiers) {
   7488    case MODIFIER_ALT:
   7489      return INDEX_ALT;
   7490    case MODIFIER_CONTROL:
   7491      return INDEX_CONTROL;
   7492    case MODIFIER_META:
   7493      return INDEX_META;
   7494    case MODIFIER_SHIFT:
   7495      return INDEX_SHIFT;
   7496    default:
   7497      // If two or more modifier keys are pressed, we should use default
   7498      // settings.
   7499      return INDEX_DEFAULT;
   7500  }
   7501 }
   7502 
   7503 void EventStateManager::WheelPrefs::GetBasePrefName(
   7504    EventStateManager::WheelPrefs::Index aIndex, nsACString& aBasePrefName) {
   7505  aBasePrefName.AssignLiteral("mousewheel.");
   7506  switch (aIndex) {
   7507    case INDEX_ALT:
   7508      aBasePrefName.AppendLiteral("with_alt.");
   7509      break;
   7510    case INDEX_CONTROL:
   7511      aBasePrefName.AppendLiteral("with_control.");
   7512      break;
   7513    case INDEX_META:
   7514      aBasePrefName.AppendLiteral("with_meta.");
   7515      break;
   7516    case INDEX_SHIFT:
   7517      aBasePrefName.AppendLiteral("with_shift.");
   7518      break;
   7519    case INDEX_DEFAULT:
   7520    default:
   7521      aBasePrefName.AppendLiteral("default.");
   7522      break;
   7523  }
   7524 }
   7525 
   7526 void EventStateManager::WheelPrefs::Init(
   7527    EventStateManager::WheelPrefs::Index aIndex) {
   7528  if (mInit[aIndex]) {
   7529    return;
   7530  }
   7531  mInit[aIndex] = true;
   7532 
   7533  nsAutoCString basePrefName;
   7534  GetBasePrefName(aIndex, basePrefName);
   7535 
   7536  nsAutoCString prefNameX(basePrefName);
   7537  prefNameX.AppendLiteral("delta_multiplier_x");
   7538  mMultiplierX[aIndex] =
   7539      static_cast<double>(Preferences::GetInt(prefNameX.get(), 100)) / 100;
   7540 
   7541  nsAutoCString prefNameY(basePrefName);
   7542  prefNameY.AppendLiteral("delta_multiplier_y");
   7543  mMultiplierY[aIndex] =
   7544      static_cast<double>(Preferences::GetInt(prefNameY.get(), 100)) / 100;
   7545 
   7546  nsAutoCString prefNameZ(basePrefName);
   7547  prefNameZ.AppendLiteral("delta_multiplier_z");
   7548  mMultiplierZ[aIndex] =
   7549      static_cast<double>(Preferences::GetInt(prefNameZ.get(), 100)) / 100;
   7550 
   7551  nsAutoCString prefNameAction(basePrefName);
   7552  prefNameAction.AppendLiteral("action");
   7553  int32_t action = Preferences::GetInt(prefNameAction.get(), ACTION_SCROLL);
   7554  if (action < int32_t(ACTION_NONE) || action > int32_t(ACTION_LAST)) {
   7555    NS_WARNING("Unsupported action pref value, replaced with 'Scroll'.");
   7556    action = ACTION_SCROLL;
   7557  }
   7558  mActions[aIndex] = static_cast<Action>(action);
   7559 
   7560  // Compute action values overridden by .override_x pref.
   7561  // At present, override is possible only for the x-direction
   7562  // because this pref is introduced mainly for tilt wheels.
   7563  // Note that ACTION_HORIZONTALIZED_SCROLL isn't a valid value for this pref
   7564  // because it affects only to deltaY.
   7565  prefNameAction.AppendLiteral(".override_x");
   7566  int32_t actionOverrideX = Preferences::GetInt(prefNameAction.get(), -1);
   7567  if (actionOverrideX < -1 || actionOverrideX > int32_t(ACTION_LAST) ||
   7568      actionOverrideX == ACTION_HORIZONTALIZED_SCROLL) {
   7569    NS_WARNING("Unsupported action override pref value, didn't override.");
   7570    actionOverrideX = -1;
   7571  }
   7572  mOverriddenActionsX[aIndex] = (actionOverrideX == -1)
   7573                                    ? static_cast<Action>(action)
   7574                                    : static_cast<Action>(actionOverrideX);
   7575 }
   7576 
   7577 void EventStateManager::WheelPrefs::GetMultiplierForDeltaXAndY(
   7578    const WidgetWheelEvent* aEvent, Index aIndex, double* aMultiplierForDeltaX,
   7579    double* aMultiplierForDeltaY) {
   7580  *aMultiplierForDeltaX = mMultiplierX[aIndex];
   7581  *aMultiplierForDeltaY = mMultiplierY[aIndex];
   7582  // If the event has been horizontalized(I.e. treated as a horizontal wheel
   7583  // scroll for a vertical wheel scroll), then we should swap mMultiplierX and
   7584  // mMultiplierY. By doing this, multipliers will still apply to the delta
   7585  // values they origianlly corresponded to.
   7586  if (aEvent->mDeltaValuesHorizontalizedForDefaultHandler &&
   7587      ComputeActionFor(aEvent) == ACTION_HORIZONTALIZED_SCROLL) {
   7588    std::swap(*aMultiplierForDeltaX, *aMultiplierForDeltaY);
   7589  }
   7590 }
   7591 
   7592 void EventStateManager::WheelPrefs::ApplyUserPrefsToDelta(
   7593    WidgetWheelEvent* aEvent) {
   7594  if (aEvent->mCustomizedByUserPrefs) {
   7595    return;
   7596  }
   7597 
   7598  Index index = GetIndexFor(aEvent);
   7599  Init(index);
   7600 
   7601  double multiplierForDeltaX = 1.0, multiplierForDeltaY = 1.0;
   7602  GetMultiplierForDeltaXAndY(aEvent, index, &multiplierForDeltaX,
   7603                             &multiplierForDeltaY);
   7604  aEvent->mDeltaX *= multiplierForDeltaX;
   7605  aEvent->mDeltaY *= multiplierForDeltaY;
   7606  aEvent->mDeltaZ *= mMultiplierZ[index];
   7607 
   7608  // If the multiplier is 1.0 or -1.0, i.e., it doesn't change the absolute
   7609  // value, we should use lineOrPageDelta values which were set by widget.
   7610  // Otherwise, we need to compute them from accumulated delta values.
   7611  if (!NeedToComputeLineOrPageDelta(aEvent)) {
   7612    aEvent->mLineOrPageDeltaX *= static_cast<int32_t>(multiplierForDeltaX);
   7613    aEvent->mLineOrPageDeltaY *= static_cast<int32_t>(multiplierForDeltaY);
   7614  } else {
   7615    aEvent->mLineOrPageDeltaX = 0;
   7616    aEvent->mLineOrPageDeltaY = 0;
   7617  }
   7618 
   7619  aEvent->mCustomizedByUserPrefs =
   7620      ((mMultiplierX[index] != 1.0) || (mMultiplierY[index] != 1.0) ||
   7621       (mMultiplierZ[index] != 1.0));
   7622 }
   7623 
   7624 void EventStateManager::WheelPrefs::CancelApplyingUserPrefsFromOverflowDelta(
   7625    WidgetWheelEvent* aEvent) {
   7626  Index index = GetIndexFor(aEvent);
   7627  Init(index);
   7628 
   7629  // XXX If the multiplier pref value is negative, the scroll direction was
   7630  //     changed and caused to scroll different direction.  In such case,
   7631  //     this method reverts the sign of overflowDelta.  Does it make widget
   7632  //     happy?  Although, widget can know the pref applied delta values by
   7633  //     referrencing the deltaX and deltaY of the event.
   7634 
   7635  double multiplierForDeltaX = 1.0, multiplierForDeltaY = 1.0;
   7636  GetMultiplierForDeltaXAndY(aEvent, index, &multiplierForDeltaX,
   7637                             &multiplierForDeltaY);
   7638  if (multiplierForDeltaX) {
   7639    aEvent->mOverflowDeltaX /= multiplierForDeltaX;
   7640  }
   7641  if (multiplierForDeltaY) {
   7642    aEvent->mOverflowDeltaY /= multiplierForDeltaY;
   7643  }
   7644 }
   7645 
   7646 EventStateManager::WheelPrefs::Action
   7647 EventStateManager::WheelPrefs::ComputeActionFor(
   7648    const WidgetWheelEvent* aEvent) {
   7649  Index index = GetIndexFor(aEvent);
   7650  Init(index);
   7651 
   7652  bool deltaXPreferred = (Abs(aEvent->mDeltaX) > Abs(aEvent->mDeltaY) &&
   7653                          Abs(aEvent->mDeltaX) > Abs(aEvent->mDeltaZ));
   7654  Action* actions = deltaXPreferred ? mOverriddenActionsX : mActions;
   7655  if (actions[index] == ACTION_NONE || actions[index] == ACTION_SCROLL ||
   7656      actions[index] == ACTION_HORIZONTALIZED_SCROLL) {
   7657    return actions[index];
   7658  }
   7659 
   7660  // Momentum events shouldn't run special actions.
   7661  if (aEvent->mIsMomentum) {
   7662    // Use the default action.  Note that user might kill the wheel scrolling.
   7663    Init(INDEX_DEFAULT);
   7664    if (actions[INDEX_DEFAULT] == ACTION_SCROLL ||
   7665        actions[INDEX_DEFAULT] == ACTION_HORIZONTALIZED_SCROLL) {
   7666      return actions[INDEX_DEFAULT];
   7667    }
   7668    return ACTION_NONE;
   7669  }
   7670 
   7671  return actions[index];
   7672 }
   7673 
   7674 bool EventStateManager::WheelPrefs::NeedToComputeLineOrPageDelta(
   7675    const WidgetWheelEvent* aEvent) {
   7676  Index index = GetIndexFor(aEvent);
   7677  Init(index);
   7678 
   7679  return (mMultiplierX[index] != 1.0 && mMultiplierX[index] != -1.0) ||
   7680         (mMultiplierY[index] != 1.0 && mMultiplierY[index] != -1.0);
   7681 }
   7682 
   7683 void EventStateManager::WheelPrefs::GetUserPrefsForEvent(
   7684    const WidgetWheelEvent* aEvent, double* aOutMultiplierX,
   7685    double* aOutMultiplierY) {
   7686  Index index = GetIndexFor(aEvent);
   7687  Init(index);
   7688 
   7689  double multiplierForDeltaX = 1.0, multiplierForDeltaY = 1.0;
   7690  GetMultiplierForDeltaXAndY(aEvent, index, &multiplierForDeltaX,
   7691                             &multiplierForDeltaY);
   7692  *aOutMultiplierX = multiplierForDeltaX;
   7693  *aOutMultiplierY = multiplierForDeltaY;
   7694 }
   7695 
   7696 // static
   7697 Maybe<layers::APZWheelAction> EventStateManager::APZWheelActionFor(
   7698    const WidgetWheelEvent* aEvent) {
   7699  if (aEvent->mMessage != eWheel) {
   7700    return Nothing();
   7701  }
   7702  WheelPrefs::Action action =
   7703      WheelPrefs::GetInstance()->ComputeActionFor(aEvent);
   7704  switch (action) {
   7705    case WheelPrefs::ACTION_SCROLL:
   7706    case WheelPrefs::ACTION_HORIZONTALIZED_SCROLL:
   7707      return Some(layers::APZWheelAction::Scroll);
   7708    case WheelPrefs::ACTION_PINCH_ZOOM:
   7709      return Some(layers::APZWheelAction::PinchZoom);
   7710    default:
   7711      return Nothing();
   7712  }
   7713 }
   7714 
   7715 // static
   7716 WheelDeltaAdjustmentStrategy EventStateManager::GetWheelDeltaAdjustmentStrategy(
   7717    const WidgetWheelEvent& aEvent) {
   7718  if (aEvent.mMessage != eWheel) {
   7719    return WheelDeltaAdjustmentStrategy::eNone;
   7720  }
   7721  switch (WheelPrefs::GetInstance()->ComputeActionFor(&aEvent)) {
   7722    case WheelPrefs::ACTION_SCROLL:
   7723      if (StaticPrefs::mousewheel_autodir_enabled() && 0 == aEvent.mDeltaZ) {
   7724        if (StaticPrefs::mousewheel_autodir_honourroot()) {
   7725          return WheelDeltaAdjustmentStrategy::eAutoDirWithRootHonour;
   7726        }
   7727        return WheelDeltaAdjustmentStrategy::eAutoDir;
   7728      }
   7729      return WheelDeltaAdjustmentStrategy::eNone;
   7730    case WheelPrefs::ACTION_HORIZONTALIZED_SCROLL:
   7731      return WheelDeltaAdjustmentStrategy::eHorizontalize;
   7732    default:
   7733      break;
   7734  }
   7735  return WheelDeltaAdjustmentStrategy::eNone;
   7736 }
   7737 
   7738 void EventStateManager::GetUserPrefsForWheelEvent(
   7739    const WidgetWheelEvent* aEvent, double* aOutMultiplierX,
   7740    double* aOutMultiplierY) {
   7741  WheelPrefs::GetInstance()->GetUserPrefsForEvent(aEvent, aOutMultiplierX,
   7742                                                  aOutMultiplierY);
   7743 }
   7744 
   7745 bool EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedX(
   7746    const WidgetWheelEvent* aEvent) {
   7747  Index index = GetIndexFor(aEvent);
   7748  Init(index);
   7749  return Abs(mMultiplierX[index]) >=
   7750         MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
   7751 }
   7752 
   7753 bool EventStateManager::WheelPrefs::IsOverOnePageScrollAllowedY(
   7754    const WidgetWheelEvent* aEvent) {
   7755  Index index = GetIndexFor(aEvent);
   7756  Init(index);
   7757  return Abs(mMultiplierY[index]) >=
   7758         MIN_MULTIPLIER_VALUE_ALLOWING_OVER_ONE_PAGE_SCROLL;
   7759 }
   7760 
   7761 void EventStateManager::UpdateGestureContent(nsIContent* aContent) {
   7762  mGestureDownContent = aContent;
   7763  mGestureDownFrameOwner = aContent;
   7764  mGestureDownInTextControl =
   7765      aContent && aContent->IsInNativeAnonymousSubtree() &&
   7766      TextControlElement::FromNodeOrNull(
   7767          aContent->GetClosestNativeAnonymousSubtreeRootParentOrHost());
   7768 }
   7769 
   7770 void EventStateManager::NotifyContentWillBeRemovedForGesture(
   7771    nsIContent& aContent) {
   7772  if (!mGestureDownContent) {
   7773    return;
   7774  }
   7775 
   7776  if (!nsContentUtils::ContentIsFlattenedTreeDescendantOf(mGestureDownContent,
   7777                                                          &aContent)) {
   7778    return;
   7779  }
   7780 
   7781  UpdateGestureContent(aContent.GetFlattenedTreeParent());
   7782 }
   7783 
   7784 }  // namespace mozilla