dom/events/EventListenerManager.h

branch
TOR_BUG_9701
changeset 15
b8a032363ba2
equal deleted inserted replaced
-1:000000000000 0:a1a5d251927f
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/. */
5
6 #ifndef mozilla_EventListenerManager_h_
7 #define mozilla_EventListenerManager_h_
8
9 #include "mozilla/BasicEvents.h"
10 #include "mozilla/dom/EventListenerBinding.h"
11 #include "mozilla/JSEventHandler.h"
12 #include "mozilla/MemoryReporting.h"
13 #include "nsCOMPtr.h"
14 #include "nsCycleCollectionParticipant.h"
15 #include "nsGkAtoms.h"
16 #include "nsIDOMEventListener.h"
17 #include "nsTObserverArray.h"
18
19 class nsIDOMEvent;
20 class nsIEventListenerInfo;
21 class nsIScriptContext;
22 class nsPIDOMWindow;
23
24 struct EventTypeData;
25
26 template<class T> class nsCOMArray;
27
28 namespace mozilla {
29
30 class ELMCreationDetector;
31 class EventListenerManager;
32
33 namespace dom {
34 class EventTarget;
35 class Element;
36 } // namespace dom
37
38 typedef dom::CallbackObjectHolder<dom::EventListener,
39 nsIDOMEventListener> EventListenerHolder;
40
41 struct EventListenerFlags
42 {
43 friend class EventListenerManager;
44 private:
45 // If mListenerIsJSListener is true, the listener is implemented by JS.
46 // Otherwise, it's implemented by native code or JS but it's wrapped.
47 bool mListenerIsJSListener : 1;
48
49 public:
50 // If mCapture is true, it means the listener captures the event. Otherwise,
51 // it's listening at bubbling phase.
52 bool mCapture : 1;
53 // If mInSystemGroup is true, the listener is listening to the events in the
54 // system group.
55 bool mInSystemGroup : 1;
56 // If mAllowUntrustedEvents is true, the listener is listening to the
57 // untrusted events too.
58 bool mAllowUntrustedEvents : 1;
59
60 EventListenerFlags() :
61 mListenerIsJSListener(false),
62 mCapture(false), mInSystemGroup(false), mAllowUntrustedEvents(false)
63 {
64 }
65
66 bool Equals(const EventListenerFlags& aOther) const
67 {
68 return (mCapture == aOther.mCapture &&
69 mInSystemGroup == aOther.mInSystemGroup &&
70 mListenerIsJSListener == aOther.mListenerIsJSListener &&
71 mAllowUntrustedEvents == aOther.mAllowUntrustedEvents);
72 }
73
74 bool EqualsIgnoringTrustness(const EventListenerFlags& aOther) const
75 {
76 return (mCapture == aOther.mCapture &&
77 mInSystemGroup == aOther.mInSystemGroup &&
78 mListenerIsJSListener == aOther.mListenerIsJSListener);
79 }
80
81 bool operator==(const EventListenerFlags& aOther) const
82 {
83 return Equals(aOther);
84 }
85 };
86
87 inline EventListenerFlags TrustedEventsAtBubble()
88 {
89 EventListenerFlags flags;
90 return flags;
91 }
92
93 inline EventListenerFlags TrustedEventsAtCapture()
94 {
95 EventListenerFlags flags;
96 flags.mCapture = true;
97 return flags;
98 }
99
100 inline EventListenerFlags AllEventsAtBubbe()
101 {
102 EventListenerFlags flags;
103 flags.mAllowUntrustedEvents = true;
104 return flags;
105 }
106
107 inline EventListenerFlags AllEventsAtCapture()
108 {
109 EventListenerFlags flags;
110 flags.mCapture = true;
111 flags.mAllowUntrustedEvents = true;
112 return flags;
113 }
114
115 inline EventListenerFlags TrustedEventsAtSystemGroupBubble()
116 {
117 EventListenerFlags flags;
118 flags.mInSystemGroup = true;
119 return flags;
120 }
121
122 inline EventListenerFlags TrustedEventsAtSystemGroupCapture()
123 {
124 EventListenerFlags flags;
125 flags.mCapture = true;
126 flags.mInSystemGroup = true;
127 return flags;
128 }
129
130 inline EventListenerFlags AllEventsAtSystemGroupBubble()
131 {
132 EventListenerFlags flags;
133 flags.mInSystemGroup = true;
134 flags.mAllowUntrustedEvents = true;
135 return flags;
136 }
137
138 inline EventListenerFlags AllEventsAtSystemGroupCapture()
139 {
140 EventListenerFlags flags;
141 flags.mCapture = true;
142 flags.mInSystemGroup = true;
143 flags.mAllowUntrustedEvents = true;
144 return flags;
145 }
146
147 /*
148 * Event listener manager
149 */
150
151 class EventListenerManager MOZ_FINAL
152 {
153 public:
154 struct Listener
155 {
156 EventListenerHolder mListener;
157 nsCOMPtr<nsIAtom> mTypeAtom; // for the main thread
158 nsString mTypeString; // for non-main-threads
159 uint16_t mEventType;
160
161 enum ListenerType MOZ_ENUM_TYPE(uint8_t)
162 {
163 eNativeListener = 0,
164 eJSEventListener,
165 eWrappedJSListener,
166 eWebIDLListener,
167 eListenerTypeCount
168 };
169 uint8_t mListenerType;
170
171 bool mListenerIsHandler : 1;
172 bool mHandlerIsString : 1;
173 bool mAllEvents : 1;
174
175 EventListenerFlags mFlags;
176
177 JSEventHandler* GetJSEventHandler() const
178 {
179 return (mListenerType == eJSEventListener) ?
180 static_cast<JSEventHandler*>(mListener.GetXPCOMCallback()) :
181 nullptr;
182 }
183
184 Listener()
185 {
186 MOZ_ASSERT(sizeof(mListenerType) == 1);
187 MOZ_ASSERT(eListenerTypeCount < 255);
188 }
189
190 ~Listener()
191 {
192 if ((mListenerType == eJSEventListener) && mListener) {
193 static_cast<JSEventHandler*>(
194 mListener.GetXPCOMCallback())->Disconnect();
195 }
196 }
197
198 MOZ_ALWAYS_INLINE bool IsListening(const WidgetEvent* aEvent) const
199 {
200 if (mFlags.mInSystemGroup != aEvent->mFlags.mInSystemGroup) {
201 return false;
202 }
203 // FIXME Should check !mFlags.mCapture when the event is in target
204 // phase because capture phase event listeners should not be fired.
205 // But it breaks at least <xul:dialog>'s buttons. Bug 235441.
206 return ((mFlags.mCapture && aEvent->mFlags.mInCapturePhase) ||
207 (!mFlags.mCapture && aEvent->mFlags.mInBubblingPhase));
208 }
209 };
210
211 EventListenerManager(dom::EventTarget* aTarget);
212 virtual ~EventListenerManager();
213
214 NS_INLINE_DECL_CYCLE_COLLECTING_NATIVE_REFCOUNTING(EventListenerManager)
215
216 NS_DECL_CYCLE_COLLECTION_NATIVE_CLASS(EventListenerManager)
217
218 void AddEventListener(const nsAString& aType,
219 nsIDOMEventListener* aListener,
220 bool aUseCapture,
221 bool aWantsUntrusted)
222 {
223 EventListenerHolder holder(aListener);
224 AddEventListener(aType, holder, aUseCapture, aWantsUntrusted);
225 }
226 void AddEventListener(const nsAString& aType,
227 dom::EventListener* aListener,
228 bool aUseCapture,
229 bool aWantsUntrusted)
230 {
231 EventListenerHolder holder(aListener);
232 AddEventListener(aType, holder, aUseCapture, aWantsUntrusted);
233 }
234 void RemoveEventListener(const nsAString& aType,
235 nsIDOMEventListener* aListener,
236 bool aUseCapture)
237 {
238 EventListenerHolder holder(aListener);
239 RemoveEventListener(aType, holder, aUseCapture);
240 }
241 void RemoveEventListener(const nsAString& aType,
242 dom::EventListener* aListener,
243 bool aUseCapture)
244 {
245 EventListenerHolder holder(aListener);
246 RemoveEventListener(aType, holder, aUseCapture);
247 }
248
249 void AddListenerForAllEvents(nsIDOMEventListener* aListener,
250 bool aUseCapture,
251 bool aWantsUntrusted,
252 bool aSystemEventGroup);
253 void RemoveListenerForAllEvents(nsIDOMEventListener* aListener,
254 bool aUseCapture,
255 bool aSystemEventGroup);
256
257 /**
258 * Sets events listeners of all types.
259 * @param an event listener
260 */
261 void AddEventListenerByType(nsIDOMEventListener *aListener,
262 const nsAString& type,
263 const EventListenerFlags& aFlags)
264 {
265 EventListenerHolder holder(aListener);
266 AddEventListenerByType(holder, type, aFlags);
267 }
268 void AddEventListenerByType(const EventListenerHolder& aListener,
269 const nsAString& type,
270 const EventListenerFlags& aFlags);
271 void RemoveEventListenerByType(nsIDOMEventListener *aListener,
272 const nsAString& type,
273 const EventListenerFlags& aFlags)
274 {
275 EventListenerHolder holder(aListener);
276 RemoveEventListenerByType(holder, type, aFlags);
277 }
278 void RemoveEventListenerByType(const EventListenerHolder& aListener,
279 const nsAString& type,
280 const EventListenerFlags& aFlags);
281
282 /**
283 * Sets the current "inline" event listener for aName to be a
284 * function compiled from aFunc if !aDeferCompilation. If
285 * aDeferCompilation, then we assume that we can get the string from
286 * mTarget later and compile lazily.
287 *
288 * aElement, if not null, is the element the string is associated with.
289 */
290 // XXXbz does that play correctly with nodes being adopted across
291 // documents? Need to double-check the spec here.
292 nsresult SetEventHandler(nsIAtom *aName,
293 const nsAString& aFunc,
294 uint32_t aLanguage,
295 bool aDeferCompilation,
296 bool aPermitUntrustedEvents,
297 dom::Element* aElement);
298 /**
299 * Remove the current "inline" event listener for aName.
300 */
301 void RemoveEventHandler(nsIAtom *aName, const nsAString& aTypeString);
302
303 void HandleEvent(nsPresContext* aPresContext,
304 WidgetEvent* aEvent,
305 nsIDOMEvent** aDOMEvent,
306 dom::EventTarget* aCurrentTarget,
307 nsEventStatus* aEventStatus)
308 {
309 if (mListeners.IsEmpty() || aEvent->mFlags.mPropagationStopped) {
310 return;
311 }
312
313 if (!mMayHaveCapturingListeners && !aEvent->mFlags.mInBubblingPhase) {
314 return;
315 }
316
317 if (!mMayHaveSystemGroupListeners && aEvent->mFlags.mInSystemGroup) {
318 return;
319 }
320
321 // Check if we already know that there is no event listener for the event.
322 if (mNoListenerForEvent == aEvent->message &&
323 (mNoListenerForEvent != NS_USER_DEFINED_EVENT ||
324 mNoListenerForEventAtom == aEvent->userType)) {
325 return;
326 }
327 HandleEventInternal(aPresContext, aEvent, aDOMEvent, aCurrentTarget,
328 aEventStatus);
329 }
330
331 /**
332 * Tells the event listener manager that its target (which owns it) is
333 * no longer using it (and could go away).
334 */
335 void Disconnect();
336
337 /**
338 * Allows us to quickly determine if we have mutation listeners registered.
339 */
340 bool HasMutationListeners();
341
342 /**
343 * Allows us to quickly determine whether we have unload or beforeunload
344 * listeners registered.
345 */
346 bool HasUnloadListeners();
347
348 /**
349 * Returns the mutation bits depending on which mutation listeners are
350 * registered to this listener manager.
351 * @note If a listener is an nsIDOMMutationListener, all possible mutation
352 * event bits are returned. All bits are also returned if one of the
353 * event listeners is registered to handle DOMSubtreeModified events.
354 */
355 uint32_t MutationListenerBits();
356
357 /**
358 * Returns true if there is at least one event listener for aEventName.
359 */
360 bool HasListenersFor(const nsAString& aEventName);
361
362 /**
363 * Returns true if there is at least one event listener for aEventNameWithOn.
364 * Note that aEventNameWithOn must start with "on"!
365 */
366 bool HasListenersFor(nsIAtom* aEventNameWithOn);
367
368 /**
369 * Returns true if there is at least one event listener.
370 */
371 bool HasListeners();
372
373 /**
374 * Sets aList to the list of nsIEventListenerInfo objects representing the
375 * listeners managed by this listener manager.
376 */
377 nsresult GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList);
378
379 uint32_t GetIdentifierForEvent(nsIAtom* aEvent);
380
381 static void Shutdown();
382
383 /**
384 * Returns true if there may be a paint event listener registered,
385 * false if there definitely isn't.
386 */
387 bool MayHavePaintEventListener() { return mMayHavePaintEventListener; }
388
389 /**
390 * Returns true if there may be a touch event listener registered,
391 * false if there definitely isn't.
392 */
393 bool MayHaveTouchEventListener() { return mMayHaveTouchEventListener; }
394
395 bool MayHaveMouseEnterLeaveEventListener() { return mMayHaveMouseEnterLeaveEventListener; }
396 bool MayHavePointerEnterLeaveEventListener() { return mMayHavePointerEnterLeaveEventListener; }
397
398 size_t SizeOfIncludingThis(MallocSizeOf aMallocSizeOf) const;
399
400 uint32_t ListenerCount() const
401 {
402 return mListeners.Length();
403 }
404
405 void MarkForCC();
406
407 dom::EventTarget* GetTarget() { return mTarget; }
408
409 protected:
410 void HandleEventInternal(nsPresContext* aPresContext,
411 WidgetEvent* aEvent,
412 nsIDOMEvent** aDOMEvent,
413 dom::EventTarget* aCurrentTarget,
414 nsEventStatus* aEventStatus);
415
416 nsresult HandleEventSubType(Listener* aListener,
417 nsIDOMEvent* aDOMEvent,
418 dom::EventTarget* aCurrentTarget);
419
420 /**
421 * Compile the "inline" event listener for aListener. The
422 * body of the listener can be provided in aBody; if this is null we
423 * will look for it on mTarget. If aBody is provided, aElement should be
424 * as well; otherwise it will also be inferred from mTarget.
425 */
426 nsresult CompileEventHandlerInternal(Listener* aListener,
427 const nsAString* aBody,
428 dom::Element* aElement);
429
430 /**
431 * Find the Listener for the "inline" event listener for aTypeAtom.
432 */
433 Listener* FindEventHandler(uint32_t aEventType,
434 nsIAtom* aTypeAtom,
435 const nsAString& aTypeString);
436
437 /**
438 * Set the "inline" event listener for aName to aHandler. aHandler may be
439 * have no actual handler set to indicate that we should lazily get and
440 * compile the string for this listener, but in that case aContext and
441 * aScopeGlobal must be non-null. Otherwise, aContext and aScopeGlobal are
442 * allowed to be null.
443 */
444 Listener* SetEventHandlerInternal(nsIAtom* aName,
445 const nsAString& aTypeString,
446 const TypedEventHandler& aHandler,
447 bool aPermitUntrustedEvents);
448
449 bool IsDeviceType(uint32_t aType);
450 void EnableDevice(uint32_t aType);
451 void DisableDevice(uint32_t aType);
452
453 public:
454 /**
455 * Set the "inline" event listener for aEventName to aHandler. If
456 * aHandler is null, this will actually remove the event listener
457 */
458 void SetEventHandler(nsIAtom* aEventName,
459 const nsAString& aTypeString,
460 dom::EventHandlerNonNull* aHandler);
461 void SetEventHandler(dom::OnErrorEventHandlerNonNull* aHandler);
462 void SetEventHandler(dom::OnBeforeUnloadEventHandlerNonNull* aHandler);
463
464 /**
465 * Get the value of the "inline" event listener for aEventName.
466 * This may cause lazy compilation if the listener is uncompiled.
467 *
468 * Note: It's the caller's responsibility to make sure to call the right one
469 * of these methods. In particular, "onerror" events use
470 * OnErrorEventHandlerNonNull for some event targets and EventHandlerNonNull
471 * for others.
472 */
473 dom::EventHandlerNonNull* GetEventHandler(nsIAtom* aEventName,
474 const nsAString& aTypeString)
475 {
476 const TypedEventHandler* typedHandler =
477 GetTypedEventHandler(aEventName, aTypeString);
478 return typedHandler ? typedHandler->NormalEventHandler() : nullptr;
479 }
480
481 dom::OnErrorEventHandlerNonNull* GetOnErrorEventHandler()
482 {
483 const TypedEventHandler* typedHandler = mIsMainThreadELM ?
484 GetTypedEventHandler(nsGkAtoms::onerror, EmptyString()) :
485 GetTypedEventHandler(nullptr, NS_LITERAL_STRING("error"));
486 return typedHandler ? typedHandler->OnErrorEventHandler() : nullptr;
487 }
488
489 dom::OnBeforeUnloadEventHandlerNonNull* GetOnBeforeUnloadEventHandler()
490 {
491 const TypedEventHandler* typedHandler =
492 GetTypedEventHandler(nsGkAtoms::onbeforeunload, EmptyString());
493 return typedHandler ? typedHandler->OnBeforeUnloadEventHandler() : nullptr;
494 }
495
496 protected:
497 /**
498 * Helper method for implementing the various Get*EventHandler above. Will
499 * return null if we don't have an event handler for this event name.
500 */
501 const TypedEventHandler* GetTypedEventHandler(nsIAtom* aEventName,
502 const nsAString& aTypeString);
503
504 void AddEventListener(const nsAString& aType,
505 const EventListenerHolder& aListener,
506 bool aUseCapture,
507 bool aWantsUntrusted);
508 void RemoveEventListener(const nsAString& aType,
509 const EventListenerHolder& aListener,
510 bool aUseCapture);
511
512 void AddEventListenerInternal(const EventListenerHolder& aListener,
513 uint32_t aType,
514 nsIAtom* aTypeAtom,
515 const nsAString& aTypeString,
516 const EventListenerFlags& aFlags,
517 bool aHandler = false,
518 bool aAllEvents = false);
519 void RemoveEventListenerInternal(const EventListenerHolder& aListener,
520 uint32_t aType,
521 nsIAtom* aUserType,
522 const nsAString& aTypeString,
523 const EventListenerFlags& aFlags,
524 bool aAllEvents = false);
525 void RemoveAllListeners();
526 const EventTypeData* GetTypeDataForIID(const nsIID& aIID);
527 const EventTypeData* GetTypeDataForEventName(nsIAtom* aName);
528 nsPIDOMWindow* GetInnerWindowForTarget();
529 already_AddRefed<nsPIDOMWindow> GetTargetAsInnerWindow() const;
530
531 bool ListenerCanHandle(Listener* aListener, WidgetEvent* aEvent);
532
533 already_AddRefed<nsIScriptGlobalObject>
534 GetScriptGlobalAndDocument(nsIDocument** aDoc);
535
536 uint32_t mMayHavePaintEventListener : 1;
537 uint32_t mMayHaveMutationListeners : 1;
538 uint32_t mMayHaveCapturingListeners : 1;
539 uint32_t mMayHaveSystemGroupListeners : 1;
540 uint32_t mMayHaveTouchEventListener : 1;
541 uint32_t mMayHaveMouseEnterLeaveEventListener : 1;
542 uint32_t mMayHavePointerEnterLeaveEventListener : 1;
543 uint32_t mClearingListeners : 1;
544 uint32_t mIsMainThreadELM : 1;
545 uint32_t mNoListenerForEvent : 23;
546
547 nsAutoTObserverArray<Listener, 2> mListeners;
548 dom::EventTarget* mTarget; // WEAK
549 nsCOMPtr<nsIAtom> mNoListenerForEventAtom;
550
551 friend class ELMCreationDetector;
552 static uint32_t sMainThreadCreatedCount;
553 };
554
555 } // namespace mozilla
556
557 /**
558 * NS_AddSystemEventListener() is a helper function for implementing
559 * EventTarget::AddSystemEventListener().
560 */
561 inline nsresult
562 NS_AddSystemEventListener(mozilla::dom::EventTarget* aTarget,
563 const nsAString& aType,
564 nsIDOMEventListener *aListener,
565 bool aUseCapture,
566 bool aWantsUntrusted)
567 {
568 mozilla::EventListenerManager* listenerManager =
569 aTarget->GetOrCreateListenerManager();
570 NS_ENSURE_STATE(listenerManager);
571 mozilla::EventListenerFlags flags;
572 flags.mInSystemGroup = true;
573 flags.mCapture = aUseCapture;
574 flags.mAllowUntrustedEvents = aWantsUntrusted;
575 listenerManager->AddEventListenerByType(aListener, aType, flags);
576 return NS_OK;
577 }
578
579 #endif // mozilla_EventListenerManager_h_

mercurial