DXR is a code search and navigation tool aimed at making sense of large projects. It supports full-text and regex searches as well as structural queries.

Implementation

Mercurial (0f28c3a1e6a7)

VCS Links

Line Code
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* vim: set ts=8 sts=2 et sw=2 tw=80: */
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

#ifndef mozilla_EventListenerManager_h_
#define mozilla_EventListenerManager_h_

#include "mozilla/BasicEvents.h"
#include "mozilla/dom/EventListenerBinding.h"
#include "mozilla/JSEventHandler.h"
#include "mozilla/MemoryReporting.h"
#include "nsCOMPtr.h"
#include "nsCycleCollectionParticipant.h"
#include "nsGkAtoms.h"
#include "nsIDOMEventListener.h"
#include "nsTObserverArray.h"

class nsIDocShell;
class nsIDOMEvent;
class nsIEventListenerInfo;
class nsPIDOMWindowInner;
class JSTracer;

struct EventTypeData;

template <class T>
class nsCOMArray;

namespace mozilla {

class ELMCreationDetector;
class EventListenerManager;

namespace dom {
class EventTarget;
class Element;
}  // namespace dom

typedef dom::CallbackObjectHolder<dom::EventListener, nsIDOMEventListener>
    EventListenerHolder;

struct EventListenerFlags {
  friend class EventListenerManager;

 private:
  // If mListenerIsJSListener is true, the listener is implemented by JS.
  // Otherwise, it's implemented by native code or JS but it's wrapped.
  bool mListenerIsJSListener : 1;

 public:
  // If mCapture is true, it means the listener captures the event.  Otherwise,
  // it's listening at bubbling phase.
  bool mCapture : 1;
  // If mInSystemGroup is true, the listener is listening to the events in the
  // system group.
  bool mInSystemGroup : 1;
  // If mAllowUntrustedEvents is true, the listener is listening to the
  // untrusted events too.
  bool mAllowUntrustedEvents : 1;
  // If mPassive is true, the listener will not be calling preventDefault on the
  // event. (If it does call preventDefault, we should ignore it).
  bool mPassive : 1;
  // If mOnce is true, the listener will be removed from the manager before it
  // is invoked, so that it would only be invoked once.
  bool mOnce : 1;

  EventListenerFlags()
      : mListenerIsJSListener(false),
        mCapture(false),
        mInSystemGroup(false),
        mAllowUntrustedEvents(false),
        mPassive(false),
        mOnce(false) {}

  bool EqualsForAddition(const EventListenerFlags& aOther) const {
    return (mCapture == aOther.mCapture &&
            mInSystemGroup == aOther.mInSystemGroup &&
            mListenerIsJSListener == aOther.mListenerIsJSListener &&
            mAllowUntrustedEvents == aOther.mAllowUntrustedEvents);
    // Don't compare mPassive or mOnce
  }

  bool EqualsForRemoval(const EventListenerFlags& aOther) const {
    return (mCapture == aOther.mCapture &&
            mInSystemGroup == aOther.mInSystemGroup &&
            mListenerIsJSListener == aOther.mListenerIsJSListener);
    // Don't compare mAllowUntrustedEvents, mPassive, or mOnce
  }
};

inline EventListenerFlags TrustedEventsAtBubble() {
  EventListenerFlags flags;
  return flags;
}

inline EventListenerFlags TrustedEventsAtCapture() {
  EventListenerFlags flags;
  flags.mCapture = true;
  return flags;
}

inline EventListenerFlags AllEventsAtBubble() {
  EventListenerFlags flags;
  flags.mAllowUntrustedEvents = true;
  return flags;
}

inline EventListenerFlags AllEventsAtCapture() {
  EventListenerFlags flags;
  flags.mCapture = true;
  flags.mAllowUntrustedEvents = true;
  return flags;
}

inline EventListenerFlags TrustedEventsAtSystemGroupBubble() {
  EventListenerFlags flags;
  flags.mInSystemGroup = true;
  return flags;
}

inline EventListenerFlags TrustedEventsAtSystemGroupCapture() {
  EventListenerFlags flags;
  flags.mCapture = true;
  flags.mInSystemGroup = true;
  return flags;
}

inline EventListenerFlags AllEventsAtSystemGroupBubble() {
  EventListenerFlags flags;
  flags.mInSystemGroup = true;
  flags.mAllowUntrustedEvents = true;
  return flags;
}

inline EventListenerFlags AllEventsAtSystemGroupCapture() {
  EventListenerFlags flags;
  flags.mCapture = true;
  flags.mInSystemGroup = true;
  flags.mAllowUntrustedEvents = true;
  return flags;
}

class EventListenerManagerBase {
 protected:
  EventListenerManagerBase();

  EventMessage mNoListenerForEvent;
  uint16_t mMayHavePaintEventListener : 1;
  uint16_t mMayHaveMutationListeners : 1;
  uint16_t mMayHaveCapturingListeners : 1;
  uint16_t mMayHaveSystemGroupListeners : 1;
  uint16_t mMayHaveTouchEventListener : 1;
  uint16_t mMayHaveMouseEnterLeaveEventListener : 1;
  uint16_t mMayHavePointerEnterLeaveEventListener : 1;
  uint16_t mMayHaveKeyEventListener : 1;
  uint16_t mMayHaveInputOrCompositionEventListener : 1;
  uint16_t mMayHaveSelectionChangeEventListener : 1;
  uint16_t mClearingListeners : 1;
  uint16_t mIsMainThreadELM : 1;
  // uint16_t mUnused : 4;
};

/*
 * Event listener manager
 */

class EventListenerManager final : public EventListenerManagerBase {
  ~EventListenerManager();

 public:
  struct Listener {
    EventListenerHolder mListener;
    RefPtr<nsAtom> mTypeAtom;  // for the main thread
    nsString mTypeString;      // for non-main-threads
    EventMessage mEventMessage;

    enum ListenerType : uint8_t {
      eNoListener,
      eNativeListener,
      eJSEventListener,
      eWrappedJSListener,
      eWebIDLListener,
    };
    ListenerType mListenerType;

    bool mListenerIsHandler : 1;
    bool mHandlerIsString : 1;
    bool mAllEvents : 1;
    bool mIsChrome : 1;

    EventListenerFlags mFlags;

    JSEventHandler* GetJSEventHandler() const {
      return (mListenerType == eJSEventListener)
                 ? static_cast<JSEventHandler*>(mListener.GetXPCOMCallback())
                 : nullptr;
    }

    Listener()
        : mEventMessage(eVoidEvent),
          mListenerType(eNoListener),
          mListenerIsHandler(false),
          mHandlerIsString(false),
          mAllEvents(false),
          mIsChrome(false) {}

    Listener(Listener&& aOther)
        : mListener(Move(aOther.mListener)),
          mTypeAtom(aOther.mTypeAtom.forget()),
          mTypeString(aOther.mTypeString),
          mEventMessage(aOther.mEventMessage),
          mListenerType(aOther.mListenerType),
          mListenerIsHandler(aOther.mListenerIsHandler),
          mHandlerIsString(aOther.mHandlerIsString),
          mAllEvents(aOther.mAllEvents),
          mIsChrome(aOther.mIsChrome) {
      aOther.mTypeString.Truncate();
      aOther.mEventMessage = eVoidEvent;
      aOther.mListenerType = eNoListener;
      aOther.mListenerIsHandler = false;
      aOther.mHandlerIsString = false;
      aOther.mAllEvents = false;
      aOther.mIsChrome = false;
    }

    ~Listener() {
      if ((mListenerType == eJSEventListener) && mListener) {
        static_cast<JSEventHandler*>(mListener.GetXPCOMCallback())
            ->Disconnect();
      }
    }

    MOZ_ALWAYS_INLINE bool IsListening(const WidgetEvent* aEvent) const {
      if (mFlags.mInSystemGroup != aEvent->mFlags.mInSystemGroup) {
        return false;
      }
      // FIXME Should check !mFlags.mCapture when the event is in target
      //       phase because capture phase event listeners should not be fired.
      //       But it breaks at least <xul:dialog>'s buttons. Bug 235441.
      return ((mFlags.mCapture && aEvent->mFlags.mInCapturePhase) ||
              (!mFlags.mCapture && aEvent->mFlags.mInBubblingPhase));
    }
  };

  explicit EventListenerManager(dom::EventTarget* aTarget);

  NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(EventListenerManager)

  NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(EventListenerManager)

  void AddEventListener(const nsAString& aType, nsIDOMEventListener* aListener,
                        bool aUseCapture, bool aWantsUntrusted) {
    AddEventListener(aType, EventListenerHolder(aListener), aUseCapture,
                     aWantsUntrusted);
  }
  void AddEventListener(const nsAString& aType, dom::EventListener* aListener,
                        const dom::AddEventListenerOptionsOrBoolean& aOptions,
                        bool aWantsUntrusted) {
    AddEventListener(aType, EventListenerHolder(aListener), aOptions,
                     aWantsUntrusted);
  }
  void RemoveEventListener(const nsAString& aType,
                           nsIDOMEventListener* aListener, bool aUseCapture) {
    RemoveEventListener(aType, EventListenerHolder(aListener), aUseCapture);
  }
  void RemoveEventListener(const nsAString& aType,
                           dom::EventListener* aListener,
                           const dom::EventListenerOptionsOrBoolean& aOptions) {
    RemoveEventListener(aType, EventListenerHolder(aListener), aOptions);
  }

  void AddListenerForAllEvents(nsIDOMEventListener* aListener, bool aUseCapture,
                               bool aWantsUntrusted, bool aSystemEventGroup);
  void RemoveListenerForAllEvents(nsIDOMEventListener* aListener,
                                  bool aUseCapture, bool aSystemEventGroup);

  /**
   * Sets events listeners of all types.
   * @param an event listener
   */
  void AddEventListenerByType(nsIDOMEventListener* aListener,
                              const nsAString& type,
                              const EventListenerFlags& aFlags) {
    AddEventListenerByType(EventListenerHolder(aListener), type, aFlags);
  }
  void AddEventListenerByType(EventListenerHolder aListener,
                              const nsAString& type,
                              const EventListenerFlags& aFlags);
  void RemoveEventListenerByType(nsIDOMEventListener* aListener,
                                 const nsAString& type,
                                 const EventListenerFlags& aFlags) {
    RemoveEventListenerByType(EventListenerHolder(aListener), type, aFlags);
  }
  void RemoveEventListenerByType(EventListenerHolder aListener,
                                 const nsAString& type,
                                 const EventListenerFlags& aFlags);

  /**
   * Sets the current "inline" event listener for aName to be a
   * function compiled from aFunc if !aDeferCompilation.  If
   * aDeferCompilation, then we assume that we can get the string from
   * mTarget later and compile lazily.
   *
   * aElement, if not null, is the element the string is associated with.
   */
  // XXXbz does that play correctly with nodes being adopted across
  // documents?  Need to double-check the spec here.
  nsresult SetEventHandler(nsAtom* aName, const nsAString& aFunc,
                           bool aDeferCompilation, bool aPermitUntrustedEvents,
                           dom::Element* aElement);
  /**
   * Remove the current "inline" event listener for aName.
   */
  void RemoveEventHandler(nsAtom* aName, const nsAString& aTypeString);

  void HandleEvent(nsPresContext* aPresContext, WidgetEvent* aEvent,
                   nsIDOMEvent** aDOMEvent, dom::EventTarget* aCurrentTarget,
                   nsEventStatus* aEventStatus) {
    if (mListeners.IsEmpty() || aEvent->PropagationStopped()) {
      return;
    }

    if (!mMayHaveCapturingListeners && !aEvent->mFlags.mInBubblingPhase) {
      return;
    }

    if (!mMayHaveSystemGroupListeners && aEvent->mFlags.mInSystemGroup) {
      return;
    }

    // Check if we already know that there is no event listener for the event.
    if (mNoListenerForEvent == aEvent->mMessage &&
        (mNoListenerForEvent != eUnidentifiedEvent ||
         mNoListenerForEventAtom == aEvent->mSpecifiedEventType)) {
      return;
    }
    HandleEventInternal(aPresContext, aEvent, aDOMEvent, aCurrentTarget,
                        aEventStatus);
  }

  /**
   * Tells the event listener manager that its target (which owns it) is
   * no longer using it (and could go away).
   */
  void Disconnect();

  /**
   * Allows us to quickly determine if we have mutation listeners registered.
   */
  bool HasMutationListeners();

  /**
   * Allows us to quickly determine whether we have unload or beforeunload
   * listeners registered.
   */
  bool HasUnloadListeners();

  /**
   * Returns the mutation bits depending on which mutation listeners are
   * registered to this listener manager.
   * @note If a listener is an nsIDOMMutationListener, all possible mutation
   *       event bits are returned. All bits are also returned if one of the
   *       event listeners is registered to handle DOMSubtreeModified events.
   */
  uint32_t MutationListenerBits();

  /**
   * Returns true if there is at least one event listener for aEventName.
   */
  bool HasListenersFor(const nsAString& aEventName);

  /**
   * Returns true if there is at least one event listener for aEventNameWithOn.
   * Note that aEventNameWithOn must start with "on"!
   */
  bool HasListenersFor(nsAtom* aEventNameWithOn);

  /**
   * Returns true if there is at least one event listener.
   */
  bool HasListeners();

  /**
   * Sets aList to the list of nsIEventListenerInfo objects representing the
   * listeners managed by this listener manager.
   */
  nsresult GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList);

  uint32_t GetIdentifierForEvent(nsAtom* aEvent);

  static void Shutdown();

  /**
   * Returns true if there may be a paint event listener registered,
   * false if there definitely isn't.
   */
  bool MayHavePaintEventListener() { return mMayHavePaintEventListener; }

  /**
   * Returns true if there may be a touch event listener registered,
   * false if there definitely isn't.
   */
  bool MayHaveTouchEventListener() { return mMayHaveTouchEventListener; }

  bool MayHaveMouseEnterLeaveEventListener() {
    return mMayHaveMouseEnterLeaveEventListener;
  }
  bool MayHavePointerEnterLeaveEventListener() {
    return mMayHavePointerEnterLeaveEventListener;
  }
  bool MayHaveSelectionChangeEventListener() {
    return mMayHaveSelectionChangeEventListener;
  }

  /**
   * Returns true if there may be a key event listener (keydown, keypress,
   * or keyup) registered, or false if there definitely isn't.
   */
  bool MayHaveKeyEventListener() { return mMayHaveKeyEventListener; }

  /**
   * Returns true if there may be an advanced input event listener (input,
   * compositionstart, compositionupdate, or compositionend) registered,
   * or false if there definitely isn't.
   */
  bool MayHaveInputOrCompositionEventListener() {
    return mMayHaveInputOrCompositionEventListener;
  }

  size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;

  uint32_t ListenerCount() const { return mListeners.Length(); }

  void MarkForCC();

  void TraceListeners(JSTracer* aTrc);

  dom::EventTarget* GetTarget() { return mTarget; }

  bool HasNonSystemGroupListenersForUntrustedKeyEvents();
  bool HasNonPassiveNonSystemGroupListenersForUntrustedKeyEvents();

  bool HasApzAwareListeners();
  bool IsApzAwareListener(Listener* aListener);
  bool IsApzAwareEvent(nsAtom* aEvent);

 protected:
  void HandleEventInternal(nsPresContext* aPresContext, WidgetEvent* aEvent,
                           nsIDOMEvent** aDOMEvent,
                           dom::EventTarget* aCurrentTarget,
                           nsEventStatus* aEventStatus);

  nsresult HandleEventSubType(Listener* aListener, nsIDOMEvent* aDOMEvent,
                              dom::EventTarget* aCurrentTarget);

  /**
   * If the given EventMessage has a legacy version that we support, then this
   * function returns that legacy version. Otherwise, this function simply
   * returns the passed-in EventMessage.
   */
  EventMessage GetLegacyEventMessage(EventMessage aEventMessage) const;

  void ProcessApzAwareEventListenerAdd();

  /**
   * Compile the "inline" event listener for aListener.  The
   * body of the listener can be provided in aBody; if this is null we
   * will look for it on mTarget.  If aBody is provided, aElement should be
   * as well; otherwise it will also be inferred from mTarget.
   */
  nsresult CompileEventHandlerInternal(Listener* aListener,
                                       const nsAString* aBody,
                                       dom::Element* aElement);

  /**
   * Find the Listener for the "inline" event listener for aTypeAtom.
   */
  Listener* FindEventHandler(EventMessage aEventMessage, nsAtom* aTypeAtom,
                             const nsAString& aTypeString);

  /**
   * Set the "inline" event listener for aName to aHandler.  aHandler may be
   * have no actual handler set to indicate that we should lazily get and
   * compile the string for this listener, but in that case aContext and
   * aScopeGlobal must be non-null.  Otherwise, aContext and aScopeGlobal are
   * allowed to be null.
   */
  Listener* SetEventHandlerInternal(nsAtom* aName, const nsAString& aTypeString,
                                    const TypedEventHandler& aHandler,
                                    bool aPermitUntrustedEvents);

  bool IsDeviceType(EventMessage aEventMessage);
  void EnableDevice(EventMessage aEventMessage);
  void DisableDevice(EventMessage aEventMessage);

 public:
  /**
   * Set the "inline" event listener for aEventName to aHandler.  If
   * aHandler is null, this will actually remove the event listener
   */
  void SetEventHandler(nsAtom* aEventName, const nsAString& aTypeString,
                       dom::EventHandlerNonNull* aHandler);
  void SetEventHandler(dom::OnErrorEventHandlerNonNull* aHandler);
  void SetEventHandler(dom::OnBeforeUnloadEventHandlerNonNull* aHandler);

  /**
   * Get the value of the "inline" event listener for aEventName.
   * This may cause lazy compilation if the listener is uncompiled.
   *
   * Note: It's the caller's responsibility to make sure to call the right one
   * of these methods.  In particular, "onerror" events use
   * OnErrorEventHandlerNonNull for some event targets and EventHandlerNonNull
   * for others.
   */
  dom::EventHandlerNonNull* GetEventHandler(nsAtom* aEventName,
                                            const nsAString& aTypeString) {
    const TypedEventHandler* typedHandler =
        GetTypedEventHandler(aEventName, aTypeString);
    return typedHandler ? typedHandler->NormalEventHandler() : nullptr;
  }

  dom::OnErrorEventHandlerNonNull* GetOnErrorEventHandler() {
    const TypedEventHandler* typedHandler =
        mIsMainThreadELM
            ? GetTypedEventHandler(nsGkAtoms::onerror, EmptyString())
            : GetTypedEventHandler(nullptr, NS_LITERAL_STRING("error"));
    return typedHandler ? typedHandler->OnErrorEventHandler() : nullptr;
  }

  dom::OnBeforeUnloadEventHandlerNonNull* GetOnBeforeUnloadEventHandler() {
    const TypedEventHandler* typedHandler =
        GetTypedEventHandler(nsGkAtoms::onbeforeunload, EmptyString());
    return typedHandler ? typedHandler->OnBeforeUnloadEventHandler() : nullptr;
  }

 protected:
  /**
   * Helper method for implementing the various Get*EventHandler above.  Will
   * return null if we don't have an event handler for this event name.
   */
  const TypedEventHandler* GetTypedEventHandler(nsAtom* aEventName,
                                                const nsAString& aTypeString);

  void AddEventListener(const nsAString& aType, EventListenerHolder aListener,
                        const dom::AddEventListenerOptionsOrBoolean& aOptions,
                        bool aWantsUntrusted);
  void AddEventListener(const nsAString& aType, EventListenerHolder aListener,
                        bool aUseCapture, bool aWantsUntrusted);
  void RemoveEventListener(const nsAString& aType,
                           EventListenerHolder aListener,
                           const dom::EventListenerOptionsOrBoolean& aOptions);
  void RemoveEventListener(const nsAString& aType,
                           EventListenerHolder aListener, bool aUseCapture);

  void AddEventListenerInternal(EventListenerHolder aListener,
                                EventMessage aEventMessage, nsAtom* aTypeAtom,
                                const nsAString& aTypeString,
                                const EventListenerFlags& aFlags,
                                bool aHandler = false, bool aAllEvents = false);
  void RemoveEventListenerInternal(EventListenerHolder aListener,
                                   EventMessage aEventMessage,
                                   nsAtom* aUserType,
                                   const nsAString& aTypeString,
                                   const EventListenerFlags& aFlags,
                                   bool aAllEvents = false);
  void RemoveAllListeners();
  void NotifyEventListenerRemoved(nsAtom* aUserType,
                                  const nsAString& aTypeString);
  const EventTypeData* GetTypeDataForIID(const nsIID& aIID);
  const EventTypeData* GetTypeDataForEventName(nsAtom* aName);
  nsPIDOMWindowInner* GetInnerWindowForTarget();
  already_AddRefed<nsPIDOMWindowInner> GetTargetAsInnerWindow() const;

  bool ListenerCanHandle(const Listener* aListener, const WidgetEvent* aEvent,
                         EventMessage aEventMessage) const;

  // BE AWARE, a lot of instances of EventListenerManager will be created.
  // Therefor, we need to keep this class compact.  When you add integer
  // members, please add them to EventListemerManagerBase and check the size
  // at build time.

  already_AddRefed<nsIScriptGlobalObject> GetScriptGlobalAndDocument(
      nsIDocument** aDoc);

  nsAutoTObserverArray<Listener, 2> mListeners;
  dom::EventTarget* MOZ_NON_OWNING_REF mTarget;
  RefPtr<nsAtom> mNoListenerForEventAtom;

  friend class ELMCreationDetector;
  static uint32_t sMainThreadCreatedCount;
};

}  // namespace mozilla

/**
 * NS_AddSystemEventListener() is a helper function for implementing
 * EventTarget::AddSystemEventListener().
 */
inline nsresult NS_AddSystemEventListener(mozilla::dom::EventTarget* aTarget,
                                          const nsAString& aType,
                                          nsIDOMEventListener* aListener,
                                          bool aUseCapture,
                                          bool aWantsUntrusted) {
  mozilla::EventListenerManager* listenerManager =
      aTarget->GetOrCreateListenerManager();
  NS_ENSURE_STATE(listenerManager);
  mozilla::EventListenerFlags flags;
  flags.mInSystemGroup = true;
  flags.mCapture = aUseCapture;
  flags.mAllowUntrustedEvents = aWantsUntrusted;
  listenerManager->AddEventListenerByType(aListener, aType, flags);
  return NS_OK;
}

#endif  // mozilla_EventListenerManager_h_