tor-browser

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

EventListenerManager.cpp (81156B)


      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 // Microsoft's API Name hackery sucks
      8 #undef CreateEvent
      9 
     10 #include "mozilla/EventListenerManager.h"
     11 
     12 #include "EventListenerService.h"
     13 #include "js/ColumnNumber.h"      // JS::ColumnNumberOneOrigin
     14 #include "js/EnvironmentChain.h"  // JS::EnvironmentChain
     15 #include "js/loader/LoadedScript.h"
     16 #include "js/loader/ScriptFetchOptions.h"
     17 #include "mozilla/Assertions.h"
     18 #include "mozilla/BasicEvents.h"
     19 #include "mozilla/BinarySearch.h"
     20 #include "mozilla/CycleCollectedJSRuntime.h"
     21 #include "mozilla/DOMEventTargetHelper.h"
     22 #include "mozilla/EventDispatcher.h"
     23 #include "mozilla/HalSensor.h"
     24 #include "mozilla/JSEventHandler.h"
     25 #include "mozilla/Maybe.h"
     26 #include "mozilla/MemoryReporting.h"
     27 #include "mozilla/Preferences.h"
     28 #include "mozilla/PresShell.h"
     29 #include "mozilla/ScopeExit.h"
     30 #include "mozilla/StaticPrefs_dom.h"
     31 #include "mozilla/TimeStamp.h"
     32 #include "mozilla/dom/AbortSignal.h"
     33 #include "mozilla/dom/BindingUtils.h"
     34 #include "mozilla/dom/ChromeUtils.h"
     35 #include "mozilla/dom/Document.h"
     36 #include "mozilla/dom/Element.h"
     37 #include "mozilla/dom/Event.h"
     38 #include "mozilla/dom/EventCallbackDebuggerNotification.h"
     39 #include "mozilla/dom/EventTargetBinding.h"
     40 #include "mozilla/dom/PolicyContainer.h"
     41 #include "mozilla/dom/PopupBlocker.h"
     42 #include "mozilla/dom/RequestBinding.h"
     43 #include "mozilla/dom/ScriptLoader.h"
     44 #include "mozilla/dom/ScriptSettings.h"
     45 #include "mozilla/dom/TouchEvent.h"
     46 #include "mozilla/dom/UserActivation.h"
     47 #include "nsCOMPtr.h"
     48 #include "nsContentUtils.h"
     49 #include "nsDOMCID.h"
     50 #include "nsDisplayList.h"
     51 #include "nsError.h"
     52 #include "nsGenericHTMLElement.h"
     53 #include "nsGkAtoms.h"
     54 #include "nsIContent.h"
     55 #include "nsIContentSecurityPolicy.h"
     56 #include "nsIFrame.h"
     57 #include "nsIScriptGlobalObject.h"
     58 #include "nsISupports.h"
     59 #include "nsJSUtils.h"
     60 #include "nsNameSpaceManager.h"
     61 #include "nsPIDOMWindow.h"
     62 #include "nsPIWindowRoot.h"
     63 #include "nsPrintfCString.h"
     64 #include "nsSandboxFlags.h"
     65 #include "nsScreen.h"
     66 #include "xpcpublic.h"
     67 
     68 namespace mozilla {
     69 
     70 using namespace dom;
     71 using namespace hal;
     72 
     73 class ListenerMapEntryComparator {
     74 public:
     75  explicit ListenerMapEntryComparator(nsAtom* aTarget)
     76      : mAddressOfEventType(reinterpret_cast<uintptr_t>(aTarget)) {}
     77 
     78  int operator()(
     79      const EventListenerManager::EventListenerMapEntry& aEntry) const {
     80    uintptr_t value = reinterpret_cast<uintptr_t>(aEntry.mTypeAtom.get());
     81    if (mAddressOfEventType == value) {
     82      return 0;
     83    }
     84 
     85    if (mAddressOfEventType < value) {
     86      return -1;
     87    }
     88 
     89    return 1;
     90  }
     91 
     92 private:
     93  const uintptr_t mAddressOfEventType;  // the address of the atom, can be 0
     94 };
     95 
     96 uint32_t EventListenerManager::sMainThreadCreatedCount = 0;
     97 
     98 EventListenerManagerBase::EventListenerManagerBase()
     99    : mMayHaveDOMActivateEventListener(false),
    100      mMayHaveCapturingListeners(false),
    101      mMayHaveSystemGroupListeners(false),
    102      mMayHaveTouchEventListener(false),
    103      mMayHaveMouseEnterLeaveEventListener(false),
    104      mMayHavePointerEnterLeaveEventListener(false),
    105      mMayHavePointerRawUpdateEventListener(false),
    106      mMayHaveSelectionChangeEventListener(false),
    107      mMayHaveFormSelectEventListener(false),
    108      mMayHaveTransitionEventListener(false),
    109      mMayHaveSMILTimeEventListener(false),
    110      mClearingListeners(false),
    111      mIsMainThreadELM(NS_IsMainThread()),
    112      mMayHaveListenersForUntrustedEvents(false) {
    113  ClearNoListenersForEvents();
    114  static_assert(sizeof(EventListenerManagerBase) == sizeof(uint64_t),
    115                "Keep the size of EventListenerManagerBase size compact!");
    116 }
    117 
    118 EventListenerManager::EventListenerManager(EventTarget* aTarget)
    119    : mTarget(aTarget) {
    120  NS_ASSERTION(aTarget, "unexpected null pointer");
    121 
    122  if (mIsMainThreadELM) {
    123    mRefCnt.SetIsOnMainThread();
    124    ++sMainThreadCreatedCount;
    125  }
    126 }
    127 
    128 EventListenerManager::~EventListenerManager() {
    129  // If your code fails this assertion, a possible reason is that
    130  // a class did not call our Disconnect() manually. Note that
    131  // this class can have Disconnect called in one of two ways:
    132  // if it is part of a cycle, then in Unlink() (such a cycle
    133  // would be with one of the listeners, not mTarget which is weak).
    134  // If not part of a cycle, then Disconnect must be called manually,
    135  // typically from the destructor of the owner class (mTarget).
    136  // XXX azakai: Is there any reason to not just call Disconnect
    137  //             from right here, if not previously called?
    138  NS_ASSERTION(!mTarget, "didn't call Disconnect");
    139  RemoveAllListenersSilently();
    140 }
    141 
    142 void EventListenerManager::RemoveAllListenersSilently() {
    143  if (mClearingListeners) {
    144    return;
    145  }
    146  mClearingListeners = true;
    147  mListenerMap.Clear();
    148  mClearingListeners = false;
    149 }
    150 
    151 inline void ImplCycleCollectionTraverse(
    152    nsCycleCollectionTraversalCallback& aCallback,
    153    EventListenerManager::EventListenerMap& aField, const char* aName,
    154    uint32_t aFlags = 0) {
    155  if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
    156    nsAutoCString name;
    157    name.AppendASCII(aName);
    158    name.AppendLiteral(" mEntries[i] event=");
    159    size_t entryPrefixLen = name.Length();
    160    for (const auto& entry : aField.mEntries) {
    161      if (entry.mTypeAtom) {
    162        name.Replace(entryPrefixLen, name.Length() - entryPrefixLen,
    163                     nsAtomCString(entry.mTypeAtom));
    164      } else {
    165        name.Replace(entryPrefixLen, name.Length() - entryPrefixLen,
    166                     "(all)"_ns);
    167      }
    168      ImplCycleCollectionTraverse(aCallback, *entry.mListeners, name.get());
    169    }
    170  } else {
    171    for (const auto& entry : aField.mEntries) {
    172      ImplCycleCollectionTraverse(aCallback, *entry.mListeners,
    173                                  ".mEntries[i].mListeners");
    174    }
    175  }
    176 }
    177 
    178 inline void ImplCycleCollectionTraverse(
    179    nsCycleCollectionTraversalCallback& aCallback,
    180    EventListenerManager::Listener& aField, const char* aName,
    181    unsigned aFlags) {
    182  if (MOZ_UNLIKELY(aCallback.WantDebugInfo())) {
    183    nsAutoCString name;
    184    name.AppendASCII(aName);
    185    name.AppendLiteral(" listenerType=");
    186    name.AppendInt(aField.mListenerType);
    187    name.AppendLiteral(" ");
    188    CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(),
    189                             name.get(), aFlags);
    190  } else {
    191    CycleCollectionNoteChild(aCallback, aField.mListener.GetISupports(), aName,
    192                             aFlags);
    193  }
    194 
    195  CycleCollectionNoteChild(aCallback, aField.mSignalFollower.get(),
    196                           "mSignalFollower", aFlags);
    197 }
    198 
    199 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager)
    200 
    201 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(EventListenerManager)
    202  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListenerMap);
    203 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
    204 
    205 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(EventListenerManager)
    206  tmp->Disconnect();
    207 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
    208 
    209 nsPIDOMWindowInner* EventListenerManager::GetInnerWindowForTarget() {
    210  if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
    211    // XXX sXBL/XBL2 issue -- do we really want the owner here?  What
    212    // if that's the XBL document?
    213    return node->OwnerDoc()->GetInnerWindow();
    214  }
    215 
    216  nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
    217  return window;
    218 }
    219 
    220 already_AddRefed<nsPIDOMWindowInner>
    221 EventListenerManager::GetTargetAsInnerWindow() const {
    222  nsCOMPtr<nsPIDOMWindowInner> window =
    223      nsPIDOMWindowInner::FromEventTargetOrNull(mTarget);
    224  return window.forget();
    225 }
    226 
    227 static mozilla::LazyLogModule sSlowChromeLog("SlowChromeEvent");
    228 
    229 static void LogForChromeEvent(nsPIDOMWindowInner* aWindow, const char* aMsg) {
    230  if (!MOZ_LOG_TEST(sSlowChromeLog, LogLevel::Info)) {
    231    return;
    232  }
    233  if (!nsContentUtils::IsChromeDoc(aWindow->GetExtantDoc())) {
    234    return;
    235  }
    236 
    237  if (JSContext* cx = nsContentUtils::GetCurrentJSContext()) {
    238    JS::AutoFilename filename;
    239    uint32_t lineNum = 0;
    240    JS::ColumnNumberOneOrigin columnNum;
    241    JS::DescribeScriptedCaller(&filename, cx, &lineNum, &columnNum);
    242    MOZ_LOG(sSlowChromeLog, LogLevel::Info,
    243            ("%s %s:%u:%u", aMsg, filename.get(), lineNum,
    244             columnNum.oneOriginValue()));
    245  } else {
    246    MOZ_LOG(sSlowChromeLog, LogLevel::Info, ("%s", aMsg));
    247  }
    248 }
    249 
    250 void EventListenerManager::AddEventListenerInternal(
    251    EventListenerHolder aListenerHolder, EventMessage aEventMessage,
    252    nsAtom* aTypeAtom, const EventListenerFlags& aFlags, bool aHandler,
    253    bool aAllEvents, AbortSignal* aSignal) {
    254  MOZ_ASSERT((aEventMessage && aTypeAtom) || aAllEvents,  // all-events listener
    255             "Missing type");
    256  MOZ_ASSERT_IF(
    257      aEventMessage != eUnidentifiedEvent && !aAllEvents,
    258      aTypeAtom == nsContentUtils::GetEventTypeFromMessage(aEventMessage));
    259 
    260  if (!aListenerHolder || mClearingListeners) {
    261    return;
    262  }
    263 
    264  if (aSignal && aSignal->Aborted()) {
    265    return;
    266  }
    267 
    268  // Since there is no public API to call us with an EventListenerHolder, we
    269  // know that there's an EventListenerHolder on the stack holding a strong ref
    270  // to the listener.
    271 
    272  RefPtr<ListenerArray> listeners =
    273      aAllEvents ? mListenerMap.GetOrCreateListenersForAllEvents()
    274                 : mListenerMap.GetOrCreateListenersForType(aTypeAtom);
    275 
    276  for (const Listener& listener : listeners->NonObservingRange()) {
    277    // mListener == aListenerHolder is the last one, since it can be a bit slow.
    278    if (listener.mListenerIsHandler == aHandler &&
    279        listener.mFlags.EqualsForAddition(aFlags) &&
    280        listener.mListener == aListenerHolder) {
    281      return;
    282    }
    283  }
    284 
    285  ClearNoListenersForEvents();
    286  mNoListenerForEventAtom = nullptr;
    287 
    288  Listener* listener = listeners->AppendElement();
    289  listener->mFlags = aFlags;
    290  listener->mListenerIsHandler = aHandler;
    291  listener->mHandlerIsString = false;
    292  listener->mAllEvents = aAllEvents;
    293 
    294  if (listener->mFlags.mAllowUntrustedEvents) {
    295    mMayHaveListenersForUntrustedEvents = true;
    296  }
    297 
    298  // Detect the type of event listener.
    299  if (aFlags.mListenerIsJSListener) {
    300    MOZ_ASSERT(!aListenerHolder.HasWebIDLCallback());
    301    listener->mListenerType = Listener::eJSEventListener;
    302  } else if (aListenerHolder.HasWebIDLCallback()) {
    303    listener->mListenerType = Listener::eWebIDLListener;
    304  } else {
    305    listener->mListenerType = Listener::eNativeListener;
    306  }
    307  listener->mListener = std::move(aListenerHolder);
    308 
    309  if (aSignal) {
    310    listener->mSignalFollower =
    311        new ListenerSignalFollower(this, listener, aTypeAtom);
    312    listener->mSignalFollower->Follow(aSignal);
    313  }
    314 
    315  if (aFlags.mInSystemGroup) {
    316    mMayHaveSystemGroupListeners = true;
    317  }
    318  if (aFlags.mCapture) {
    319    mMayHaveCapturingListeners = true;
    320  }
    321 
    322  // Events which are not supported in the running environment is mapped to
    323  // eUnidentifiedEvent.  Then, we need to consider the proper event message
    324  // with comparing the atom.
    325  {
    326    EventMessage resolvedEventMessage = aEventMessage;
    327    if (resolvedEventMessage == eUnidentifiedEvent && aTypeAtom->IsStatic()) {
    328      // TouchEvents are registered only when
    329      // nsContentUtils::InitializeTouchEventTable() is called.
    330      if (aTypeAtom == nsGkAtoms::ontouchstart) {
    331        resolvedEventMessage = eTouchStart;
    332      } else if (aTypeAtom == nsGkAtoms::ontouchend) {
    333        resolvedEventMessage = eTouchEnd;
    334      } else if (aTypeAtom == nsGkAtoms::ontouchmove) {
    335        resolvedEventMessage = eTouchMove;
    336      } else if (aTypeAtom == nsGkAtoms::ontouchcancel) {
    337        resolvedEventMessage = eTouchCancel;
    338      }
    339    }
    340 
    341    switch (resolvedEventMessage) {
    342      case eLegacyDOMActivate:
    343        mMayHaveDOMActivateEventListener = true;
    344        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    345          window->SetHasDOMActivateEventListeners();
    346        }
    347        break;
    348      case ePointerEnter:
    349      case ePointerLeave:
    350        mMayHavePointerEnterLeaveEventListener = true;
    351        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    352          LogForChromeEvent(
    353              window,
    354              "Please do not use pointerenter/leave events in chrome. "
    355              "They are slower than pointerover/out!");
    356          window->SetHasPointerEnterLeaveEventListeners();
    357        }
    358        break;
    359      case ePointerRawUpdate:
    360        if (!StaticPrefs::dom_event_pointer_rawupdate_enabled()) {
    361          break;
    362        }
    363        mMayHavePointerRawUpdateEventListener = true;
    364        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    365          LogForChromeEvent(
    366              window, "Please do not use pointerrawupdate event in chrome.");
    367          window->MaybeSetHasPointerRawUpdateEventListeners();
    368        }
    369        break;
    370      case eGamepadButtonDown:
    371      case eGamepadButtonUp:
    372      case eGamepadAxisMove:
    373      case eGamepadConnected:
    374      case eGamepadDisconnected:
    375        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    376          window->SetHasGamepadEventListener();
    377        }
    378        break;
    379      case eDeviceOrientation:
    380      case eDeviceOrientationAbsolute:
    381      case eUserProximity:
    382      case eDeviceLight:
    383      case eDeviceMotion:
    384 #if defined(MOZ_WIDGET_ANDROID)
    385      case eOrientationChange:
    386 #endif  // #if defined(MOZ_WIDGET_ANDROID)
    387        EnableDevice(aTypeAtom);
    388        break;
    389      case eTouchStart:
    390      case eTouchEnd:
    391      case eTouchMove:
    392      case eTouchCancel:
    393        mMayHaveTouchEventListener = true;
    394        // we don't want touchevent listeners added by scrollbars to flip this
    395        // flag so we ignore listeners created with system event flag
    396        if (!aFlags.mInSystemGroup) {
    397          if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    398            window->SetHasTouchEventListeners();
    399          }
    400        }
    401        break;
    402      case eMouseEnter:
    403      case eMouseLeave:
    404        mMayHaveMouseEnterLeaveEventListener = true;
    405        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    406          LogForChromeEvent(
    407              window,
    408              "Please do not use mouseenter/leave events in chrome. "
    409              "They are slower than mouseover/out!");
    410          window->SetHasMouseEnterLeaveEventListeners();
    411        }
    412        break;
    413      case eEditorBeforeInput:
    414        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    415          window->SetHasBeforeInputEventListenersForTelemetry();
    416        }
    417        break;
    418      case eSelectionChange:
    419        mMayHaveSelectionChangeEventListener = true;
    420        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    421          window->SetHasSelectionChangeEventListeners();
    422        }
    423        break;
    424      case eFormSelect:
    425        mMayHaveFormSelectEventListener = true;
    426        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    427          window->SetHasFormSelectEventListeners();
    428        }
    429        break;
    430      case eLegacyMouseLineOrPageScroll:
    431        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    432          if (Document* doc = window->GetExtantDoc()) {
    433            doc->SetUseCounter(eUseCounter_custom_ondommousescroll);
    434          }
    435        }
    436        break;
    437      case eLegacyMousePixelScroll:
    438        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    439          if (Document* doc = window->GetExtantDoc()) {
    440            doc->SetUseCounter(eUseCounter_custom_onmozmousepixelscroll);
    441          }
    442        }
    443        break;
    444      case eTransitionStart:
    445      case eTransitionRun:
    446      case eTransitionEnd:
    447      case eTransitionCancel:
    448      case eWebkitTransitionEnd:
    449        mMayHaveTransitionEventListener = true;
    450        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    451          window->SetHasTransitionEventListeners();
    452        }
    453        break;
    454      case eSMILBeginEvent:
    455      case eSMILEndEvent:
    456      case eSMILRepeatEvent:
    457        mMayHaveSMILTimeEventListener = true;
    458        if (nsPIDOMWindowInner* window = GetInnerWindowForTarget()) {
    459          window->SetHasSMILTimeEventListeners();
    460        }
    461        break;
    462      case eFormCheckboxStateChange:
    463        nsContentUtils::SetMayHaveFormCheckboxStateChangeListeners();
    464        break;
    465      case eFormRadioStateChange:
    466        nsContentUtils::SetMayHaveFormRadioStateChangeListeners();
    467        break;
    468      case eMozOrientationChange:
    469        if (nsScreen* screen = mTarget->GetAsScreen()) {
    470          if (nsPIDOMWindowOuter* outer = screen->GetOuter()) {
    471            if (Document* doc = outer->GetExtantDoc()) {
    472              doc->WarnOnceAbout(
    473                  DeprecatedOperations::eMozorientationchangeDeprecated);
    474            }
    475          }
    476        }
    477        break;
    478      default:
    479        // XXX Use NS_ASSERTION here to print resolvedEventMessage since
    480        //     MOZ_ASSERT can take only string literal, not pointer to
    481        //     characters.
    482        NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerenter,
    483                     nsPrintfCString("resolvedEventMessage=%s",
    484                                     ToChar(resolvedEventMessage))
    485                         .get());
    486        NS_ASSERTION(aTypeAtom != nsGkAtoms::onpointerleave,
    487                     nsPrintfCString("resolvedEventMessage=%s",
    488                                     ToChar(resolvedEventMessage))
    489                         .get());
    490        NS_ASSERTION(
    491            resolvedEventMessage < eGamepadEventFirst ||
    492                resolvedEventMessage > eGamepadEventLast,
    493            nsPrintfCString("You added new gamepad event, but it's not "
    494                            "handled above, resolvedEventMessage=%s",
    495                            ToChar(resolvedEventMessage))
    496                .get());
    497        NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientation,
    498                     nsPrintfCString("resolvedEventMessage=%s",
    499                                     ToChar(resolvedEventMessage))
    500                         .get());
    501        NS_ASSERTION(aTypeAtom != nsGkAtoms::ondeviceorientationabsolute,
    502                     nsPrintfCString("resolvedEventMessage=%s",
    503                                     ToChar(resolvedEventMessage))
    504                         .get());
    505        NS_ASSERTION(aTypeAtom != nsGkAtoms::onuserproximity,
    506                     nsPrintfCString("resolvedEventMessage=%s",
    507                                     ToChar(resolvedEventMessage))
    508                         .get());
    509        NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicelight,
    510                     nsPrintfCString("resolvedEventMessage=%s",
    511                                     ToChar(resolvedEventMessage))
    512                         .get());
    513        NS_ASSERTION(aTypeAtom != nsGkAtoms::ondevicemotion,
    514                     nsPrintfCString("resolvedEventMessage=%s",
    515                                     ToChar(resolvedEventMessage))
    516                         .get());
    517 #if defined(MOZ_WIDGET_ANDROID)
    518        NS_ASSERTION(aTypeAtom != nsGkAtoms::onorientationchange,
    519                     nsPrintfCString("resolvedEventMessage=%s",
    520                                     ToChar(resolvedEventMessage))
    521                         .get());
    522 #endif  // #if defined(MOZ_WIDGET_ANDROID)
    523        NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchstart,
    524                     nsPrintfCString("resolvedEventMessage=%s",
    525                                     ToChar(resolvedEventMessage))
    526                         .get());
    527        NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchend,
    528                     nsPrintfCString("resolvedEventMessage=%s",
    529                                     ToChar(resolvedEventMessage))
    530                         .get());
    531        NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchmove,
    532                     nsPrintfCString("resolvedEventMessage=%s",
    533                                     ToChar(resolvedEventMessage))
    534                         .get());
    535        NS_ASSERTION(aTypeAtom != nsGkAtoms::ontouchcancel,
    536                     nsPrintfCString("resolvedEventMessage=%s",
    537                                     ToChar(resolvedEventMessage))
    538                         .get());
    539        NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseenter,
    540                     nsPrintfCString("resolvedEventMessage=%s",
    541                                     ToChar(resolvedEventMessage))
    542                         .get());
    543        NS_ASSERTION(aTypeAtom != nsGkAtoms::onmouseleave,
    544                     nsPrintfCString("resolvedEventMessage=%s",
    545                                     ToChar(resolvedEventMessage))
    546                         .get());
    547        NS_ASSERTION(aTypeAtom != nsGkAtoms::onbeforeinput,
    548                     nsPrintfCString("resolvedEventMessage=%s",
    549                                     ToChar(resolvedEventMessage))
    550                         .get());
    551        NS_ASSERTION(aTypeAtom != nsGkAtoms::onselectionchange,
    552                     nsPrintfCString("resolvedEventMessage=%s",
    553                                     ToChar(resolvedEventMessage))
    554                         .get());
    555        NS_ASSERTION(aTypeAtom != nsGkAtoms::onselect,
    556                     nsPrintfCString("resolvedEventMessage=%s",
    557                                     ToChar(resolvedEventMessage))
    558                         .get());
    559        NS_ASSERTION(aTypeAtom != nsGkAtoms::onDOMMouseScroll,
    560                     nsPrintfCString("resolvedEventMessage=%s",
    561                                     ToChar(resolvedEventMessage))
    562                         .get());
    563        NS_ASSERTION(aTypeAtom != nsGkAtoms::onMozMousePixelScroll,
    564                     nsPrintfCString("resolvedEventMessage=%s",
    565                                     ToChar(resolvedEventMessage))
    566                         .get());
    567        NS_ASSERTION(aTypeAtom != nsGkAtoms::onmozorientationchange,
    568                     nsPrintfCString("resolvedEventMessage=%s",
    569                                     ToChar(resolvedEventMessage))
    570                         .get());
    571        break;
    572    }
    573  }
    574 
    575  if (mIsMainThreadELM && !aFlags.mPassive && IsApzAwareEvent(aTypeAtom)) {
    576    ProcessApzAwareEventListenerAdd();
    577  }
    578 
    579  if (mTarget) {
    580    mTarget->EventListenerAdded(aTypeAtom);
    581  }
    582 
    583  if (mIsMainThreadELM && mTarget) {
    584    EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
    585                                                              aTypeAtom);
    586  }
    587 }
    588 
    589 void EventListenerManager::ProcessApzAwareEventListenerAdd() {
    590  Document* doc = nullptr;
    591 
    592  // Mark the node as having apz aware listeners
    593  if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
    594    node->SetMayBeApzAware();
    595    doc = node->OwnerDoc();
    596  }
    597 
    598  // Schedule a paint so event regions on the layer tree gets updated
    599  if (!doc) {
    600    if (nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow()) {
    601      doc = window->GetExtantDoc();
    602    }
    603  }
    604  if (!doc) {
    605    if (nsCOMPtr<DOMEventTargetHelper> helper = do_QueryInterface(mTarget)) {
    606      if (nsPIDOMWindowInner* window = helper->GetOwnerWindow()) {
    607        doc = window->GetExtantDoc();
    608      }
    609    }
    610  }
    611 
    612  if (doc && gfxPlatform::AsyncPanZoomEnabled()) {
    613    PresShell* presShell = doc->GetPresShell();
    614    if (presShell) {
    615      nsIFrame* f = presShell->GetRootFrame();
    616      if (f) {
    617        f->SchedulePaint();
    618      }
    619    }
    620  }
    621 }
    622 
    623 bool EventListenerManager::IsDeviceType(nsAtom* aTypeAtom) {
    624  return aTypeAtom == nsGkAtoms::ondeviceorientation ||
    625         aTypeAtom == nsGkAtoms::ondeviceorientationabsolute ||
    626         aTypeAtom == nsGkAtoms::ondevicemotion ||
    627         aTypeAtom == nsGkAtoms::ondevicelight
    628 #if defined(MOZ_WIDGET_ANDROID)
    629         || aTypeAtom == nsGkAtoms::onorientationchange
    630 #endif
    631         || aTypeAtom == nsGkAtoms::onuserproximity;
    632 }
    633 
    634 void EventListenerManager::EnableDevice(nsAtom* aTypeAtom) {
    635  nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
    636  if (!window) {
    637    return;
    638  }
    639 
    640  if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
    641 #ifdef MOZ_WIDGET_ANDROID
    642    // Falls back to SENSOR_ROTATION_VECTOR and SENSOR_ORIENTATION if
    643    // unavailable on device.
    644    window->EnableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
    645    window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
    646 #else
    647    window->EnableDeviceSensor(SENSOR_ORIENTATION);
    648 #endif
    649    return;
    650  }
    651 
    652  if (aTypeAtom == nsGkAtoms::ondeviceorientationabsolute) {
    653 #ifdef MOZ_WIDGET_ANDROID
    654    // Falls back to SENSOR_ORIENTATION if unavailable on device.
    655    window->EnableDeviceSensor(SENSOR_ROTATION_VECTOR);
    656 #else
    657    window->EnableDeviceSensor(SENSOR_ORIENTATION);
    658 #endif
    659    return;
    660  }
    661 
    662  if (aTypeAtom == nsGkAtoms::onuserproximity) {
    663    window->EnableDeviceSensor(SENSOR_PROXIMITY);
    664    return;
    665  }
    666 
    667  if (aTypeAtom == nsGkAtoms::ondevicelight) {
    668    window->EnableDeviceSensor(SENSOR_LIGHT);
    669    return;
    670  }
    671 
    672  if (aTypeAtom == nsGkAtoms::ondevicemotion) {
    673    window->EnableDeviceSensor(SENSOR_ACCELERATION);
    674    window->EnableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
    675    window->EnableDeviceSensor(SENSOR_GYROSCOPE);
    676    return;
    677  }
    678 
    679 #if defined(MOZ_WIDGET_ANDROID)
    680  if (aTypeAtom == nsGkAtoms::onorientationchange) {
    681    window->EnableOrientationChangeListener();
    682    return;
    683  }
    684 #endif
    685 
    686  NS_WARNING("Enabling an unknown device sensor.");
    687 }
    688 
    689 void EventListenerManager::DisableDevice(nsAtom* aTypeAtom) {
    690  nsCOMPtr<nsPIDOMWindowInner> window = GetTargetAsInnerWindow();
    691  if (!window) {
    692    return;
    693  }
    694 
    695  if (aTypeAtom == nsGkAtoms::ondeviceorientation) {
    696 #ifdef MOZ_WIDGET_ANDROID
    697    // Disable all potential fallback sensors.
    698    window->DisableDeviceSensor(SENSOR_GAME_ROTATION_VECTOR);
    699    window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
    700 #endif
    701    window->DisableDeviceSensor(SENSOR_ORIENTATION);
    702    return;
    703  }
    704 
    705  if (aTypeAtom == nsGkAtoms::ondeviceorientationabsolute) {
    706 #ifdef MOZ_WIDGET_ANDROID
    707    window->DisableDeviceSensor(SENSOR_ROTATION_VECTOR);
    708 #endif
    709    window->DisableDeviceSensor(SENSOR_ORIENTATION);
    710    return;
    711  }
    712 
    713  if (aTypeAtom == nsGkAtoms::ondevicemotion) {
    714    window->DisableDeviceSensor(SENSOR_ACCELERATION);
    715    window->DisableDeviceSensor(SENSOR_LINEAR_ACCELERATION);
    716    window->DisableDeviceSensor(SENSOR_GYROSCOPE);
    717    return;
    718  }
    719 
    720  if (aTypeAtom == nsGkAtoms::onuserproximity) {
    721    window->DisableDeviceSensor(SENSOR_PROXIMITY);
    722    return;
    723  }
    724 
    725  if (aTypeAtom == nsGkAtoms::ondevicelight) {
    726    window->DisableDeviceSensor(SENSOR_LIGHT);
    727    return;
    728  }
    729 
    730 #if defined(MOZ_WIDGET_ANDROID)
    731  if (aTypeAtom == nsGkAtoms::onorientationchange) {
    732    window->DisableOrientationChangeListener();
    733    return;
    734  }
    735 #endif
    736 
    737  NS_WARNING("Disabling an unknown device sensor.");
    738 }
    739 
    740 void EventListenerManager::NotifyEventListenerRemoved(nsAtom* aUserType) {
    741  // If the following code is changed, other callsites of EventListenerRemoved
    742  // and NotifyAboutMainThreadListenerChange should be changed too.
    743  ClearNoListenersForEvents();
    744  mNoListenerForEventAtom = nullptr;
    745  if (mTarget) {
    746    mTarget->EventListenerRemoved(aUserType);
    747  }
    748  if (mIsMainThreadELM && mTarget) {
    749    EventListenerService::NotifyAboutMainThreadListenerChange(mTarget,
    750                                                              aUserType);
    751  }
    752 }
    753 
    754 void EventListenerManager::RemoveEventListenerInternal(
    755    EventListenerHolder aListenerHolder, nsAtom* aUserType,
    756    const EventListenerFlags& aFlags, bool aAllEvents) {
    757  if (!aListenerHolder || (!aUserType && !aAllEvents) || mClearingListeners) {
    758    return;
    759  }
    760 
    761  Maybe<size_t> entryIndex = aAllEvents
    762                                 ? mListenerMap.EntryIndexForAllEvents()
    763                                 : mListenerMap.EntryIndexForType(aUserType);
    764  if (!entryIndex) {
    765    return;
    766  }
    767 
    768  ListenerArray& listenerArray = *mListenerMap.mEntries[*entryIndex].mListeners;
    769 
    770  Maybe<uint32_t> listenerIndex = [&]() -> Maybe<uint32_t> {
    771    uint32_t count = listenerArray.Length();
    772    for (uint32_t i = 0; i < count; ++i) {
    773      Listener* listener = &listenerArray.ElementAt(i);
    774      if (listener->mListener == aListenerHolder &&
    775          listener->mFlags.EqualsForRemoval(aFlags)) {
    776        return Some(i);
    777      }
    778    }
    779    return Nothing();
    780  }();
    781 
    782  if (!listenerIndex) {
    783    return;
    784  }
    785 
    786  listenerArray.RemoveElementAt(*listenerIndex);
    787  if (listenerArray.IsEmpty()) {
    788    mListenerMap.mEntries.RemoveElementAt(*entryIndex);
    789  }
    790 
    791  RefPtr<EventListenerManager> kungFuDeathGrip(this);
    792  if (!aAllEvents) {
    793    NotifyEventListenerRemoved(aUserType);
    794    if (IsDeviceType(aUserType)) {
    795      DisableDevice(aUserType);
    796    }
    797  }
    798 
    799  // XXX Should we clear mMayHavePointerRawUpdateEventListener if the last
    800  // pointerrawupdate event listener is removed?  If so, nsPIDOMWindowInner
    801  // needs to count how may event listener managers had some pointerrawupdate
    802  // event listener.  If we've notified the window of having a pointerrawupdate
    803  // event listener, some behavior is changed because pointerrawupdate event
    804  // dispatcher needs to handle some things before dispatching an event to the
    805  // DOM.  However, it is expected that web apps using `pointerrawupdate` don't
    806  // remove the event listeners.
    807 }
    808 
    809 static bool IsDefaultPassiveWhenOnRoot(EventMessage aMessage) {
    810  if (aMessage == eTouchStart || aMessage == eTouchMove || aMessage == eWheel ||
    811      aMessage == eLegacyMouseLineOrPageScroll ||
    812      aMessage == eLegacyMousePixelScroll) {
    813    return true;
    814  }
    815  return false;
    816 }
    817 
    818 static bool IsRootEventTarget(EventTarget* aTarget) {
    819  if (!aTarget) {
    820    return false;
    821  }
    822  if (aTarget->IsInnerWindow()) {
    823    return true;
    824  }
    825  const nsINode* node = nsINode::FromEventTarget(aTarget);
    826  if (!node) {
    827    return false;
    828  }
    829  Document* doc = node->OwnerDoc();
    830  return node == doc || node == doc->GetRootElement() || node == doc->GetBody();
    831 }
    832 
    833 void EventListenerManager::MaybeMarkPassive(EventMessage aMessage,
    834                                            EventListenerFlags& aFlags) {
    835  if (!mIsMainThreadELM) {
    836    return;
    837  }
    838  if (!IsDefaultPassiveWhenOnRoot(aMessage)) {
    839    return;
    840  }
    841  if (!IsRootEventTarget(mTarget)) {
    842    return;
    843  }
    844  aFlags.mPassive = true;
    845 }
    846 
    847 void EventListenerManager::AddEventListenerByType(
    848    EventListenerHolder aListenerHolder, const nsAString& aType,
    849    const EventListenerFlags& aFlags, const Optional<bool>& aPassive,
    850    AbortSignal* aSignal) {
    851  RefPtr<nsAtom> atom;
    852  EventMessage message =
    853      GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
    854 
    855  EventListenerFlags flags = aFlags;
    856  if (aPassive.WasPassed()) {
    857    flags.mPassive = aPassive.Value();
    858  } else {
    859    MaybeMarkPassive(message, flags);
    860  }
    861 
    862  AddEventListenerInternal(std::move(aListenerHolder), message, atom, flags,
    863                           false, false, aSignal);
    864 }
    865 
    866 void EventListenerManager::RemoveEventListenerByType(
    867    EventListenerHolder aListenerHolder, const nsAString& aType,
    868    const EventListenerFlags& aFlags) {
    869  RefPtr<nsAtom> atom;
    870  (void)GetEventMessageAndAtomForListener(aType, getter_AddRefs(atom));
    871  RemoveEventListenerInternal(std::move(aListenerHolder), atom, aFlags);
    872 }
    873 
    874 EventListenerManager::Listener* EventListenerManager::FindEventHandler(
    875    nsAtom* aTypeAtom) {
    876  // Run through the listeners for this type and see if a script
    877  // listener is registered
    878  RefPtr<ListenerArray> listeners = mListenerMap.GetListenersForType(aTypeAtom);
    879  if (!listeners) {
    880    return nullptr;
    881  }
    882 
    883  uint32_t count = listeners->Length();
    884  for (uint32_t i = 0; i < count; ++i) {
    885    Listener* listener = &listeners->ElementAt(i);
    886    if (listener->mListenerIsHandler) {
    887      return listener;
    888    }
    889  }
    890  return nullptr;
    891 }
    892 
    893 EventListenerManager::Listener* EventListenerManager::SetEventHandlerInternal(
    894    nsAtom* aName, const TypedEventHandler& aTypedHandler,
    895    bool aPermitUntrustedEvents) {
    896  MOZ_ASSERT(aName);
    897 
    898  EventMessage eventMessage = GetEventMessage(aName);
    899  Listener* listener = FindEventHandler(aName);
    900 
    901  if (!listener) {
    902    // If we didn't find a script listener or no listeners existed
    903    // create and add a new one.
    904    EventListenerFlags flags;
    905    flags.mListenerIsJSListener = true;
    906    MaybeMarkPassive(eventMessage, flags);
    907 
    908    nsCOMPtr<JSEventHandler> jsEventHandler;
    909    NS_NewJSEventHandler(mTarget, aName, aTypedHandler,
    910                         getter_AddRefs(jsEventHandler));
    911    AddEventListenerInternal(EventListenerHolder(jsEventHandler), eventMessage,
    912                             aName, flags, true);
    913 
    914    listener = FindEventHandler(aName);
    915  } else {
    916    JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
    917    MOZ_ASSERT(jsEventHandler,
    918               "How can we have an event handler with no JSEventHandler?");
    919 
    920    bool same = jsEventHandler->GetTypedEventHandler() == aTypedHandler;
    921    // Possibly the same listener, but update still the context and scope.
    922    jsEventHandler->SetHandler(aTypedHandler);
    923    if (mTarget && !same) {
    924      mTarget->EventListenerRemoved(aName);
    925      mTarget->EventListenerAdded(aName);
    926    }
    927    if (mIsMainThreadELM && mTarget) {
    928      EventListenerService::NotifyAboutMainThreadListenerChange(mTarget, aName);
    929    }
    930  }
    931 
    932  // Set flag to indicate possible need for compilation later
    933  listener->mHandlerIsString = !aTypedHandler.HasEventHandler();
    934  if (aPermitUntrustedEvents) {
    935    listener->mFlags.mAllowUntrustedEvents = true;
    936    mMayHaveListenersForUntrustedEvents = true;
    937  }
    938 
    939  return listener;
    940 }
    941 
    942 nsresult EventListenerManager::SetEventHandler(nsAtom* aName,
    943                                               const nsAString& aBody,
    944                                               bool aDeferCompilation,
    945                                               bool aPermitUntrustedEvents,
    946                                               Element* aElement) {
    947  auto removeEventHandler = MakeScopeExit([&] { RemoveEventHandler(aName); });
    948 
    949  nsCOMPtr<Document> doc;
    950  nsCOMPtr<nsIScriptGlobalObject> global =
    951      GetScriptGlobalAndDocument(getter_AddRefs(doc));
    952 
    953  if (!global) {
    954    // This can happen; for example this document might have been
    955    // loaded as data.
    956    return NS_OK;
    957  }
    958 
    959  nsresult rv = NS_OK;
    960  // return early preventing the event listener from being added
    961  // 'doc' is fetched above
    962  if (doc) {
    963    // Don't allow adding an event listener if the document is sandboxed
    964    // without 'allow-scripts'.
    965    if (doc->HasScriptsBlockedBySandbox()) {
    966      return NS_ERROR_DOM_SECURITY_ERR;
    967    }
    968 
    969    // Perform CSP check
    970    nsCOMPtr<nsIContentSecurityPolicy> csp =
    971        PolicyContainer::GetCSP(doc->GetPolicyContainer());
    972    uint32_t lineNum = 0;
    973    JS::ColumnNumberOneOrigin columnNum;
    974 
    975    JSContext* cx = nsContentUtils::GetCurrentJSContext();
    976    if (cx) {
    977      JS::DescribeScriptedCaller(nullptr, cx, &lineNum, &columnNum);
    978    }
    979 
    980    if (csp) {
    981      bool allowsInlineScript = true;
    982      rv = csp->GetAllowsInline(
    983          nsIContentSecurityPolicy::SCRIPT_SRC_ATTR_DIRECTIVE,
    984          true,    // aHasUnsafeHash
    985          u""_ns,  // aNonce
    986          true,    // aParserCreated (true because attribute event handler)
    987          aElement,
    988          nullptr,  // nsICSPEventListener
    989          aBody, lineNum, columnNum.oneOriginValue(), &allowsInlineScript);
    990      NS_ENSURE_SUCCESS(rv, rv);
    991 
    992      // return early if CSP wants us to block inline scripts
    993      if (!allowsInlineScript) {
    994        return NS_OK;
    995      }
    996    }
    997  }
    998 
    999  // This might be the first reference to this language in the global
   1000  // We must init the language before we attempt to fetch its context.
   1001  if (NS_FAILED(global->EnsureScriptEnvironment())) {
   1002    NS_WARNING("Failed to setup script environment for this language");
   1003    // but fall through and let the inevitable failure below handle it.
   1004  }
   1005 
   1006  nsIScriptContext* context = global->GetScriptContext();
   1007  NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
   1008  NS_ENSURE_STATE(global->HasJSGlobal());
   1009 
   1010  removeEventHandler.release();
   1011 
   1012  Listener* listener = SetEventHandlerInternal(aName, TypedEventHandler(),
   1013                                               aPermitUntrustedEvents);
   1014 
   1015  if (!aDeferCompilation) {
   1016    return CompileEventHandlerInternal(listener, aName, &aBody, aElement);
   1017  }
   1018 
   1019  return NS_OK;
   1020 }
   1021 
   1022 void EventListenerManager::RemoveEventHandler(nsAtom* aName) {
   1023  if (mClearingListeners) {
   1024    return;
   1025  }
   1026 
   1027  Maybe<size_t> entryIndex = mListenerMap.EntryIndexForType(aName);
   1028  if (!entryIndex) {
   1029    return;
   1030  }
   1031 
   1032  ListenerArray& listenerArray = *mListenerMap.mEntries[*entryIndex].mListeners;
   1033 
   1034  Maybe<uint32_t> listenerIndex = [&]() -> Maybe<uint32_t> {
   1035    uint32_t count = listenerArray.Length();
   1036    for (uint32_t i = 0; i < count; ++i) {
   1037      Listener* listener = &listenerArray.ElementAt(i);
   1038      if (listener->mListenerIsHandler) {
   1039        return Some(i);
   1040      }
   1041    }
   1042    return Nothing();
   1043  }();
   1044 
   1045  if (!listenerIndex) {
   1046    return;
   1047  }
   1048 
   1049  listenerArray.RemoveElementAt(*listenerIndex);
   1050  if (listenerArray.IsEmpty()) {
   1051    mListenerMap.mEntries.RemoveElementAt(*entryIndex);
   1052  }
   1053 
   1054  RefPtr<EventListenerManager> kungFuDeathGrip(this);
   1055  NotifyEventListenerRemoved(aName);
   1056  if (IsDeviceType(aName)) {
   1057    DisableDevice(aName);
   1058  }
   1059 }
   1060 
   1061 nsresult EventListenerManager::CompileEventHandlerInternal(
   1062    Listener* aListener, nsAtom* aTypeAtom, const nsAString* aBody,
   1063    Element* aElement) {
   1064  MOZ_ASSERT(aListener->GetJSEventHandler());
   1065  MOZ_ASSERT(aListener->mHandlerIsString,
   1066             "Why are we compiling a non-string JS listener?");
   1067  JSEventHandler* jsEventHandler = aListener->GetJSEventHandler();
   1068  MOZ_ASSERT(!jsEventHandler->GetTypedEventHandler().HasEventHandler(),
   1069             "What is there to compile?");
   1070 
   1071  nsresult result = NS_OK;
   1072  nsCOMPtr<Document> doc;
   1073  nsCOMPtr<nsIScriptGlobalObject> global =
   1074      GetScriptGlobalAndDocument(getter_AddRefs(doc));
   1075  NS_ENSURE_STATE(global);
   1076 
   1077  // Activate JSAPI, and make sure that exceptions are reported on the right
   1078  // Window.
   1079  AutoJSAPI jsapi;
   1080  if (NS_WARN_IF(!jsapi.Init(global))) {
   1081    return NS_ERROR_UNEXPECTED;
   1082  }
   1083  JSContext* cx = jsapi.cx();
   1084 
   1085  nsAtom* attrName = aTypeAtom;
   1086 
   1087  // Flag us as not a string so we don't keep trying to compile strings which
   1088  // can't be compiled.
   1089  aListener->mHandlerIsString = false;
   1090 
   1091  // mTarget may not be an Element if it's a window and we're
   1092  // getting an inline event listener forwarded from <html:body> or
   1093  // <html:frameset> or <xul:window> or the like.
   1094  // XXX I don't like that we have to reference content from
   1095  // here. The alternative is to store the event handler string on
   1096  // the JSEventHandler itself, and that still doesn't address
   1097  // the arg names issue.
   1098  RefPtr<Element> element = Element::FromEventTargetOrNull(mTarget);
   1099  MOZ_ASSERT(element || aBody, "Where will we get our body?");
   1100  nsAutoString handlerBody;
   1101  const nsAString* body = aBody;
   1102  if (!aBody) {
   1103    if (aTypeAtom == nsGkAtoms::onSVGLoad) {
   1104      attrName = nsGkAtoms::onload;
   1105    } else if (aTypeAtom == nsGkAtoms::onSVGScroll) {
   1106      attrName = nsGkAtoms::onscroll;
   1107    } else if (aTypeAtom == nsGkAtoms::onbeginEvent) {
   1108      attrName = nsGkAtoms::onbegin;
   1109    } else if (aTypeAtom == nsGkAtoms::onrepeatEvent) {
   1110      attrName = nsGkAtoms::onrepeat;
   1111    } else if (aTypeAtom == nsGkAtoms::onendEvent) {
   1112      attrName = nsGkAtoms::onend;
   1113    } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationEnd) {
   1114      attrName = nsGkAtoms::onwebkitanimationend;
   1115    } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationIteration) {
   1116      attrName = nsGkAtoms::onwebkitanimationiteration;
   1117    } else if (aTypeAtom == nsGkAtoms::onwebkitAnimationStart) {
   1118      attrName = nsGkAtoms::onwebkitanimationstart;
   1119    } else if (aTypeAtom == nsGkAtoms::onwebkitTransitionEnd) {
   1120      attrName = nsGkAtoms::onwebkittransitionend;
   1121    }
   1122 
   1123    element->GetAttr(attrName, handlerBody);
   1124    body = &handlerBody;
   1125    aElement = element;
   1126  }
   1127  aListener = nullptr;
   1128 
   1129  nsAutoCString url("-moz-evil:lying-event-listener"_ns);
   1130  MOZ_ASSERT(body);
   1131  MOZ_ASSERT(aElement);
   1132  nsIURI* uri = aElement->OwnerDoc()->GetDocumentURI();
   1133  if (uri) {
   1134    uri->GetSpec(url);
   1135  }
   1136 
   1137  nsCOMPtr<nsPIDOMWindowInner> win =
   1138      nsPIDOMWindowInner::FromEventTargetOrNull(mTarget);
   1139  uint32_t argCount;
   1140  const char** argNames;
   1141  nsContentUtils::GetEventArgNames(aElement->GetNameSpaceID(), aTypeAtom, win,
   1142                                   &argCount, &argNames);
   1143 
   1144  // Wrap the event target, so that we can use it as the scope for the event
   1145  // handler. Note that mTarget is different from aElement in the <body> case,
   1146  // where mTarget is a Window.
   1147  //
   1148  // The wrapScope doesn't really matter here, because the target will create
   1149  // its reflector in the proper scope, and then we'll enter that realm.
   1150  JS::Rooted<JSObject*> wrapScope(cx, global->GetGlobalJSObject());
   1151  JS::Rooted<JS::Value> v(cx);
   1152  {
   1153    JSAutoRealm ar(cx, wrapScope);
   1154    nsresult rv = nsContentUtils::WrapNative(cx, mTarget, &v,
   1155                                             /* aAllowWrapping = */ false);
   1156    if (NS_WARN_IF(NS_FAILED(rv))) {
   1157      return rv;
   1158    }
   1159  }
   1160 
   1161  JS::Rooted<JSObject*> target(cx, &v.toObject());
   1162  JSAutoRealm ar(cx, target);
   1163 
   1164  // Now that we've entered the realm we actually care about, create our
   1165  // environment chain.  Note that we start with |element|, not aElement,
   1166  // because mTarget is different from aElement in the <body> case, where
   1167  // mTarget is a Window, and in that case we do not want the environment chain
   1168  // to include the body or the document.
   1169  JS::EnvironmentChain envChain(cx, JS::SupportUnscopables::Yes);
   1170  if (!nsJSUtils::GetEnvironmentChainForElement(cx, element, envChain)) {
   1171    return NS_ERROR_OUT_OF_MEMORY;
   1172  }
   1173 
   1174  nsDependentAtomString str(attrName);
   1175  // Most of our names are short enough that we don't even have to malloc
   1176  // the JS string stuff, so don't worry about playing games with
   1177  // refcounting XPCOM stringbuffers.
   1178  JS::Rooted<JSString*> jsStr(
   1179      cx, JS_NewUCStringCopyN(cx, str.BeginReading(), str.Length()));
   1180  NS_ENSURE_TRUE(jsStr, NS_ERROR_OUT_OF_MEMORY);
   1181 
   1182  // Get the reflector for |aElement|, so that we can pass to setElement.
   1183  if (NS_WARN_IF(!GetOrCreateDOMReflector(cx, aElement, &v))) {
   1184    return NS_ERROR_FAILURE;
   1185  }
   1186 
   1187  RefPtr<JS::loader::ScriptFetchOptions> fetchOptions =
   1188      new JS::loader::ScriptFetchOptions(
   1189          CORS_NONE, /* aNonce = */ u""_ns, RequestPriority::Auto,
   1190          JS::loader::ParserMetadata::NotParserInserted,
   1191          aElement->OwnerDoc()->NodePrincipal());
   1192 
   1193  RefPtr<JS::loader::EventScript> eventScript = new JS::loader::EventScript(
   1194      aElement->OwnerDoc()->GetReferrerPolicy(), fetchOptions, uri);
   1195 
   1196  JS::CompileOptions options(cx);
   1197  // Use line 0 to make the function body starts from line 1.
   1198  options.setIntroductionType("eventHandler")
   1199      .setFileAndLine(url.get(), 0)
   1200      .setDeferDebugMetadata(true);
   1201 
   1202  JS::Rooted<JSObject*> handler(cx);
   1203  result = nsJSUtils::CompileFunction(jsapi, envChain, options,
   1204                                      nsAtomCString(aTypeAtom), argCount,
   1205                                      argNames, *body, handler.address());
   1206  NS_ENSURE_SUCCESS(result, result);
   1207  NS_ENSURE_TRUE(handler, NS_ERROR_FAILURE);
   1208 
   1209  JS::Rooted<JS::Value> privateValue(cx, JS::PrivateValue(eventScript));
   1210  result = nsJSUtils::UpdateFunctionDebugMetadata(jsapi, handler, options,
   1211                                                  jsStr, privateValue);
   1212  NS_ENSURE_SUCCESS(result, result);
   1213 
   1214  MOZ_ASSERT(js::IsObjectInContextCompartment(handler, cx));
   1215  JS::Rooted<JSObject*> handlerGlobal(cx, JS::CurrentGlobalOrNull(cx));
   1216 
   1217  if (jsEventHandler->EventName() == nsGkAtoms::onerror && win) {
   1218    RefPtr<OnErrorEventHandlerNonNull> handlerCallback =
   1219        new OnErrorEventHandlerNonNull(static_cast<JSContext*>(nullptr),
   1220                                       handler, handlerGlobal,
   1221                                       /* aIncumbentGlobal = */ nullptr);
   1222    jsEventHandler->SetHandler(handlerCallback);
   1223  } else if (jsEventHandler->EventName() == nsGkAtoms::onbeforeunload && win) {
   1224    RefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
   1225        new OnBeforeUnloadEventHandlerNonNull(static_cast<JSContext*>(nullptr),
   1226                                              handler, handlerGlobal,
   1227                                              /* aIncumbentGlobal = */ nullptr);
   1228    jsEventHandler->SetHandler(handlerCallback);
   1229  } else {
   1230    RefPtr<EventHandlerNonNull> handlerCallback = new EventHandlerNonNull(
   1231        static_cast<JSContext*>(nullptr), handler, handlerGlobal,
   1232        /* aIncumbentGlobal = */ nullptr);
   1233    jsEventHandler->SetHandler(handlerCallback);
   1234  }
   1235 
   1236  return result;
   1237 }
   1238 
   1239 bool EventListenerManager::HandleEventSingleListener(
   1240    Listener* aListener, nsAtom* aTypeAtom, WidgetEvent* aEvent,
   1241    Event* aDOMEvent, EventTarget* aCurrentTarget, bool aItemInShadowTree) {
   1242  if (!aEvent->mCurrentTarget) {
   1243    aEvent->mCurrentTarget = aCurrentTarget->GetTargetForDOMEvent();
   1244    if (!aEvent->mCurrentTarget) {
   1245      return false;
   1246    }
   1247  }
   1248 
   1249  aEvent->mFlags.mInPassiveListener = aListener->mFlags.mPassive;
   1250 
   1251  nsCOMPtr<nsPIDOMWindowInner> innerWindow =
   1252      WindowFromListener(aListener, aTypeAtom, aItemInShadowTree);
   1253  mozilla::dom::Event* oldWindowEvent = nullptr;
   1254  if (innerWindow) {
   1255    oldWindowEvent = innerWindow->SetEvent(aDOMEvent);
   1256  }
   1257 
   1258  nsresult result = NS_OK;
   1259 
   1260  // strong ref
   1261  EventListenerHolder listenerHolder(aListener->mListener.Clone());
   1262 
   1263  // If this is a script handler and we haven't yet
   1264  // compiled the event handler itself
   1265  if ((aListener->mListenerType == Listener::eJSEventListener) &&
   1266      aListener->mHandlerIsString) {
   1267    result =
   1268        CompileEventHandlerInternal(aListener, aTypeAtom, nullptr, nullptr);
   1269    aListener = nullptr;
   1270  }
   1271 
   1272  if (NS_SUCCEEDED(result)) {
   1273    Maybe<EventCallbackDebuggerNotificationGuard> dbgGuard;
   1274    if (dom::ChromeUtils::IsDevToolsOpened() || profiler_is_active()) {
   1275      dbgGuard.emplace(aCurrentTarget, aDOMEvent);
   1276    }
   1277    nsAutoMicroTask mt;
   1278 
   1279    // Event::currentTarget is set in EventDispatcher.
   1280    if (listenerHolder.HasWebIDLCallback()) {
   1281      ErrorResult rv;
   1282      listenerHolder.GetWebIDLCallback()->HandleEvent(aCurrentTarget,
   1283                                                      *aDOMEvent, rv);
   1284      result = rv.StealNSResult();
   1285    } else {
   1286      // listenerHolder is holding a stack ref here.
   1287      result = MOZ_KnownLive(listenerHolder.GetXPCOMCallback())
   1288                   ->HandleEvent(aDOMEvent);
   1289    }
   1290  }
   1291 
   1292  if (innerWindow) {
   1293    (void)innerWindow->SetEvent(oldWindowEvent);
   1294  }
   1295 
   1296  if (NS_FAILED(result)) {
   1297    aEvent->mFlags.mExceptionWasRaised = true;
   1298  }
   1299  aEvent->mFlags.mInPassiveListener = false;
   1300  return !aEvent->mFlags.mImmediatePropagationStopped;
   1301 }
   1302 
   1303 /* static */ EventMessage EventListenerManager::GetLegacyEventMessage(
   1304    EventMessage aEventMessage) {
   1305  // webkit-prefixed legacy events:
   1306  if (aEventMessage == eTransitionEnd) {
   1307    return eWebkitTransitionEnd;
   1308  }
   1309  if (aEventMessage == eAnimationStart) {
   1310    return eWebkitAnimationStart;
   1311  }
   1312  if (aEventMessage == eAnimationEnd) {
   1313    return eWebkitAnimationEnd;
   1314  }
   1315  if (aEventMessage == eAnimationIteration) {
   1316    return eWebkitAnimationIteration;
   1317  }
   1318 
   1319  switch (aEventMessage) {
   1320    case eFullscreenChange:
   1321      return eMozFullscreenChange;
   1322    case eFullscreenError:
   1323      return eMozFullscreenError;
   1324    default:
   1325      return aEventMessage;
   1326  }
   1327 }
   1328 
   1329 EventMessage EventListenerManager::GetEventMessage(nsAtom* aEventName) const {
   1330  if (mIsMainThreadELM) {
   1331    return nsContentUtils::GetEventMessage(aEventName);
   1332  }
   1333 
   1334  // The nsContentUtils event message hashtables aren't threadsafe, so just fall
   1335  // back to eUnidentifiedEvent.
   1336  return eUnidentifiedEvent;
   1337 }
   1338 
   1339 EventMessage EventListenerManager::GetEventMessageAndAtomForListener(
   1340    const nsAString& aType, nsAtom** aAtom) {
   1341  if (mIsMainThreadELM) {
   1342    return nsContentUtils::GetEventMessageAndAtomForListener(aType, aAtom);
   1343  }
   1344 
   1345  *aAtom = NS_Atomize(u"on"_ns + aType).take();
   1346  return eUnidentifiedEvent;
   1347 }
   1348 
   1349 already_AddRefed<nsPIDOMWindowInner> EventListenerManager::WindowFromListener(
   1350    Listener* aListener, nsAtom* aTypeAtom, bool aItemInShadowTree) {
   1351  nsCOMPtr<nsPIDOMWindowInner> innerWindow;
   1352  if (!aItemInShadowTree) {
   1353    if (aListener->mListener.HasWebIDLCallback()) {
   1354      CallbackObject* callback = aListener->mListener.GetWebIDLCallback();
   1355      nsIGlobalObject* global = nullptr;
   1356      if (callback) {
   1357        global = callback->IncumbentGlobalOrNull();
   1358      }
   1359      if (global) {
   1360        innerWindow = global->GetAsInnerWindow();  // Can be nullptr
   1361      }
   1362    } else if (mTarget) {
   1363      // This ensures `window.event` can be set properly for
   1364      // nsWindowRoot to handle KeyPress event.
   1365      if (aListener && aTypeAtom == nsGkAtoms::onkeypress &&
   1366          mTarget->IsRootWindow()) {
   1367        nsPIWindowRoot* root = mTarget->AsWindowRoot();
   1368        if (nsPIDOMWindowOuter* outerWindow = root->GetWindow()) {
   1369          innerWindow = outerWindow->GetCurrentInnerWindow();
   1370        }
   1371      } else {
   1372        // Can't get the global from
   1373        // listener->mListener.GetXPCOMCallback().
   1374        // In most cases, it would be the same as for
   1375        // the target, so let's do that.
   1376        if (nsIGlobalObject* global = mTarget->GetOwnerGlobal()) {
   1377          innerWindow = global->GetAsInnerWindow();
   1378        }
   1379      }
   1380    }
   1381  }
   1382  return innerWindow.forget();
   1383 }
   1384 
   1385 Maybe<size_t> EventListenerManager::EventListenerMap::EntryIndexForType(
   1386    nsAtom* aTypeAtom) const {
   1387  MOZ_ASSERT(aTypeAtom);
   1388 
   1389  size_t matchIndexOrInsertionPoint = 0;
   1390  bool foundMatch = BinarySearchIf(mEntries, 0, mEntries.Length(),
   1391                                   ListenerMapEntryComparator(aTypeAtom),
   1392                                   &matchIndexOrInsertionPoint);
   1393  return foundMatch ? Some(matchIndexOrInsertionPoint) : Nothing();
   1394 }
   1395 
   1396 Maybe<size_t> EventListenerManager::EventListenerMap::EntryIndexForAllEvents()
   1397    const {
   1398  // If we have an entry for "all events listeners", it'll be at the beginning
   1399  // of the list and its type atom will be null.
   1400  return !mEntries.IsEmpty() && mEntries[0].mTypeAtom == nullptr ? Some(0)
   1401                                                                 : Nothing();
   1402 }
   1403 
   1404 RefPtr<EventListenerManager::ListenerArray>
   1405 EventListenerManager::EventListenerMap::GetListenersForType(
   1406    nsAtom* aTypeAtom) const {
   1407  Maybe<size_t> index = EntryIndexForType(aTypeAtom);
   1408  return index ? mEntries[*index].mListeners : nullptr;
   1409 }
   1410 
   1411 RefPtr<EventListenerManager::ListenerArray>
   1412 EventListenerManager::EventListenerMap::GetListenersForAllEvents() const {
   1413  Maybe<size_t> index = EntryIndexForAllEvents();
   1414  return index ? mEntries[*index].mListeners : nullptr;
   1415 }
   1416 
   1417 RefPtr<EventListenerManager::ListenerArray>
   1418 EventListenerManager::EventListenerMap::GetOrCreateListenersForType(
   1419    nsAtom* aTypeAtom) {
   1420  MOZ_ASSERT(aTypeAtom);
   1421  size_t matchIndexOrInsertionPoint = 0;
   1422  bool foundMatch = BinarySearchIf(mEntries, 0, mEntries.Length(),
   1423                                   ListenerMapEntryComparator(aTypeAtom),
   1424                                   &matchIndexOrInsertionPoint);
   1425  if (foundMatch) {
   1426    return mEntries[matchIndexOrInsertionPoint].mListeners;
   1427  }
   1428  RefPtr<ListenerArray> listeners = MakeRefPtr<ListenerArray>();
   1429  mEntries.InsertElementAt(matchIndexOrInsertionPoint,
   1430                           EventListenerMapEntry{aTypeAtom, listeners});
   1431 
   1432  return listeners;
   1433 }
   1434 
   1435 RefPtr<EventListenerManager::ListenerArray>
   1436 EventListenerManager::EventListenerMap::GetOrCreateListenersForAllEvents() {
   1437  RefPtr<ListenerArray> listeners = GetListenersForAllEvents();
   1438  if (!listeners) {
   1439    listeners = MakeRefPtr<ListenerArray>();
   1440    mEntries.InsertElementAt(0, EventListenerMapEntry{nullptr, listeners});
   1441  }
   1442  return listeners;
   1443 }
   1444 
   1445 void EventListenerManager::HandleEventInternal(nsPresContext* aPresContext,
   1446                                               WidgetEvent* aEvent,
   1447                                               Event** aDOMEvent,
   1448                                               EventTarget* aCurrentTarget,
   1449                                               nsEventStatus* aEventStatus,
   1450                                               bool aItemInShadowTree) {
   1451  MOZ_ASSERT_IF(aEvent->mMessage != eUnidentifiedEvent, mIsMainThreadELM);
   1452 
   1453  // Set the value of the internal PreventDefault flag properly based on
   1454  // aEventStatus
   1455  if (!aEvent->DefaultPrevented() &&
   1456      *aEventStatus == nsEventStatus_eConsumeNoDefault) {
   1457    // Assume that if only aEventStatus claims that the event has already been
   1458    // consumed, the consumer is default event handler.
   1459    aEvent->PreventDefault();
   1460  }
   1461 
   1462  if (aEvent->mFlags.mImmediatePropagationStopped) {
   1463    return;
   1464  }
   1465 
   1466  Maybe<AutoHandlingUserInputStatePusher> userInputStatePusher;
   1467  Maybe<AutoPopupStatePusher> popupStatePusher;
   1468  if (mIsMainThreadELM) {
   1469    userInputStatePusher.emplace(UserActivation::IsUserInteractionEvent(aEvent),
   1470                                 aEvent);
   1471    popupStatePusher.emplace(
   1472        PopupBlocker::GetEventPopupControlState(aEvent, *aDOMEvent));
   1473  }
   1474 
   1475  RefPtr<nsAtom> typeAtom = nsContentUtils::GetEventType(aEvent);
   1476  if (!typeAtom) {
   1477    // Some messages don't have a corresponding type atom, e.g.
   1478    // eMouseEnterIntoWidget. These events can't have a listener, so we
   1479    // can stop here.
   1480    return;
   1481  }
   1482 
   1483  EventMessage eventMessage = aEvent->mMessage;
   1484  bool hasAnyListenerForEventType = false;
   1485 
   1486  // First, notify any "all events" listeners.
   1487  if (RefPtr<ListenerArray> listenersForAllEvents =
   1488          mListenerMap.GetListenersForAllEvents()) {
   1489    HandleEventWithListenerArray(listenersForAllEvents, typeAtom, eventMessage,
   1490                                 aPresContext, aEvent, aDOMEvent,
   1491                                 aCurrentTarget, aItemInShadowTree);
   1492    hasAnyListenerForEventType = true;
   1493  }
   1494 
   1495  // Now look for listeners for typeAtom, and call them if we have any.
   1496  bool hasAnyListenerMatchingGroup = false;
   1497  if (RefPtr<ListenerArray> listeners =
   1498          mListenerMap.GetListenersForType(typeAtom)) {
   1499    hasAnyListenerMatchingGroup = HandleEventWithListenerArray(
   1500        listeners, typeAtom, eventMessage, aPresContext, aEvent, aDOMEvent,
   1501        aCurrentTarget, aItemInShadowTree);
   1502    hasAnyListenerForEventType = true;
   1503  }
   1504 
   1505  if (!hasAnyListenerMatchingGroup && aEvent->IsTrusted()) {
   1506    // If we didn't find any matching listeners, and our event has a legacy
   1507    // version, check the listeners for the legacy version.
   1508    EventMessage legacyEventMessage = GetLegacyEventMessage(eventMessage);
   1509    if (legacyEventMessage != eventMessage) {
   1510      MOZ_ASSERT(
   1511          GetLegacyEventMessage(legacyEventMessage) == legacyEventMessage,
   1512          "Legacy event messages should not themselves have legacy versions");
   1513      RefPtr<nsAtom> legacyTypeAtom =
   1514          nsContentUtils::GetEventTypeFromMessage(legacyEventMessage);
   1515      if (RefPtr<ListenerArray> legacyListeners =
   1516              mListenerMap.GetListenersForType(legacyTypeAtom)) {
   1517        HandleEventWithListenerArray(
   1518            legacyListeners, legacyTypeAtom, legacyEventMessage, aPresContext,
   1519            aEvent, aDOMEvent, aCurrentTarget, aItemInShadowTree);
   1520        hasAnyListenerForEventType = true;
   1521      }
   1522    }
   1523  }
   1524 
   1525  aEvent->mCurrentTarget = nullptr;
   1526 
   1527  if (mIsMainThreadELM && !hasAnyListenerForEventType) {
   1528    if (aEvent->mMessage != eUnidentifiedEvent) {
   1529      mNoListenerForEvents[2] = mNoListenerForEvents[1];
   1530      mNoListenerForEvents[1] = mNoListenerForEvents[0];
   1531      mNoListenerForEvents[0] = aEvent->mMessage;
   1532    } else {
   1533      mNoListenerForEventAtom = aEvent->mSpecifiedEventType;
   1534    }
   1535  }
   1536 
   1537  if (aEvent->DefaultPrevented()) {
   1538    *aEventStatus = nsEventStatus_eConsumeNoDefault;
   1539  }
   1540 }
   1541 
   1542 bool EventListenerManager::HandleEventWithListenerArray(
   1543    ListenerArray* aListeners, nsAtom* aTypeAtom, EventMessage aEventMessage,
   1544    nsPresContext* aPresContext, WidgetEvent* aEvent, Event** aDOMEvent,
   1545    EventTarget* aCurrentTarget, bool aItemInShadowTree) {
   1546  auto ensureDOMEvent = [&]() {
   1547    if (!*aDOMEvent) {
   1548      // Lazily create the DOM event.
   1549      // This is tiny bit slow, but happens only once per event.
   1550      // Similar code also in EventDispatcher.
   1551      nsCOMPtr<EventTarget> et = aEvent->mOriginalTarget;
   1552      RefPtr<Event> event =
   1553          EventDispatcher::CreateEvent(et, aPresContext, aEvent, u""_ns);
   1554      event.forget(aDOMEvent);
   1555    }
   1556    return *aDOMEvent != nullptr;
   1557  };
   1558 
   1559  Maybe<EventMessageAutoOverride> eventMessageAutoOverride;
   1560  bool isOverridingEventMessage = aEvent->mMessage != aEventMessage;
   1561  bool hasAnyListenerMatchingGroup = false;
   1562  bool didReplaceOnceListener = false;
   1563 
   1564  for (Listener& listenerRef : aListeners->EndLimitedRange()) {
   1565    Listener* listener = &listenerRef;
   1566    if (!ListenerCanHandle(listener, aEvent)) {
   1567      continue;
   1568    }
   1569    hasAnyListenerMatchingGroup = true;
   1570 
   1571    // Check that the phase is same in event and event listener. Also check
   1572    // that the event is trusted or that the listener allows untrusted events.
   1573    if (!listener->MatchesEventPhase(aEvent) ||
   1574        !listener->AllowsEventTrustedness(aEvent)) {
   1575      continue;
   1576    }
   1577 
   1578    Maybe<Listener> listenerHolder;
   1579    if (listener->mFlags.mOnce) {
   1580      // Move the listener to the stack before handling the event.
   1581      // The order is important, otherwise the listener could be
   1582      // called again inside the listener.
   1583      listenerHolder.emplace(std::move(*listener));
   1584      listener = listenerHolder.ptr();
   1585      didReplaceOnceListener = true;
   1586    }
   1587    if (ensureDOMEvent()) {
   1588      if (isOverridingEventMessage && !eventMessageAutoOverride) {
   1589        // Override the domEvent's event-message (its .type) until we
   1590        // finish traversing listeners (when eventMessageAutoOverride
   1591        // destructs).
   1592        eventMessageAutoOverride.emplace(*aDOMEvent, aEventMessage);
   1593      }
   1594      if (!HandleEventSingleListener(listener, aTypeAtom, aEvent, *aDOMEvent,
   1595                                     aCurrentTarget, aItemInShadowTree)) {
   1596        break;
   1597      }
   1598    }
   1599  }
   1600 
   1601  if (didReplaceOnceListener) {
   1602    // If there are any once listeners replaced with a placeholder during the
   1603    // loop above, we need to clean up them here. Note that this could clear
   1604    // once listeners handled in some outer level as well, but that should not
   1605    // affect the result.
   1606    size_t oldLength = aListeners->Length();
   1607    aListeners->NonObservingRemoveElementsBy([](const Listener& aListener) {
   1608      return aListener.mListenerType == Listener::eNoListener;
   1609    });
   1610    size_t newLength = aListeners->Length();
   1611    if (newLength == 0) {
   1612      // Remove the entry that has now become empty.
   1613      mListenerMap.mEntries.RemoveElementsBy([](EventListenerMapEntry& entry) {
   1614        return entry.mListeners->IsEmpty();
   1615      });
   1616    }
   1617    if (newLength < oldLength) {
   1618      // Call NotifyEventListenerRemoved once for every removed listener.
   1619      size_t removedCount = oldLength - newLength;
   1620      for (size_t i = 0; i < removedCount; i++) {
   1621        NotifyEventListenerRemoved(aTypeAtom);
   1622      }
   1623      if (IsDeviceType(aTypeAtom)) {
   1624        // Call DisableDevice once for every removed listener.
   1625        for (size_t i = 0; i < removedCount; i++) {
   1626          DisableDevice(aTypeAtom);
   1627        }
   1628      }
   1629    }
   1630  }
   1631 
   1632  return hasAnyListenerMatchingGroup;
   1633 }
   1634 
   1635 void EventListenerManager::Disconnect() {
   1636  mTarget = nullptr;
   1637  RemoveAllListenersSilently();
   1638 }
   1639 
   1640 void EventListenerManager::AddEventListener(const nsAString& aType,
   1641                                            EventListenerHolder aListenerHolder,
   1642                                            bool aUseCapture,
   1643                                            bool aWantsUntrusted) {
   1644  EventListenerFlags flags;
   1645  flags.mCapture = aUseCapture;
   1646  flags.mAllowUntrustedEvents = aWantsUntrusted;
   1647  return AddEventListenerByType(std::move(aListenerHolder), aType, flags);
   1648 }
   1649 
   1650 void EventListenerManager::AddEventListener(
   1651    const nsAString& aType, EventListenerHolder aListenerHolder,
   1652    const dom::AddEventListenerOptionsOrBoolean& aOptions,
   1653    bool aWantsUntrusted) {
   1654  EventListenerFlags flags;
   1655  Optional<bool> passive;
   1656  AbortSignal* signal = nullptr;
   1657  if (aOptions.IsBoolean()) {
   1658    flags.mCapture = aOptions.GetAsBoolean();
   1659  } else {
   1660    const auto& options = aOptions.GetAsAddEventListenerOptions();
   1661    flags.mCapture = options.mCapture;
   1662    flags.mInSystemGroup = options.mMozSystemGroup;
   1663    flags.mOnce = options.mOnce;
   1664    if (options.mPassive.WasPassed()) {
   1665      passive.Construct(options.mPassive.Value());
   1666    }
   1667 
   1668    if (options.mSignal.WasPassed()) {
   1669      signal = &options.mSignal.Value();
   1670    }
   1671  }
   1672 
   1673  flags.mAllowUntrustedEvents = aWantsUntrusted;
   1674  return AddEventListenerByType(std::move(aListenerHolder), aType, flags,
   1675                                passive, signal);
   1676 }
   1677 
   1678 void EventListenerManager::RemoveEventListener(
   1679    const nsAString& aType, EventListenerHolder aListenerHolder,
   1680    bool aUseCapture) {
   1681  EventListenerFlags flags;
   1682  flags.mCapture = aUseCapture;
   1683  RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
   1684 }
   1685 
   1686 void EventListenerManager::RemoveEventListener(
   1687    const nsAString& aType, EventListenerHolder aListenerHolder,
   1688    const dom::EventListenerOptionsOrBoolean& aOptions) {
   1689  EventListenerFlags flags;
   1690  if (aOptions.IsBoolean()) {
   1691    flags.mCapture = aOptions.GetAsBoolean();
   1692  } else {
   1693    const auto& options = aOptions.GetAsEventListenerOptions();
   1694    flags.mCapture = options.mCapture;
   1695    flags.mInSystemGroup = options.mMozSystemGroup;
   1696  }
   1697  RemoveEventListenerByType(std::move(aListenerHolder), aType, flags);
   1698 }
   1699 
   1700 void EventListenerManager::AddListenerForAllEvents(EventListener* aDOMListener,
   1701                                                   bool aUseCapture,
   1702                                                   bool aWantsUntrusted,
   1703                                                   bool aSystemEventGroup) {
   1704  EventListenerFlags flags;
   1705  flags.mCapture = aUseCapture;
   1706  flags.mAllowUntrustedEvents = aWantsUntrusted;
   1707  flags.mInSystemGroup = aSystemEventGroup;
   1708  AddEventListenerInternal(EventListenerHolder(aDOMListener), eAllEvents,
   1709                           nullptr, flags, false, true);
   1710 }
   1711 
   1712 void EventListenerManager::RemoveListenerForAllEvents(
   1713    EventListener* aDOMListener, bool aUseCapture, bool aSystemEventGroup) {
   1714  EventListenerFlags flags;
   1715  flags.mCapture = aUseCapture;
   1716  flags.mInSystemGroup = aSystemEventGroup;
   1717  RemoveEventListenerInternal(EventListenerHolder(aDOMListener), nullptr, flags,
   1718                              true);
   1719 }
   1720 
   1721 bool EventListenerManager::HasListenersFor(const nsAString& aEventName) const {
   1722  RefPtr<nsAtom> atom = NS_Atomize(u"on"_ns + aEventName);
   1723  return HasListenersFor(atom);
   1724 }
   1725 
   1726 bool EventListenerManager::HasListenersFor(nsAtom* aEventNameWithOn) const {
   1727  return HasListenersForInternal(aEventNameWithOn, false);
   1728 }
   1729 
   1730 bool EventListenerManager::HasNonPassiveListenersFor(
   1731    const WidgetEvent* aEvent) const {
   1732  if (RefPtr<nsAtom> typeAtom = nsContentUtils::GetEventType(aEvent)) {
   1733    if (const auto& listeners = mListenerMap.GetListenersForType(typeAtom)) {
   1734      for (const Listener& listener : listeners->NonObservingRange()) {
   1735        if (!listener.mFlags.mPassive && ListenerCanHandle(&listener, aEvent)) {
   1736          return true;
   1737        }
   1738      }
   1739    }
   1740 
   1741    // After dispatching wheel, legacy mouse scroll events are dispatched
   1742    // and listeners on those can also default prevent the behavior.
   1743    if (aEvent->mMessage == eWheel) {
   1744      if (const auto& listeners =
   1745              mListenerMap.GetListenersForType(nsGkAtoms::onDOMMouseScroll)) {
   1746        for (const Listener& listener : listeners->NonObservingRange()) {
   1747          if (!listener.mFlags.mPassive &&
   1748              ListenerCanHandle(&listener, aEvent)) {
   1749            return true;
   1750          }
   1751        }
   1752      }
   1753      if (const auto& listeners = mListenerMap.GetListenersForType(
   1754              nsGkAtoms::onMozMousePixelScroll)) {
   1755        for (const Listener& listener : listeners->NonObservingRange()) {
   1756          if (!listener.mFlags.mPassive &&
   1757              ListenerCanHandle(&listener, aEvent)) {
   1758            return true;
   1759          }
   1760        }
   1761      }
   1762    }
   1763  }
   1764 
   1765  return false;
   1766 }
   1767 
   1768 bool EventListenerManager::ListenerCanHandle(const Listener* aListener,
   1769                                             const WidgetEvent* aEvent) const {
   1770  if (aListener->mListenerType == Listener::eNoListener) {
   1771    // The listener is a placeholder value of a removed "once" listener.
   1772    return false;
   1773  }
   1774  if (!aListener->mEnabled) {
   1775    // The listener has been disabled, for example by devtools.
   1776    return false;
   1777  }
   1778  if (!aListener->MatchesEventGroup(aEvent)) {
   1779    return false;
   1780  }
   1781 
   1782  return true;
   1783 }
   1784 
   1785 bool EventListenerManager::HasNonSystemGroupListenersFor(
   1786    nsAtom* aEventNameWithOn) const {
   1787  return HasListenersForInternal(aEventNameWithOn, true);
   1788 }
   1789 
   1790 bool EventListenerManager::HasListenersForInternal(
   1791    nsAtom* aEventNameWithOn, bool aIgnoreSystemGroup) const {
   1792 #ifdef DEBUG
   1793  nsAutoString name;
   1794  aEventNameWithOn->ToString(name);
   1795 #endif
   1796  NS_ASSERTION(StringBeginsWith(name, u"on"_ns),
   1797               "Event name does not start with 'on'");
   1798  RefPtr<ListenerArray> listeners =
   1799      mListenerMap.GetListenersForType(aEventNameWithOn);
   1800  if (!listeners) {
   1801    return false;
   1802  }
   1803 
   1804  MOZ_ASSERT(!listeners->IsEmpty());
   1805 
   1806  if (!aIgnoreSystemGroup) {
   1807    return true;
   1808  }
   1809 
   1810  // Check if any non-system-group listeners exist in `listeners`.
   1811  for (const auto& listener : listeners->NonObservingRange()) {
   1812    if (!listener.mFlags.mInSystemGroup) {
   1813      return true;
   1814    }
   1815  }
   1816 
   1817  return false;
   1818 }
   1819 
   1820 bool EventListenerManager::HasListeners() const {
   1821  return !mListenerMap.IsEmpty();
   1822 }
   1823 
   1824 nsresult EventListenerManager::GetListenerInfo(
   1825    nsTArray<RefPtr<nsIEventListenerInfo>>& aList) {
   1826  nsCOMPtr<EventTarget> target = mTarget;
   1827  NS_ENSURE_STATE(target);
   1828  aList.Clear();
   1829  for (const auto& entry : mListenerMap.mEntries) {
   1830    for (const Listener& listener : entry.mListeners->ForwardRange()) {
   1831      // If this is a script handler and we haven't yet
   1832      // compiled the event handler itself go ahead and compile it
   1833      if (listener.mListenerType == Listener::eJSEventListener &&
   1834          listener.mHandlerIsString) {
   1835        CompileEventHandlerInternal(const_cast<Listener*>(&listener),
   1836                                    entry.mTypeAtom, nullptr, nullptr);
   1837      }
   1838      nsAutoString eventType;
   1839      if (listener.mAllEvents) {
   1840        eventType.SetIsVoid(true);
   1841      } else if (listener.mListenerType == Listener::eNoListener) {
   1842        continue;
   1843      } else {
   1844        eventType.Assign(Substring(nsDependentAtomString(entry.mTypeAtom), 2));
   1845      }
   1846 
   1847      JS::Rooted<JSObject*> callback(RootingCx());
   1848      JS::Rooted<JSObject*> callbackGlobal(RootingCx());
   1849      if (JSEventHandler* handler = listener.GetJSEventHandler()) {
   1850        if (handler->GetTypedEventHandler().HasEventHandler()) {
   1851          CallbackFunction* callbackFun = handler->GetTypedEventHandler().Ptr();
   1852          callback = callbackFun->CallableOrNull();
   1853          callbackGlobal = callbackFun->CallbackGlobalOrNull();
   1854          if (!callback) {
   1855            // This will be null for cross-compartment event listeners
   1856            // which have been destroyed.
   1857            continue;
   1858          }
   1859        }
   1860      } else if (listener.mListenerType == Listener::eWebIDLListener) {
   1861        EventListener* listenerCallback =
   1862            listener.mListener.GetWebIDLCallback();
   1863        callback = listenerCallback->CallbackOrNull();
   1864        callbackGlobal = listenerCallback->CallbackGlobalOrNull();
   1865        if (!callback) {
   1866          // This will be null for cross-compartment event listeners
   1867          // which have been destroyed.
   1868          continue;
   1869        }
   1870      }
   1871 
   1872      RefPtr<EventListenerInfo> info = new EventListenerInfo(
   1873          this, eventType, callback, callbackGlobal, listener.mFlags.mCapture,
   1874          listener.mFlags.mAllowUntrustedEvents, listener.mFlags.mInSystemGroup,
   1875          listener.mListenerIsHandler);
   1876      aList.AppendElement(info.forget());
   1877    }
   1878  }
   1879  return NS_OK;
   1880 }
   1881 
   1882 EventListenerManager::Listener* EventListenerManager::GetListenerFor(
   1883    nsAString& aType, JSObject* aListener, bool aCapturing,
   1884    bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler) {
   1885  NS_ENSURE_TRUE(aListener, nullptr);
   1886 
   1887  RefPtr<ListenerArray> listeners = ([&]() -> RefPtr<ListenerArray> {
   1888    if (aType.IsVoid()) {
   1889      return mListenerMap.GetListenersForAllEvents();
   1890    }
   1891 
   1892    for (auto& mapEntry : mListenerMap.mEntries) {
   1893      if (RefPtr<nsAtom> typeAtom = mapEntry.mTypeAtom) {
   1894        if (Substring(nsDependentAtomString(typeAtom), 2).Equals(aType)) {
   1895          return mapEntry.mListeners;
   1896        }
   1897      }
   1898    }
   1899 
   1900    return nullptr;
   1901  })();
   1902 
   1903  if (!listeners) {
   1904    return nullptr;
   1905  }
   1906 
   1907  for (Listener& listener : listeners->ForwardRange()) {
   1908    if (listener.mListenerType == Listener::eNoListener) {
   1909      continue;
   1910    }
   1911 
   1912    if (listener.mFlags.mCapture != aCapturing ||
   1913        listener.mFlags.mAllowUntrustedEvents != aAllowsUntrusted ||
   1914        listener.mFlags.mInSystemGroup != aInSystemEventGroup) {
   1915      continue;
   1916    }
   1917 
   1918    if (aIsHandler) {
   1919      if (JSEventHandler* handler = listener.GetJSEventHandler()) {
   1920        if (handler->GetTypedEventHandler().HasEventHandler()) {
   1921          if (handler->GetTypedEventHandler().Ptr()->CallableOrNull() ==
   1922              aListener) {
   1923            return &listener;
   1924          }
   1925        }
   1926      }
   1927    } else if (listener.mListenerType == Listener::eWebIDLListener &&
   1928               listener.mListener.GetWebIDLCallback()->CallbackOrNull() ==
   1929                   aListener) {
   1930      return &listener;
   1931    }
   1932  }
   1933  return nullptr;
   1934 }
   1935 
   1936 nsresult EventListenerManager::IsListenerEnabled(
   1937    nsAString& aType, JSObject* aListener, bool aCapturing,
   1938    bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
   1939    bool* aEnabled) {
   1940  Listener* listener =
   1941      GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
   1942                     aInSystemEventGroup, aIsHandler);
   1943  NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
   1944  *aEnabled = listener->mEnabled;
   1945  return NS_OK;
   1946 }
   1947 
   1948 nsresult EventListenerManager::SetListenerEnabled(
   1949    nsAString& aType, JSObject* aListener, bool aCapturing,
   1950    bool aAllowsUntrusted, bool aInSystemEventGroup, bool aIsHandler,
   1951    bool aEnabled) {
   1952  Listener* listener =
   1953      GetListenerFor(aType, aListener, aCapturing, aAllowsUntrusted,
   1954                     aInSystemEventGroup, aIsHandler);
   1955  NS_ENSURE_TRUE(listener, NS_ERROR_NOT_AVAILABLE);
   1956  listener->mEnabled = aEnabled;
   1957  if (aEnabled) {
   1958    // We may have enabled some listener, clear the cache for which events
   1959    // we don't have listeners.
   1960    ClearNoListenersForEvents();
   1961    mNoListenerForEventAtom = nullptr;
   1962  }
   1963  return NS_OK;
   1964 }
   1965 
   1966 bool EventListenerManager::HasUnloadListeners() {
   1967  return mListenerMap.GetListenersForType(nsGkAtoms::onunload) != nullptr;
   1968 }
   1969 
   1970 bool EventListenerManager::HasBeforeUnloadListeners() {
   1971  return mListenerMap.GetListenersForType(nsGkAtoms::onbeforeunload) != nullptr;
   1972 }
   1973 
   1974 void EventListenerManager::SetEventHandler(nsAtom* aEventName,
   1975                                           EventHandlerNonNull* aHandler) {
   1976  if (!aHandler) {
   1977    RemoveEventHandler(aEventName);
   1978    return;
   1979  }
   1980 
   1981  // Untrusted events are always permitted for non-chrome script
   1982  // handlers.
   1983  SetEventHandlerInternal(
   1984      aEventName, TypedEventHandler(aHandler),
   1985      !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
   1986 }
   1987 
   1988 void EventListenerManager::SetEventHandler(
   1989    OnErrorEventHandlerNonNull* aHandler) {
   1990  if (!aHandler) {
   1991    RemoveEventHandler(nsGkAtoms::onerror);
   1992    return;
   1993  }
   1994 
   1995  // Untrusted events are always permitted on workers and for non-chrome script
   1996  // on the main thread.
   1997  bool allowUntrusted = !mIsMainThreadELM || !nsContentUtils::IsCallerChrome();
   1998 
   1999  SetEventHandlerInternal(nsGkAtoms::onerror, TypedEventHandler(aHandler),
   2000                          allowUntrusted);
   2001 }
   2002 
   2003 void EventListenerManager::SetEventHandler(
   2004    OnBeforeUnloadEventHandlerNonNull* aHandler) {
   2005  if (!aHandler) {
   2006    RemoveEventHandler(nsGkAtoms::onbeforeunload);
   2007    return;
   2008  }
   2009 
   2010  // Untrusted events are always permitted for non-chrome script
   2011  // handlers.
   2012  SetEventHandlerInternal(
   2013      nsGkAtoms::onbeforeunload, TypedEventHandler(aHandler),
   2014      !mIsMainThreadELM || !nsContentUtils::IsCallerChrome());
   2015 }
   2016 
   2017 const TypedEventHandler* EventListenerManager::GetTypedEventHandler(
   2018    nsAtom* aEventName) {
   2019  Listener* listener = FindEventHandler(aEventName);
   2020 
   2021  if (!listener) {
   2022    return nullptr;
   2023  }
   2024 
   2025  JSEventHandler* jsEventHandler = listener->GetJSEventHandler();
   2026 
   2027  if (listener->mHandlerIsString) {
   2028    CompileEventHandlerInternal(listener, aEventName, nullptr, nullptr);
   2029  }
   2030 
   2031  const TypedEventHandler& typedHandler =
   2032      jsEventHandler->GetTypedEventHandler();
   2033  return typedHandler.HasEventHandler() ? &typedHandler : nullptr;
   2034 }
   2035 
   2036 size_t EventListenerManager::SizeOfIncludingThis(
   2037    MallocSizeOf aMallocSizeOf) const {
   2038  return aMallocSizeOf(this) + mListenerMap.SizeOfExcludingThis(aMallocSizeOf);
   2039 }
   2040 
   2041 size_t EventListenerManager::EventListenerMap::SizeOfExcludingThis(
   2042    MallocSizeOf aMallocSizeOf) const {
   2043  size_t n = mEntries.ShallowSizeOfExcludingThis(aMallocSizeOf);
   2044  for (const auto& entry : mEntries) {
   2045    n += entry.SizeOfExcludingThis(aMallocSizeOf);
   2046  }
   2047  return n;
   2048 }
   2049 
   2050 size_t EventListenerManager::EventListenerMapEntry::SizeOfExcludingThis(
   2051    MallocSizeOf aMallocSizeOf) const {
   2052  return mListeners->SizeOfIncludingThis(aMallocSizeOf);
   2053 }
   2054 
   2055 size_t EventListenerManager::ListenerArray::SizeOfIncludingThis(
   2056    MallocSizeOf aMallocSizeOf) const {
   2057  size_t n = aMallocSizeOf(this);
   2058  n += ShallowSizeOfExcludingThis(aMallocSizeOf);
   2059  for (const auto& listener : NonObservingRange()) {
   2060    JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
   2061    if (jsEventHandler) {
   2062      n += jsEventHandler->SizeOfIncludingThis(aMallocSizeOf);
   2063    }
   2064  }
   2065  return n;
   2066 }
   2067 
   2068 uint32_t EventListenerManager::ListenerCount() const {
   2069  uint32_t count = 0;
   2070  for (const auto& entry : mListenerMap.mEntries) {
   2071    count += entry.mListeners->Length();
   2072  }
   2073  return count;
   2074 }
   2075 
   2076 void EventListenerManager::MarkForCC() {
   2077  for (const auto& entry : mListenerMap.mEntries) {
   2078    for (const auto& listener : entry.mListeners->NonObservingRange()) {
   2079      JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
   2080      if (jsEventHandler) {
   2081        const TypedEventHandler& typedHandler =
   2082            jsEventHandler->GetTypedEventHandler();
   2083        if (typedHandler.HasEventHandler()) {
   2084          typedHandler.Ptr()->MarkForCC();
   2085        }
   2086      } else if (listener.mListenerType == Listener::eWebIDLListener) {
   2087        listener.mListener.GetWebIDLCallback()->MarkForCC();
   2088      }
   2089    }
   2090  }
   2091  if (mRefCnt.IsPurple()) {
   2092    mRefCnt.RemovePurple();
   2093  }
   2094 }
   2095 
   2096 void EventListenerManager::TraceListeners(JSTracer* aTrc) {
   2097  for (const auto& entry : mListenerMap.mEntries) {
   2098    for (const auto& listener : entry.mListeners->NonObservingRange()) {
   2099      JSEventHandler* jsEventHandler = listener.GetJSEventHandler();
   2100      if (jsEventHandler) {
   2101        const TypedEventHandler& typedHandler =
   2102            jsEventHandler->GetTypedEventHandler();
   2103        if (typedHandler.HasEventHandler()) {
   2104          mozilla::TraceScriptHolder(typedHandler.Ptr(), aTrc);
   2105        }
   2106      } else if (listener.mListenerType == Listener::eWebIDLListener) {
   2107        mozilla::TraceScriptHolder(listener.mListener.GetWebIDLCallback(),
   2108                                   aTrc);
   2109      }
   2110      // We might have eWrappedJSListener, but that is the legacy type for
   2111      // JS implemented event listeners, and trickier to handle here.
   2112    }
   2113  }
   2114 }
   2115 
   2116 bool EventListenerManager::HasNonSystemGroupListenersForUntrustedKeyEvents() {
   2117  for (const auto& entry : mListenerMap.mEntries) {
   2118    if (entry.mTypeAtom != nsGkAtoms::onkeydown &&
   2119        entry.mTypeAtom != nsGkAtoms::onkeypress &&
   2120        entry.mTypeAtom != nsGkAtoms::onkeyup) {
   2121      continue;
   2122    }
   2123    for (const auto& listener : entry.mListeners->NonObservingRange()) {
   2124      if (!listener.mFlags.mInSystemGroup &&
   2125          listener.mFlags.mAllowUntrustedEvents) {
   2126        return true;
   2127      }
   2128    }
   2129  }
   2130  return false;
   2131 }
   2132 
   2133 bool EventListenerManager::
   2134    HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents() {
   2135  for (const auto& entry : mListenerMap.mEntries) {
   2136    if (entry.mTypeAtom != nsGkAtoms::onkeydown &&
   2137        entry.mTypeAtom != nsGkAtoms::onkeypress &&
   2138        entry.mTypeAtom != nsGkAtoms::onkeyup) {
   2139      continue;
   2140    }
   2141    for (const auto& listener : entry.mListeners->NonObservingRange()) {
   2142      if (!listener.mFlags.mPassive && !listener.mFlags.mInSystemGroup &&
   2143          listener.mFlags.mAllowUntrustedEvents) {
   2144        return true;
   2145      }
   2146    }
   2147  }
   2148  return false;
   2149 }
   2150 
   2151 bool EventListenerManager::HasApzAwareListeners() {
   2152  if (!mIsMainThreadELM) {
   2153    return false;
   2154  }
   2155 
   2156  for (const auto& entry : mListenerMap.mEntries) {
   2157    if (!IsApzAwareEvent(entry.mTypeAtom)) {
   2158      continue;
   2159    }
   2160    for (const auto& listener : entry.mListeners->NonObservingRange()) {
   2161      if (!listener.mFlags.mPassive) {
   2162        return true;
   2163      }
   2164    }
   2165  }
   2166  return false;
   2167 }
   2168 
   2169 static bool IsWheelEventType(nsAtom* aEvent) {
   2170  if (aEvent == nsGkAtoms::onwheel || aEvent == nsGkAtoms::onDOMMouseScroll ||
   2171      aEvent == nsGkAtoms::onmousewheel ||
   2172      aEvent == nsGkAtoms::onMozMousePixelScroll) {
   2173    return true;
   2174  }
   2175  return false;
   2176 }
   2177 
   2178 bool EventListenerManager::IsApzAwareEvent(nsAtom* aEvent) {
   2179  if (IsWheelEventType(aEvent)) {
   2180    return true;
   2181  }
   2182  // In theory we should schedule a repaint if the touch event pref changes,
   2183  // because the event regions might be out of date. In practice that seems like
   2184  // overkill because users generally shouldn't be flipping this pref, much
   2185  // less expecting touch listeners on the page to immediately start preventing
   2186  // scrolling without so much as a repaint. Tests that we write can work
   2187  // around this constraint easily enough.
   2188  if (aEvent == nsGkAtoms::ontouchstart || aEvent == nsGkAtoms::ontouchmove) {
   2189    return TouchEvent::PrefEnabled(
   2190        nsContentUtils::GetDocShellForEventTarget(mTarget));
   2191  }
   2192  return false;
   2193 }
   2194 
   2195 bool EventListenerManager::HasNonPassiveWheelListener() {
   2196  MOZ_ASSERT(NS_IsMainThread());
   2197  for (const auto& entry : mListenerMap.mEntries) {
   2198    if (!IsWheelEventType(entry.mTypeAtom)) {
   2199      continue;
   2200    }
   2201    for (const auto& listener : entry.mListeners->NonObservingRange()) {
   2202      if (!listener.mFlags.mPassive) {
   2203        return true;
   2204      }
   2205    }
   2206  }
   2207  return false;
   2208 }
   2209 
   2210 void EventListenerManager::RemoveAllListeners() {
   2211  while (!mListenerMap.IsEmpty()) {
   2212    size_t entryIndex = mListenerMap.mEntries.Length() - 1;
   2213    EventListenerMapEntry& entry = mListenerMap.mEntries[entryIndex];
   2214    RefPtr<nsAtom> type = entry.mTypeAtom;
   2215    MOZ_ASSERT(!entry.mListeners->IsEmpty());
   2216    size_t idx = entry.mListeners->Length() - 1;
   2217    entry.mListeners->RemoveElementAt(idx);
   2218    if (entry.mListeners->IsEmpty()) {
   2219      mListenerMap.mEntries.RemoveElementAt(entryIndex);
   2220    }
   2221    NotifyEventListenerRemoved(type);
   2222    if (IsDeviceType(type)) {
   2223      DisableDevice(type);
   2224    }
   2225  }
   2226 }
   2227 
   2228 already_AddRefed<nsIScriptGlobalObject>
   2229 EventListenerManager::GetScriptGlobalAndDocument(Document** aDoc) {
   2230  nsCOMPtr<Document> doc;
   2231  nsCOMPtr<nsPIDOMWindowInner> win;
   2232  if (nsINode* node = nsINode::FromEventTargetOrNull(mTarget)) {
   2233    // Try to get context from doc
   2234    doc = node->OwnerDoc();
   2235    if (doc->IsLoadedAsData()) {
   2236      return nullptr;
   2237    }
   2238 
   2239    win = do_QueryInterface(doc->GetScopeObject());
   2240  } else if ((win = GetTargetAsInnerWindow())) {
   2241    doc = win->GetExtantDoc();
   2242  }
   2243 
   2244  if (!win || !win->IsCurrentInnerWindow()) {
   2245    return nullptr;
   2246  }
   2247 
   2248  doc.forget(aDoc);
   2249  nsCOMPtr<nsIScriptGlobalObject> global = do_QueryInterface(win);
   2250  return global.forget();
   2251 }
   2252 
   2253 EventListenerManager::ListenerSignalFollower::ListenerSignalFollower(
   2254    EventListenerManager* aListenerManager,
   2255    EventListenerManager::Listener* aListener, nsAtom* aTypeAtom)
   2256    : dom::AbortFollower(),
   2257      mListenerManager(aListenerManager),
   2258      mListener(aListener->mListener.Clone()),
   2259      mTypeAtom(aTypeAtom),
   2260      mAllEvents(aListener->mAllEvents),
   2261      mFlags(aListener->mFlags) {};
   2262 
   2263 NS_IMPL_CYCLE_COLLECTION_CLASS(EventListenerManager::ListenerSignalFollower)
   2264 
   2265 NS_IMPL_CYCLE_COLLECTING_ADDREF(EventListenerManager::ListenerSignalFollower)
   2266 NS_IMPL_CYCLE_COLLECTING_RELEASE(EventListenerManager::ListenerSignalFollower)
   2267 
   2268 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(
   2269    EventListenerManager::ListenerSignalFollower)
   2270  NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mListener)
   2271 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
   2272 
   2273 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(
   2274    EventListenerManager::ListenerSignalFollower)
   2275  NS_IMPL_CYCLE_COLLECTION_UNLINK(mListener)
   2276  tmp->mListenerManager = nullptr;
   2277 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
   2278 
   2279 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(
   2280    EventListenerManager::ListenerSignalFollower)
   2281  NS_INTERFACE_MAP_ENTRY(nsISupports)
   2282 NS_INTERFACE_MAP_END
   2283 
   2284 void EventListenerManager::ListenerSignalFollower::RunAbortAlgorithm() {
   2285  if (mListenerManager) {
   2286    RefPtr<EventListenerManager> elm = mListenerManager;
   2287    mListenerManager = nullptr;
   2288    elm->RemoveEventListenerInternal(std::move(mListener), mTypeAtom, mFlags,
   2289                                     mAllEvents);
   2290  }
   2291 }
   2292 
   2293 }  // namespace mozilla