1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3 * License, v. 2.0. If a copy of the MPL was not distributed with this
4 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6 #ifndef mozilla_EventListenerManager_h_
7 #define mozilla_EventListenerManager_h_
9 #include "mozilla/BasicEvents.h"
10 #include "mozilla/dom/EventListenerBinding.h"
11 #include "mozilla/JSEventHandler.h"
12 #include "mozilla/MemoryReporting.h"
14 #include "nsCycleCollectionParticipant.h"
15 #include "nsGkAtoms.h"
16 #include "nsIDOMEventListener.h"
17 #include "nsTObserverArray.h"
20 class nsIEventListenerInfo
;
21 class nsIScriptContext
;
27 template<class T
> class nsCOMArray
;
31 class ELMCreationDetector
;
32 class EventListenerManager
;
39 typedef dom::CallbackObjectHolder
<dom::EventListener
,
40 nsIDOMEventListener
> EventListenerHolder
;
42 struct EventListenerFlags
44 friend class EventListenerManager
;
46 // If mListenerIsJSListener is true, the listener is implemented by JS.
47 // Otherwise, it's implemented by native code or JS but it's wrapped.
48 bool mListenerIsJSListener
: 1;
51 // If mCapture is true, it means the listener captures the event. Otherwise,
52 // it's listening at bubbling phase.
54 // If mInSystemGroup is true, the listener is listening to the events in the
56 bool mInSystemGroup
: 1;
57 // If mAllowUntrustedEvents is true, the listener is listening to the
58 // untrusted events too.
59 bool mAllowUntrustedEvents
: 1;
61 EventListenerFlags() :
62 mListenerIsJSListener(false),
63 mCapture(false), mInSystemGroup(false), mAllowUntrustedEvents(false)
67 bool Equals(const EventListenerFlags
& aOther
) const
69 return (mCapture
== aOther
.mCapture
&&
70 mInSystemGroup
== aOther
.mInSystemGroup
&&
71 mListenerIsJSListener
== aOther
.mListenerIsJSListener
&&
72 mAllowUntrustedEvents
== aOther
.mAllowUntrustedEvents
);
75 bool EqualsIgnoringTrustness(const EventListenerFlags
& aOther
) const
77 return (mCapture
== aOther
.mCapture
&&
78 mInSystemGroup
== aOther
.mInSystemGroup
&&
79 mListenerIsJSListener
== aOther
.mListenerIsJSListener
);
82 bool operator==(const EventListenerFlags
& aOther
) const
84 return Equals(aOther
);
88 inline EventListenerFlags
TrustedEventsAtBubble()
90 EventListenerFlags flags
;
94 inline EventListenerFlags
TrustedEventsAtCapture()
96 EventListenerFlags flags
;
97 flags
.mCapture
= true;
101 inline EventListenerFlags
AllEventsAtBubbe()
103 EventListenerFlags flags
;
104 flags
.mAllowUntrustedEvents
= true;
108 inline EventListenerFlags
AllEventsAtCapture()
110 EventListenerFlags flags
;
111 flags
.mCapture
= true;
112 flags
.mAllowUntrustedEvents
= true;
116 inline EventListenerFlags
TrustedEventsAtSystemGroupBubble()
118 EventListenerFlags flags
;
119 flags
.mInSystemGroup
= true;
123 inline EventListenerFlags
TrustedEventsAtSystemGroupCapture()
125 EventListenerFlags flags
;
126 flags
.mCapture
= true;
127 flags
.mInSystemGroup
= true;
131 inline EventListenerFlags
AllEventsAtSystemGroupBubble()
133 EventListenerFlags flags
;
134 flags
.mInSystemGroup
= true;
135 flags
.mAllowUntrustedEvents
= true;
139 inline EventListenerFlags
AllEventsAtSystemGroupCapture()
141 EventListenerFlags flags
;
142 flags
.mCapture
= true;
143 flags
.mInSystemGroup
= true;
144 flags
.mAllowUntrustedEvents
= true;
149 * Event listener manager
152 class EventListenerManager MOZ_FINAL
154 ~EventListenerManager();
159 EventListenerHolder mListener
;
160 nsCOMPtr
<nsIAtom
> mTypeAtom
; // for the main thread
161 nsString mTypeString
; // for non-main-threads
164 enum ListenerType
MOZ_ENUM_TYPE(uint8_t)
172 uint8_t mListenerType
;
174 bool mListenerIsHandler
: 1;
175 bool mHandlerIsString
: 1;
178 EventListenerFlags mFlags
;
180 JSEventHandler
* GetJSEventHandler() const
182 return (mListenerType
== eJSEventListener
) ?
183 static_cast<JSEventHandler
*>(mListener
.GetXPCOMCallback()) :
189 MOZ_ASSERT(sizeof(mListenerType
) == 1);
190 MOZ_ASSERT(eListenerTypeCount
< 255);
195 if ((mListenerType
== eJSEventListener
) && mListener
) {
196 static_cast<JSEventHandler
*>(
197 mListener
.GetXPCOMCallback())->Disconnect();
201 MOZ_ALWAYS_INLINE
bool IsListening(const WidgetEvent
* aEvent
) const
203 if (mFlags
.mInSystemGroup
!= aEvent
->mFlags
.mInSystemGroup
) {
206 // FIXME Should check !mFlags.mCapture when the event is in target
207 // phase because capture phase event listeners should not be fired.
208 // But it breaks at least <xul:dialog>'s buttons. Bug 235441.
209 return ((mFlags
.mCapture
&& aEvent
->mFlags
.mInCapturePhase
) ||
210 (!mFlags
.mCapture
&& aEvent
->mFlags
.mInBubblingPhase
));
214 explicit EventListenerManager(dom::EventTarget
* aTarget
);
216 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(EventListenerManager
)
218 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(EventListenerManager
)
220 void AddEventListener(const nsAString
& aType
,
221 nsIDOMEventListener
* aListener
,
223 bool aWantsUntrusted
)
225 EventListenerHolder
holder(aListener
);
226 AddEventListener(aType
, holder
, aUseCapture
, aWantsUntrusted
);
228 void AddEventListener(const nsAString
& aType
,
229 dom::EventListener
* aListener
,
231 bool aWantsUntrusted
)
233 EventListenerHolder
holder(aListener
);
234 AddEventListener(aType
, holder
, aUseCapture
, aWantsUntrusted
);
236 void RemoveEventListener(const nsAString
& aType
,
237 nsIDOMEventListener
* aListener
,
240 EventListenerHolder
holder(aListener
);
241 RemoveEventListener(aType
, holder
, aUseCapture
);
243 void RemoveEventListener(const nsAString
& aType
,
244 dom::EventListener
* aListener
,
247 EventListenerHolder
holder(aListener
);
248 RemoveEventListener(aType
, holder
, aUseCapture
);
251 void AddListenerForAllEvents(nsIDOMEventListener
* aListener
,
253 bool aWantsUntrusted
,
254 bool aSystemEventGroup
);
255 void RemoveListenerForAllEvents(nsIDOMEventListener
* aListener
,
257 bool aSystemEventGroup
);
260 * Sets events listeners of all types.
261 * @param an event listener
263 void AddEventListenerByType(nsIDOMEventListener
*aListener
,
264 const nsAString
& type
,
265 const EventListenerFlags
& aFlags
)
267 EventListenerHolder
holder(aListener
);
268 AddEventListenerByType(holder
, type
, aFlags
);
270 void AddEventListenerByType(const EventListenerHolder
& aListener
,
271 const nsAString
& type
,
272 const EventListenerFlags
& aFlags
);
273 void RemoveEventListenerByType(nsIDOMEventListener
*aListener
,
274 const nsAString
& type
,
275 const EventListenerFlags
& aFlags
)
277 EventListenerHolder
holder(aListener
);
278 RemoveEventListenerByType(holder
, type
, aFlags
);
280 void RemoveEventListenerByType(const EventListenerHolder
& aListener
,
281 const nsAString
& type
,
282 const EventListenerFlags
& aFlags
);
285 * Sets the current "inline" event listener for aName to be a
286 * function compiled from aFunc if !aDeferCompilation. If
287 * aDeferCompilation, then we assume that we can get the string from
288 * mTarget later and compile lazily.
290 * aElement, if not null, is the element the string is associated with.
292 // XXXbz does that play correctly with nodes being adopted across
293 // documents? Need to double-check the spec here.
294 nsresult
SetEventHandler(nsIAtom
*aName
,
295 const nsAString
& aFunc
,
296 bool aDeferCompilation
,
297 bool aPermitUntrustedEvents
,
298 dom::Element
* aElement
);
300 * Remove the current "inline" event listener for aName.
302 void RemoveEventHandler(nsIAtom
*aName
, const nsAString
& aTypeString
);
304 void HandleEvent(nsPresContext
* aPresContext
,
306 nsIDOMEvent
** aDOMEvent
,
307 dom::EventTarget
* aCurrentTarget
,
308 nsEventStatus
* aEventStatus
)
310 if (mListeners
.IsEmpty() || aEvent
->mFlags
.mPropagationStopped
) {
314 if (!mMayHaveCapturingListeners
&& !aEvent
->mFlags
.mInBubblingPhase
) {
318 if (!mMayHaveSystemGroupListeners
&& aEvent
->mFlags
.mInSystemGroup
) {
322 // Check if we already know that there is no event listener for the event.
323 if (mNoListenerForEvent
== aEvent
->message
&&
324 (mNoListenerForEvent
!= NS_USER_DEFINED_EVENT
||
325 mNoListenerForEventAtom
== aEvent
->userType
)) {
328 HandleEventInternal(aPresContext
, aEvent
, aDOMEvent
, aCurrentTarget
,
333 * Tells the event listener manager that its target (which owns it) is
334 * no longer using it (and could go away).
339 * Allows us to quickly determine if we have mutation listeners registered.
341 bool HasMutationListeners();
344 * Allows us to quickly determine whether we have unload or beforeunload
345 * listeners registered.
347 bool HasUnloadListeners();
350 * Returns the mutation bits depending on which mutation listeners are
351 * registered to this listener manager.
352 * @note If a listener is an nsIDOMMutationListener, all possible mutation
353 * event bits are returned. All bits are also returned if one of the
354 * event listeners is registered to handle DOMSubtreeModified events.
356 uint32_t MutationListenerBits();
359 * Returns true if there is at least one event listener for aEventName.
361 bool HasListenersFor(const nsAString
& aEventName
);
364 * Returns true if there is at least one event listener for aEventNameWithOn.
365 * Note that aEventNameWithOn must start with "on"!
367 bool HasListenersFor(nsIAtom
* aEventNameWithOn
);
370 * Returns true if there is at least one event listener.
375 * Sets aList to the list of nsIEventListenerInfo objects representing the
376 * listeners managed by this listener manager.
378 nsresult
GetListenerInfo(nsCOMArray
<nsIEventListenerInfo
>* aList
);
380 uint32_t GetIdentifierForEvent(nsIAtom
* aEvent
);
382 static void Shutdown();
385 * Returns true if there may be a paint event listener registered,
386 * false if there definitely isn't.
388 bool MayHavePaintEventListener() { return mMayHavePaintEventListener
; }
391 * Returns true if there may be a touch event listener registered,
392 * false if there definitely isn't.
394 bool MayHaveTouchEventListener() { return mMayHaveTouchEventListener
; }
396 bool MayHaveMouseEnterLeaveEventListener() { return mMayHaveMouseEnterLeaveEventListener
; }
397 bool MayHavePointerEnterLeaveEventListener() { return mMayHavePointerEnterLeaveEventListener
; }
399 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf
) const;
401 uint32_t ListenerCount() const
403 return mListeners
.Length();
408 void TraceListeners(JSTracer
* aTrc
);
410 dom::EventTarget
* GetTarget() { return mTarget
; }
413 void HandleEventInternal(nsPresContext
* aPresContext
,
415 nsIDOMEvent
** aDOMEvent
,
416 dom::EventTarget
* aCurrentTarget
,
417 nsEventStatus
* aEventStatus
);
419 nsresult
HandleEventSubType(Listener
* aListener
,
420 nsIDOMEvent
* aDOMEvent
,
421 dom::EventTarget
* aCurrentTarget
);
424 * Compile the "inline" event listener for aListener. The
425 * body of the listener can be provided in aBody; if this is null we
426 * will look for it on mTarget. If aBody is provided, aElement should be
427 * as well; otherwise it will also be inferred from mTarget.
429 nsresult
CompileEventHandlerInternal(Listener
* aListener
,
430 const nsAString
* aBody
,
431 dom::Element
* aElement
);
434 * Find the Listener for the "inline" event listener for aTypeAtom.
436 Listener
* FindEventHandler(uint32_t aEventType
,
438 const nsAString
& aTypeString
);
441 * Set the "inline" event listener for aName to aHandler. aHandler may be
442 * have no actual handler set to indicate that we should lazily get and
443 * compile the string for this listener, but in that case aContext and
444 * aScopeGlobal must be non-null. Otherwise, aContext and aScopeGlobal are
445 * allowed to be null.
447 Listener
* SetEventHandlerInternal(nsIAtom
* aName
,
448 const nsAString
& aTypeString
,
449 const TypedEventHandler
& aHandler
,
450 bool aPermitUntrustedEvents
);
452 bool IsDeviceType(uint32_t aType
);
453 void EnableDevice(uint32_t aType
);
454 void DisableDevice(uint32_t aType
);
458 * Set the "inline" event listener for aEventName to aHandler. If
459 * aHandler is null, this will actually remove the event listener
461 void SetEventHandler(nsIAtom
* aEventName
,
462 const nsAString
& aTypeString
,
463 dom::EventHandlerNonNull
* aHandler
);
464 void SetEventHandler(dom::OnErrorEventHandlerNonNull
* aHandler
);
465 void SetEventHandler(dom::OnBeforeUnloadEventHandlerNonNull
* aHandler
);
468 * Get the value of the "inline" event listener for aEventName.
469 * This may cause lazy compilation if the listener is uncompiled.
471 * Note: It's the caller's responsibility to make sure to call the right one
472 * of these methods. In particular, "onerror" events use
473 * OnErrorEventHandlerNonNull for some event targets and EventHandlerNonNull
476 dom::EventHandlerNonNull
* GetEventHandler(nsIAtom
* aEventName
,
477 const nsAString
& aTypeString
)
479 const TypedEventHandler
* typedHandler
=
480 GetTypedEventHandler(aEventName
, aTypeString
);
481 return typedHandler
? typedHandler
->NormalEventHandler() : nullptr;
484 dom::OnErrorEventHandlerNonNull
* GetOnErrorEventHandler()
486 const TypedEventHandler
* typedHandler
= mIsMainThreadELM
?
487 GetTypedEventHandler(nsGkAtoms::onerror
, EmptyString()) :
488 GetTypedEventHandler(nullptr, NS_LITERAL_STRING("error"));
489 return typedHandler
? typedHandler
->OnErrorEventHandler() : nullptr;
492 dom::OnBeforeUnloadEventHandlerNonNull
* GetOnBeforeUnloadEventHandler()
494 const TypedEventHandler
* typedHandler
=
495 GetTypedEventHandler(nsGkAtoms::onbeforeunload
, EmptyString());
496 return typedHandler
? typedHandler
->OnBeforeUnloadEventHandler() : nullptr;
501 * Helper method for implementing the various Get*EventHandler above. Will
502 * return null if we don't have an event handler for this event name.
504 const TypedEventHandler
* GetTypedEventHandler(nsIAtom
* aEventName
,
505 const nsAString
& aTypeString
);
507 void AddEventListener(const nsAString
& aType
,
508 const EventListenerHolder
& aListener
,
510 bool aWantsUntrusted
);
511 void RemoveEventListener(const nsAString
& aType
,
512 const EventListenerHolder
& aListener
,
515 void AddEventListenerInternal(const EventListenerHolder
& aListener
,
518 const nsAString
& aTypeString
,
519 const EventListenerFlags
& aFlags
,
520 bool aHandler
= false,
521 bool aAllEvents
= false);
522 void RemoveEventListenerInternal(const EventListenerHolder
& aListener
,
525 const nsAString
& aTypeString
,
526 const EventListenerFlags
& aFlags
,
527 bool aAllEvents
= false);
528 void RemoveAllListeners();
529 const EventTypeData
* GetTypeDataForIID(const nsIID
& aIID
);
530 const EventTypeData
* GetTypeDataForEventName(nsIAtom
* aName
);
531 nsPIDOMWindow
* GetInnerWindowForTarget();
532 already_AddRefed
<nsPIDOMWindow
> GetTargetAsInnerWindow() const;
534 bool ListenerCanHandle(Listener
* aListener
, WidgetEvent
* aEvent
);
536 already_AddRefed
<nsIScriptGlobalObject
>
537 GetScriptGlobalAndDocument(nsIDocument
** aDoc
);
539 uint32_t mMayHavePaintEventListener
: 1;
540 uint32_t mMayHaveMutationListeners
: 1;
541 uint32_t mMayHaveCapturingListeners
: 1;
542 uint32_t mMayHaveSystemGroupListeners
: 1;
543 uint32_t mMayHaveTouchEventListener
: 1;
544 uint32_t mMayHaveMouseEnterLeaveEventListener
: 1;
545 uint32_t mMayHavePointerEnterLeaveEventListener
: 1;
546 uint32_t mClearingListeners
: 1;
547 uint32_t mIsMainThreadELM
: 1;
548 uint32_t mNoListenerForEvent
: 23;
550 nsAutoTObserverArray
<Listener
, 2> mListeners
;
551 dom::EventTarget
* mTarget
; // WEAK
552 nsCOMPtr
<nsIAtom
> mNoListenerForEventAtom
;
554 friend class ELMCreationDetector
;
555 static uint32_t sMainThreadCreatedCount
;
558 } // namespace mozilla
561 * NS_AddSystemEventListener() is a helper function for implementing
562 * EventTarget::AddSystemEventListener().
565 NS_AddSystemEventListener(mozilla::dom::EventTarget
* aTarget
,
566 const nsAString
& aType
,
567 nsIDOMEventListener
*aListener
,
569 bool aWantsUntrusted
)
571 mozilla::EventListenerManager
* listenerManager
=
572 aTarget
->GetOrCreateListenerManager();
573 NS_ENSURE_STATE(listenerManager
);
574 mozilla::EventListenerFlags flags
;
575 flags
.mInSystemGroup
= true;
576 flags
.mCapture
= aUseCapture
;
577 flags
.mAllowUntrustedEvents
= aWantsUntrusted
;
578 listenerManager
->AddEventListenerByType(aListener
, aType
, flags
);
582 #endif // mozilla_EventListenerManager_h_