1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* ***** BEGIN LICENSE BLOCK *****
3 * Version: MPL 1.1/GPL 2.0/LGPL 2.1
5 * The contents of this file are subject to the Mozilla Public License Version
6 * 1.1 (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 * http://www.mozilla.org/MPL/
10 * Software distributed under the License is distributed on an "AS IS" basis,
11 * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
12 * for the specific language governing rights and limitations under the
15 * The Original Code is mozilla.org code.
17 * The Initial Developer of the Original Code is
18 * Netscape Communications Corporation.
19 * Portions created by the Initial Developer are Copyright (C) 1998
20 * the Initial Developer. All Rights Reserved.
24 * Alternatively, the contents of this file may be used under the terms of
25 * either of the GNU General Public License Version 2 or later (the "GPL"),
26 * or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
27 * in which case the provisions of the GPL or the LGPL are applicable instead
28 * of those above. If you wish to allow use of your version of this file only
29 * under the terms of either the GPL or the LGPL, and not to allow others to
30 * use your version of this file under the terms of the MPL, indicate your
31 * decision by deleting the provisions above and replace them with the notice
32 * and other provisions required by the GPL or the LGPL. If you do not delete
33 * the provisions above, a recipient may use your version of this file under
34 * the terms of any one of the MPL, the GPL or the LGPL.
36 * ***** END LICENSE BLOCK ***** */
38 #include "nsISupports.h"
39 #include "nsGUIEvent.h"
40 #include "nsDOMEvent.h"
41 #include "nsEventListenerManager.h"
43 #include "nsIDOMNSEvent.h"
44 #include "nsIDOMEventListener.h"
45 #include "nsIDOMMouseListener.h"
46 #include "nsIDOMMouseMotionListener.h"
47 #include "nsIDOMContextMenuListener.h"
48 #include "nsIDOMKeyListener.h"
49 #include "nsIDOMFocusListener.h"
50 #include "nsIDOMFormListener.h"
51 #include "nsIDOMLoadListener.h"
52 #include "nsIDOMDragListener.h"
53 #include "nsIDOMTextListener.h"
54 #include "nsIDOMCompositionListener.h"
55 #include "nsIDOMXULListener.h"
56 #include "nsIDOMUIListener.h"
57 #include "nsITextControlFrame.h"
59 #include "nsIDOMSVGListener.h"
60 #include "nsIDOMSVGZoomListener.h"
61 #include "nsGkAtoms.h"
63 #include "nsIEventStateManager.h"
64 #include "nsPIDOMWindow.h"
65 #include "nsIPrivateDOMEvent.h"
66 #include "nsIJSEventListener.h"
68 #include "nsIScriptGlobalObject.h"
69 #include "nsIScriptRuntime.h"
70 #include "nsLayoutUtils.h"
72 // XXXbz the fact that this is ifdef MOZ_XUL is good indication that
73 // it doesn't belong here...
74 #include "nsITreeBoxObject.h"
75 #include "nsITreeColumns.h"
76 #include "nsIDOMXULMultSelectCntrlEl.h"
77 #include "nsIDOMXULSelectCntrlItemEl.h"
78 #include "nsIDOMXULMenuListElement.h"
80 #include "nsINameSpaceManager.h"
81 #include "nsIContent.h"
84 #include "nsIViewManager.h"
85 #include "nsIScrollableView.h"
87 #include "nsIServiceManager.h"
88 #include "nsIScriptSecurityManager.h"
89 #include "nsDOMError.h"
90 #include "nsIJSContextStack.h"
91 #include "nsIDocument.h"
92 #include "nsIPresShell.h"
93 #include "nsMutationEvent.h"
94 #include "nsIXPConnect.h"
96 #include "nsIScriptObjectOwner.h" // for nsIScriptEventHandlerOwner
97 #include "nsIFocusController.h"
98 #include "nsIDOMElement.h"
99 #include "nsIBoxObject.h"
100 #include "nsIDOMNSDocument.h"
101 #include "nsIWidget.h"
102 #include "nsContentUtils.h"
103 #include "nsJSUtils.h"
104 #include "nsIDOMEventGroup.h"
105 #include "nsContentCID.h"
106 #include "nsEventDispatcher.h"
107 #include "nsDOMJSUtils.h"
108 #include "nsDOMScriptObjectHolder.h"
109 #include "nsDataHashtable.h"
111 #define EVENT_TYPE_EQUALS( ls, type, userType ) \
112 (ls->mEventType && ls->mEventType == type && \
113 (ls->mEventType != NS_USER_DEFINED_EVENT || ls->mTypeAtom == userType))
115 #define EVENT_TYPE_DATA_EQUALS( type1, type2 ) \
116 (type1 && type2 && type1->iid && type2->iid && \
117 type1->iid->Equals(*(type2->iid)))
119 static NS_DEFINE_CID(kDOMScriptObjectFactoryCID
,
120 NS_DOM_SCRIPT_OBJECT_FACTORY_CID
);
121 static NS_DEFINE_CID(kDOMEventGroupCID
, NS_DOMEVENTGROUP_CID
);
123 static const PRUint32 kAllMutationBits
=
124 NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED
|
125 NS_EVENT_BITS_MUTATION_NODEINSERTED
|
126 NS_EVENT_BITS_MUTATION_NODEREMOVED
|
127 NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT
|
128 NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT
|
129 NS_EVENT_BITS_MUTATION_ATTRMODIFIED
|
130 NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED
;
133 MutationBitForEventType(PRUint32 aEventType
)
135 switch (aEventType
) {
136 case NS_MUTATION_SUBTREEMODIFIED
:
137 return NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED
;
138 case NS_MUTATION_NODEINSERTED
:
139 return NS_EVENT_BITS_MUTATION_NODEINSERTED
;
140 case NS_MUTATION_NODEREMOVED
:
141 return NS_EVENT_BITS_MUTATION_NODEREMOVED
;
142 case NS_MUTATION_NODEREMOVEDFROMDOCUMENT
:
143 return NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT
;
144 case NS_MUTATION_NODEINSERTEDINTODOCUMENT
:
145 return NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT
;
146 case NS_MUTATION_ATTRMODIFIED
:
147 return NS_EVENT_BITS_MUTATION_ATTRMODIFIED
;
148 case NS_MUTATION_CHARACTERDATAMODIFIED
:
149 return NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED
;
157 NS_STDCALL_FUNCPROTO(nsresult
,
159 nsIDOMEventListener
, HandleEvent
,
163 * Things here are not as they appear. Namely, |ifaceListener| below is
164 * not really a pointer to the nsIDOMEventListener interface, and aMethod is
165 * not really a pointer-to-member for nsIDOMEventListener. They both
166 * actually refer to the event-type-specific listener interface. The casting
167 * magic allows us to use a single dispatch method. This relies on the
168 * assumption that nsIDOMEventListener and the event type listener interfaces
169 * have the same object layout and will therefore have compatible
170 * pointer-to-member implementations.
173 static nsresult
DispatchToInterface(nsIDOMEvent
* aEvent
,
174 nsIDOMEventListener
* aListener
,
175 GenericHandler aMethod
,
178 nsIDOMEventListener
* ifaceListener
= nsnull
;
180 aListener
->QueryInterface(aIID
, (void**) &ifaceListener
);
181 NS_WARN_IF_FALSE(ifaceListener
,
182 "DispatchToInterface couldn't QI to the right interface");
184 rv
= (ifaceListener
->*aMethod
)(aEvent
);
185 NS_RELEASE(ifaceListener
);
190 struct EventDispatchData
193 GenericHandler method
;
198 const EventDispatchData
* events
;
203 #define HANDLER(x) reinterpret_cast<GenericHandler>(x)
205 static const EventDispatchData sMouseEvents
[] = {
206 { NS_MOUSE_BUTTON_DOWN
, HANDLER(&nsIDOMMouseListener::MouseDown
) },
207 { NS_MOUSE_BUTTON_UP
, HANDLER(&nsIDOMMouseListener::MouseUp
) },
208 { NS_MOUSE_CLICK
, HANDLER(&nsIDOMMouseListener::MouseClick
) },
209 { NS_MOUSE_DOUBLECLICK
, HANDLER(&nsIDOMMouseListener::MouseDblClick
) },
210 { NS_MOUSE_ENTER_SYNTH
, HANDLER(&nsIDOMMouseListener::MouseOver
) },
211 { NS_MOUSE_EXIT_SYNTH
, HANDLER(&nsIDOMMouseListener::MouseOut
) }
214 static const EventDispatchData sMouseMotionEvents
[] = {
215 { NS_MOUSE_MOVE
, HANDLER(&nsIDOMMouseMotionListener::MouseMove
) }
218 static const EventDispatchData sContextMenuEvents
[] = {
219 { NS_CONTEXTMENU
, HANDLER(&nsIDOMContextMenuListener::ContextMenu
) }
222 static const EventDispatchData sCompositionEvents
[] = {
223 { NS_COMPOSITION_START
,
224 HANDLER(&nsIDOMCompositionListener::HandleStartComposition
) },
225 { NS_COMPOSITION_END
,
226 HANDLER(&nsIDOMCompositionListener::HandleEndComposition
) },
227 { NS_COMPOSITION_QUERY
,
228 HANDLER(&nsIDOMCompositionListener::HandleQueryComposition
) }
231 static const EventDispatchData sTextEvents
[] = {
232 { NS_TEXT_TEXT
, HANDLER(&nsIDOMTextListener::HandleText
) }
235 static const EventDispatchData sKeyEvents
[] = {
236 { NS_KEY_UP
, HANDLER(&nsIDOMKeyListener::KeyUp
) },
237 { NS_KEY_DOWN
, HANDLER(&nsIDOMKeyListener::KeyDown
) },
238 { NS_KEY_PRESS
, HANDLER(&nsIDOMKeyListener::KeyPress
) }
241 static const EventDispatchData sFocusEvents
[] = {
242 { NS_FOCUS_CONTENT
, HANDLER(&nsIDOMFocusListener::Focus
) },
243 { NS_BLUR_CONTENT
, HANDLER(&nsIDOMFocusListener::Blur
) }
246 static const EventDispatchData sFormEvents
[] = {
247 { NS_FORM_SUBMIT
, HANDLER(&nsIDOMFormListener::Submit
) },
248 { NS_FORM_RESET
, HANDLER(&nsIDOMFormListener::Reset
) },
249 { NS_FORM_CHANGE
, HANDLER(&nsIDOMFormListener::Change
) },
250 { NS_FORM_SELECTED
, HANDLER(&nsIDOMFormListener::Select
) },
251 { NS_FORM_INPUT
, HANDLER(&nsIDOMFormListener::Input
) }
254 static const EventDispatchData sLoadEvents
[] = {
255 { NS_LOAD
, HANDLER(&nsIDOMLoadListener::Load
) },
256 { NS_PAGE_UNLOAD
, HANDLER(&nsIDOMLoadListener::Unload
) },
257 { NS_LOAD_ERROR
, HANDLER(&nsIDOMLoadListener::Error
) },
258 { NS_BEFORE_PAGE_UNLOAD
, HANDLER(&nsIDOMLoadListener::BeforeUnload
) }
261 static const EventDispatchData sDragEvents
[] = {
262 { NS_DRAGDROP_ENTER
, HANDLER(&nsIDOMDragListener::DragEnter
) },
263 { NS_DRAGDROP_OVER_SYNTH
, HANDLER(&nsIDOMDragListener::DragOver
) },
264 { NS_DRAGDROP_EXIT_SYNTH
, HANDLER(&nsIDOMDragListener::DragExit
) },
265 { NS_DRAGDROP_DRAGDROP
, HANDLER(&nsIDOMDragListener::DragDrop
) },
266 { NS_DRAGDROP_GESTURE
, HANDLER(&nsIDOMDragListener::DragGesture
) },
267 { NS_DRAGDROP_DRAG
, HANDLER(&nsIDOMDragListener::Drag
) },
268 { NS_DRAGDROP_END
, HANDLER(&nsIDOMDragListener::DragEnd
) },
269 { NS_DRAGDROP_START
, HANDLER(&nsIDOMDragListener::DragStart
) },
270 { NS_DRAGDROP_LEAVE_SYNTH
, HANDLER(&nsIDOMDragListener::DragLeave
) },
271 { NS_DRAGDROP_DROP
, HANDLER(&nsIDOMDragListener::Drop
) }
274 static const EventDispatchData sXULEvents
[] = {
275 { NS_XUL_POPUP_SHOWING
, HANDLER(&nsIDOMXULListener::PopupShowing
) },
276 { NS_XUL_POPUP_SHOWN
, HANDLER(&nsIDOMXULListener::PopupShown
) },
277 { NS_XUL_POPUP_HIDING
, HANDLER(&nsIDOMXULListener::PopupHiding
) },
278 { NS_XUL_POPUP_HIDDEN
, HANDLER(&nsIDOMXULListener::PopupHidden
) },
279 { NS_XUL_CLOSE
, HANDLER(&nsIDOMXULListener::Close
) },
280 { NS_XUL_COMMAND
, HANDLER(&nsIDOMXULListener::Command
) },
281 { NS_XUL_BROADCAST
, HANDLER(&nsIDOMXULListener::Broadcast
) },
282 { NS_XUL_COMMAND_UPDATE
, HANDLER(&nsIDOMXULListener::CommandUpdate
) }
285 static const EventDispatchData sUIEvents
[] = {
286 { NS_UI_ACTIVATE
, HANDLER(&nsIDOMUIListener::Activate
) },
287 { NS_UI_FOCUSIN
, HANDLER(&nsIDOMUIListener::FocusIn
) },
288 { NS_UI_FOCUSOUT
, HANDLER(&nsIDOMUIListener::FocusOut
) }
292 static const EventDispatchData sSVGEvents
[] = {
293 { NS_SVG_LOAD
, HANDLER(&nsIDOMSVGListener::Load
) },
294 { NS_SVG_UNLOAD
, HANDLER(&nsIDOMSVGListener::Unload
) },
295 { NS_SVG_ABORT
, HANDLER(&nsIDOMSVGListener::Abort
) },
296 { NS_SVG_ERROR
, HANDLER(&nsIDOMSVGListener::Error
) },
297 { NS_SVG_RESIZE
, HANDLER(&nsIDOMSVGListener::Resize
) },
298 { NS_SVG_SCROLL
, HANDLER(&nsIDOMSVGListener::Scroll
) }
301 static const EventDispatchData sSVGZoomEvents
[] = {
302 { NS_SVG_ZOOM
, HANDLER(&nsIDOMSVGZoomListener::Zoom
) }
306 #define IMPL_EVENTTYPEDATA(type) \
309 NS_ARRAY_LENGTH(s##type##Events), \
310 &NS_GET_IID(nsIDOM##type##Listener) \
313 // IMPORTANT: indices match up with eEventArrayType_ enum values
315 static const EventTypeData sEventTypes
[] = {
316 IMPL_EVENTTYPEDATA(Mouse
),
317 IMPL_EVENTTYPEDATA(MouseMotion
),
318 IMPL_EVENTTYPEDATA(ContextMenu
),
319 IMPL_EVENTTYPEDATA(Key
),
320 IMPL_EVENTTYPEDATA(Load
),
321 IMPL_EVENTTYPEDATA(Focus
),
322 IMPL_EVENTTYPEDATA(Form
),
323 IMPL_EVENTTYPEDATA(Drag
),
324 IMPL_EVENTTYPEDATA(Text
),
325 IMPL_EVENTTYPEDATA(Composition
),
326 IMPL_EVENTTYPEDATA(XUL
),
327 IMPL_EVENTTYPEDATA(UI
)
330 IMPL_EVENTTYPEDATA(SVG
),
331 IMPL_EVENTTYPEDATA(SVGZoom
)
335 // Strong references to event groups
336 nsIDOMEventGroup
* gSystemEventGroup
= nsnull
;
337 nsIDOMEventGroup
* gDOM2EventGroup
= nsnull
;
339 nsDataHashtable
<nsISupportsHashKey
, PRUint32
>* gEventIdTable
= nsnull
;
341 PRUint32
nsEventListenerManager::mInstanceCount
= 0;
342 PRUint32
nsEventListenerManager::sCreatedCount
= 0;
344 nsEventListenerManager::nsEventListenerManager() :
351 nsEventListenerManager::~nsEventListenerManager()
353 NS_ASSERTION(!mTarget
, "didn't call Disconnect");
354 RemoveAllListeners();
357 if(mInstanceCount
== 0) {
358 NS_IF_RELEASE(gSystemEventGroup
);
359 NS_IF_RELEASE(gDOM2EventGroup
);
360 delete gEventIdTable
;
361 gEventIdTable
= nsnull
;
366 nsEventListenerManager::RemoveAllListeners()
373 nsEventListenerManager::Shutdown()
375 sAddListenerID
= JSVAL_VOID
;
376 nsDOMEvent::Shutdown();
379 NS_IMPL_CYCLE_COLLECTION_CLASS(nsEventListenerManager
)
381 NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEventListenerManager
)
382 NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports
, nsIEventListenerManager
)
383 NS_INTERFACE_MAP_ENTRY(nsIEventListenerManager
)
384 NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget
)
385 NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget
)
388 NS_IMPL_CYCLE_COLLECTING_ADDREF_AMBIGUOUS(nsEventListenerManager
, nsIEventListenerManager
)
389 NS_IMPL_CYCLE_COLLECTING_RELEASE_AMBIGUOUS(nsEventListenerManager
, nsIEventListenerManager
)
391 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(nsEventListenerManager
)
392 PRUint32 count
= tmp
->mListeners
.Length();
393 for (PRUint32 i
= 0; i
< count
; i
++) {
394 NS_CYCLE_COLLECTION_NOTE_EDGE_NAME(cb
, "mListeners[i] mListener");
395 cb
.NoteXPCOMChild(tmp
->mListeners
.ElementAt(i
).mListener
.get());
397 NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
399 NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsEventListenerManager
)
401 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
405 nsEventListenerManager::GetTypeDataForIID(const nsIID
& aIID
)
407 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(sEventTypes
); ++i
) {
408 if (aIID
.Equals(*(sEventTypes
[i
].iid
))) {
409 return &sEventTypes
[i
];
416 nsEventListenerManager::GetTypeDataForEventName(nsIAtom
* aName
)
418 PRUint32 event
= nsContentUtils::GetEventId(aName
);
419 if (event
!= NS_USER_DEFINED_EVENT
) {
420 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(sEventTypes
); ++i
) {
421 for (PRInt32 j
= 0; j
< sEventTypes
[i
].numEvents
; ++j
) {
422 if (event
== sEventTypes
[i
].events
[j
].message
) {
423 return &sEventTypes
[i
];
432 nsEventListenerManager::GetInnerWindowForTarget()
434 nsCOMPtr
<nsINode
> node
= do_QueryInterface(mTarget
);
436 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
437 // if that's the XBL document?
438 nsIDocument
* document
= node
->GetOwnerDoc();
440 return document
->GetInnerWindow();
443 nsCOMPtr
<nsPIDOMWindow
> window
= do_QueryInterface(mTarget
);
445 NS_ASSERTION(window
->IsInnerWindow(), "Target should not be an outer window");
453 nsEventListenerManager::AddEventListener(nsIDOMEventListener
*aListener
,
456 const EventTypeData
* aTypeData
,
458 nsIDOMEventGroup
* aEvtGrp
)
460 NS_ENSURE_TRUE(aListener
, NS_ERROR_FAILURE
);
461 NS_ENSURE_TRUE(aType
|| aTypeData
, NS_ERROR_FAILURE
);
463 nsRefPtr
<nsIDOMEventListener
> kungFuDeathGrip
= aListener
;
465 PRBool isSame
= PR_FALSE
;
467 nsCOMPtr
<nsIDOMEventGroup
> sysGroup
;
468 GetSystemEventGroupLM(getter_AddRefs(sysGroup
));
470 sysGroup
->IsSameEventGroup(aEvtGrp
, &isSame
);
472 group
= NS_EVENT_FLAG_SYSTEM_EVENT
;
477 // If we don't have type data, we can try to QI listener to the right
478 // interface and set mTypeData only if QI succeeds. This way we can save
479 // calls to DispatchToInterface (in HandleEvent) in those cases when QI
481 // @see also DispatchToInterface()
482 const EventTypeData
* td
= GetTypeDataForEventName(aTypeAtom
);
484 nsIDOMEventListener
* ifaceListener
= nsnull
;
485 aListener
->QueryInterface(*(td
->iid
), (void**) &ifaceListener
);
488 NS_RELEASE(ifaceListener
);
493 nsListenerStruct
* ls
;
494 PRUint32 count
= mListeners
.Length();
495 for (PRUint32 i
= 0; i
< count
; i
++) {
496 ls
= &mListeners
.ElementAt(i
);
497 if (ls
->mListener
== aListener
&& ls
->mFlags
== aFlags
&&
498 ls
->mGroupFlags
== group
&&
499 (EVENT_TYPE_EQUALS(ls
, aType
, aTypeAtom
) ||
500 EVENT_TYPE_DATA_EQUALS(aTypeData
, ls
->mTypeData
))) {
505 mNoListenerForEvent
= NS_EVENT_TYPE_NULL
;
506 mNoListenerForEventAtom
= nsnull
;
508 ls
= mListeners
.AppendElement();
509 NS_ENSURE_TRUE(ls
, NS_ERROR_OUT_OF_MEMORY
);
511 ls
->mListener
= aListener
;
512 ls
->mEventType
= aType
;
513 ls
->mTypeAtom
= aTypeAtom
;
515 ls
->mGroupFlags
= group
;
516 ls
->mHandlerIsString
= PR_FALSE
;
517 ls
->mTypeData
= aTypeData
;
519 if (aType
== NS_AFTERPAINT
) {
520 mMayHavePaintEventListener
= PR_TRUE
;
521 nsPIDOMWindow
* window
= GetInnerWindowForTarget();
523 window
->SetHasPaintEventListeners();
525 } else if (aType
>= NS_MUTATION_START
&& aType
<= NS_MUTATION_END
) {
526 // For mutation listeners, we need to update the global bit on the DOM window.
527 // Otherwise we won't actually fire the mutation event.
528 mMayHaveMutationListeners
= PR_TRUE
;
529 // Go from our target to the nearest enclosing DOM window.
530 nsPIDOMWindow
* window
= GetInnerWindowForTarget();
532 // If aType is NS_MUTATION_SUBTREEMODIFIED, we need to listen all
533 // mutations. nsContentUtils::HasMutationListeners relies on this.
534 window
->SetMutationListeners((aType
== NS_MUTATION_SUBTREEMODIFIED
) ?
536 MutationBitForEventType(aType
));
544 nsEventListenerManager::RemoveEventListener(nsIDOMEventListener
*aListener
,
547 const EventTypeData
* aTypeData
,
549 nsIDOMEventGroup
* aEvtGrp
)
551 if (!aListener
|| !(aType
|| aTypeData
)) {
555 PRBool isSame
= PR_FALSE
;
557 nsCOMPtr
<nsIDOMEventGroup
> sysGroup
;
558 GetSystemEventGroupLM(getter_AddRefs(sysGroup
));
560 sysGroup
->IsSameEventGroup(aEvtGrp
, &isSame
);
562 group
= NS_EVENT_FLAG_SYSTEM_EVENT
;
566 nsListenerStruct
* ls
;
567 aFlags
&= ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED
;
569 PRUint32 count
= mListeners
.Length();
570 for (PRUint32 i
= 0; i
< count
; ++i
) {
571 ls
= &mListeners
.ElementAt(i
);
572 if (ls
->mListener
== aListener
&&
573 ls
->mGroupFlags
== group
&&
574 ((ls
->mFlags
& ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED
) == aFlags
) &&
575 (EVENT_TYPE_EQUALS(ls
, aType
, aUserType
) ||
576 (!(ls
->mEventType
) &&
577 EVENT_TYPE_DATA_EQUALS(ls
->mTypeData
, aTypeData
)))) {
578 mListeners
.RemoveElementAt(i
);
579 mNoListenerForEvent
= NS_EVENT_TYPE_NULL
;
580 mNoListenerForEventAtom
= nsnull
;
589 nsEventListenerManager::AddEventListenerByIID(nsIDOMEventListener
*aListener
,
593 AddEventListener(aListener
, NS_EVENT_TYPE_NULL
, nsnull
,
594 GetTypeDataForIID(aIID
), aFlags
, nsnull
);
599 nsEventListenerManager::RemoveEventListenerByIID(nsIDOMEventListener
*aListener
,
603 RemoveEventListener(aListener
, NS_EVENT_TYPE_NULL
, nsnull
,
604 GetTypeDataForIID(aIID
), aFlags
, nsnull
);
609 nsEventListenerManager::ListenerCanHandle(nsListenerStruct
* aLs
,
612 if (aEvent
->message
== NS_USER_DEFINED_EVENT
) {
613 // We don't want to check aLs->mEventType here, bug 276846.
614 return (aEvent
->userType
&& aLs
->mTypeAtom
== aEvent
->userType
);
616 return (aLs
->mEventType
== aEvent
->message
);
620 nsEventListenerManager::AddEventListenerByType(nsIDOMEventListener
*aListener
,
621 const nsAString
& aType
,
623 nsIDOMEventGroup
* aEvtGrp
)
625 nsCOMPtr
<nsIAtom
> atom
= do_GetAtom(NS_LITERAL_STRING("on") + aType
);
626 PRUint32 type
= nsContentUtils::GetEventId(atom
);
627 AddEventListener(aListener
, type
, atom
, nsnull
, aFlags
, aEvtGrp
);
632 nsEventListenerManager::RemoveEventListenerByType(nsIDOMEventListener
*aListener
,
633 const nsAString
& aType
,
635 nsIDOMEventGroup
* aEvtGrp
)
637 nsCOMPtr
<nsIAtom
> atom
= do_GetAtom(NS_LITERAL_STRING("on") + aType
);
638 PRUint32 type
= nsContentUtils::GetEventId(atom
);
639 RemoveEventListener(aListener
, type
, atom
, nsnull
, aFlags
, aEvtGrp
);
644 nsEventListenerManager::FindJSEventListener(PRUint32 aEventType
,
647 // Run through the listeners for this type and see if a script
648 // listener is registered
649 nsListenerStruct
*ls
;
650 PRUint32 count
= mListeners
.Length();
651 for (PRUint32 i
= 0; i
< count
; ++i
) {
652 ls
= &mListeners
.ElementAt(i
);
653 if (EVENT_TYPE_EQUALS(ls
, aEventType
, aTypeAtom
) &&
654 ls
->mFlags
& NS_PRIV_EVENT_FLAG_SCRIPT
) {
662 nsEventListenerManager::SetJSEventListener(nsIScriptContext
*aContext
,
664 nsISupports
*aObject
,
667 PRBool aPermitUntrustedEvents
)
670 PRUint32 eventType
= nsContentUtils::GetEventId(aName
);
671 nsListenerStruct
* ls
= FindJSEventListener(eventType
, aName
);
674 // If we didn't find a script listener or no listeners existed
675 // create and add a new one.
676 nsCOMPtr
<nsIDOMEventListener
> scriptListener
;
677 rv
= NS_NewJSEventListener(aContext
, aScopeObject
, aObject
,
678 getter_AddRefs(scriptListener
));
679 if (NS_SUCCEEDED(rv
)) {
680 AddEventListener(scriptListener
, eventType
, aName
, nsnull
,
681 NS_EVENT_FLAG_BUBBLE
| NS_PRIV_EVENT_FLAG_SCRIPT
, nsnull
);
683 ls
= FindJSEventListener(eventType
, aName
);
687 if (NS_SUCCEEDED(rv
) && ls
) {
688 // Set flag to indicate possible need for compilation later
689 ls
->mHandlerIsString
= aIsString
;
691 if (aPermitUntrustedEvents
) {
692 ls
->mFlags
|= NS_PRIV_EVENT_UNTRUSTED_PERMITTED
;
700 nsEventListenerManager::AddScriptEventListener(nsISupports
*aObject
,
702 const nsAString
& aBody
,
704 PRBool aDeferCompilation
,
705 PRBool aPermitUntrustedEvents
)
707 NS_PRECONDITION(aLanguage
!= nsIProgrammingLanguage::UNKNOWN
,
708 "Must know the language for the script event listener");
709 nsIScriptContext
*context
= nsnull
;
711 // |aPermitUntrustedEvents| is set to False for chrome - events
712 // *generated* from an unknown source are not allowed.
713 // However, for script languages with no 'sandbox', we want to reject
714 // such scripts based on the source of their code, not just the source
716 if (aPermitUntrustedEvents
&&
717 aLanguage
!= nsIProgrammingLanguage::JAVASCRIPT
) {
718 NS_WARNING("Discarding non-JS event listener from untrusted source");
719 return NS_ERROR_FAILURE
;
722 nsCOMPtr
<nsINode
> node(do_QueryInterface(aObject
));
724 nsCOMPtr
<nsIDocument
> doc
;
726 nsISupports
*objiSupp
= aObject
;
727 nsCOMPtr
<nsIScriptGlobalObject
> global
;
730 // Try to get context from doc
731 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
732 // if that's the XBL document?
733 doc
= node
->GetOwnerDoc();
735 global
= doc
->GetScriptGlobalObject();
737 nsCOMPtr
<nsPIDOMWindow
> win(do_QueryInterface(aObject
));
739 NS_ASSERTION(win
->IsInnerWindow(),
740 "Event listener added to outer window!");
742 nsCOMPtr
<nsIDOMDocument
> domdoc
;
743 win
->GetDocument(getter_AddRefs(domdoc
));
744 doc
= do_QueryInterface(domdoc
);
745 global
= do_QueryInterface(win
);
747 global
= do_QueryInterface(aObject
);
752 // This can happen; for example this document might have been
757 // This might be the first reference to this language in the global
758 // We must init the language before we attempt to fetch its context.
759 if (NS_FAILED(global
->EnsureScriptEnvironment(aLanguage
))) {
760 NS_WARNING("Failed to setup script environment for this language");
761 // but fall through and let the inevitable failure below handle it.
764 context
= global
->GetScriptContext(aLanguage
);
765 NS_ENSURE_TRUE(context
, NS_ERROR_FAILURE
);
767 void *scope
= global
->GetScriptGlobal(aLanguage
);
770 if (!aDeferCompilation
) {
771 nsCOMPtr
<nsIScriptEventHandlerOwner
> handlerOwner
=
772 do_QueryInterface(aObject
);
774 nsScriptObjectHolder
handler(context
);
775 PRBool done
= PR_FALSE
;
778 rv
= handlerOwner
->GetCompiledEventHandler(aName
, handler
);
779 if (NS_SUCCEEDED(rv
) && handler
) {
780 rv
= context
->BindCompiledEventHandler(aObject
, scope
, aName
, handler
);
789 nsCAutoString
url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
791 nsIURI
*uri
= doc
->GetDocumentURI();
799 // Always let the handler owner compile the event handler, as
800 // it may want to use a special context or scope object.
801 rv
= handlerOwner
->CompileEventHandler(context
, aObject
, aName
,
802 aBody
, url
.get(), lineNo
, handler
);
805 PRInt32 nameSpace
= kNameSpaceID_Unknown
;
806 if (node
&& node
->IsNodeOfType(nsINode::eCONTENT
)) {
807 nsIContent
* content
= static_cast<nsIContent
*>(node
.get());
808 nameSpace
= content
->GetNameSpaceID();
811 nsCOMPtr
<nsIContent
> root
= doc
->GetRootContent();
813 nameSpace
= root
->GetNameSpaceID();
816 const char **argNames
;
817 nsContentUtils::GetEventArgNames(nameSpace
, aName
, &argCount
,
820 rv
= context
->CompileEventHandler(aName
, argCount
, argNames
,
823 SCRIPTVERSION_DEFAULT
, // for now?
825 if (rv
== NS_ERROR_ILLEGAL_VALUE
) {
826 NS_WARNING("Probably a syntax error in the event handler!");
827 context
->ReportPendingException();
828 return NS_SUCCESS_LOSS_OF_INSIGNIFICANT_DATA
;
830 NS_ENSURE_SUCCESS(rv
, rv
);
832 rv
= context
->BindCompiledEventHandler(aObject
, scope
,
835 if (NS_FAILED(rv
)) return rv
;
839 return SetJSEventListener(context
, scope
, objiSupp
, aName
, aDeferCompilation
,
840 aPermitUntrustedEvents
);
844 nsEventListenerManager::RemoveScriptEventListener(nsIAtom
* aName
)
846 PRUint32 eventType
= nsContentUtils::GetEventId(aName
);
847 nsListenerStruct
* ls
= FindJSEventListener(eventType
, aName
);
850 mListeners
.RemoveElementAt(PRUint32(ls
- &mListeners
.ElementAt(0)));
851 mNoListenerForEvent
= NS_EVENT_TYPE_NULL
;
852 mNoListenerForEventAtom
= nsnull
;
859 nsEventListenerManager::sAddListenerID
= JSVAL_VOID
;
862 nsEventListenerManager::RegisterScriptEventListener(nsIScriptContext
*aContext
,
864 nsISupports
*aObject
,
867 // Check that we have access to set an event listener. Prevents
868 // snooping attacks across domains by setting onkeypress handlers,
870 // You'd think it'd work just to get the JSContext from aContext,
871 // but that's actually the JSContext whose private object parents
872 // the object in aObject.
874 nsCOMPtr
<nsIJSContextStack
> stack
=
875 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv
);
879 if (NS_FAILED(rv
= stack
->Peek(&cx
)))
883 if (sAddListenerID
== JSVAL_VOID
) {
884 JSAutoRequest
ar(cx
);
886 STRING_TO_JSVAL(::JS_InternString(cx
, "addEventListener"));
889 if (aContext
->GetScriptTypeID() == nsIProgrammingLanguage::JAVASCRIPT
) {
890 nsCOMPtr
<nsIXPConnectJSObjectHolder
> holder
;
891 rv
= nsContentUtils::XPConnect()->
892 WrapNative(cx
, (JSObject
*)aScope
, aObject
, NS_GET_IID(nsISupports
),
893 getter_AddRefs(holder
));
894 NS_ENSURE_SUCCESS(rv
, rv
);
895 JSObject
*jsobj
= nsnull
;
897 rv
= holder
->GetJSObject(&jsobj
);
898 NS_ENSURE_SUCCESS(rv
, rv
);
900 rv
= nsContentUtils::GetSecurityManager()->
901 CheckPropertyAccess(cx
, jsobj
,
904 nsIXPCSecurityManager::ACCESS_SET_PROPERTY
);
906 // XXX set pending exception on the native call context?
910 NS_WARNING("Skipping CheckPropertyAccess for non JS language");
915 // Untrusted events are always permitted for non-chrome script
917 return SetJSEventListener(aContext
, aScope
, aObject
, aName
,
918 PR_FALSE
, !nsContentUtils::IsCallerChrome());
922 nsEventListenerManager::CompileScriptEventListener(nsIScriptContext
*aContext
,
924 nsISupports
*aObject
,
929 *aDidCompile
= PR_FALSE
;
930 PRUint32 eventType
= nsContentUtils::GetEventId(aName
);
931 nsListenerStruct
* ls
= FindJSEventListener(eventType
, aName
);
938 if (ls
->mHandlerIsString
) {
939 rv
= CompileEventHandlerInternal(aContext
, aScope
, aObject
, aName
,
940 ls
, /*XXX fixme*/nsnull
);
943 // Set *aDidCompile to true even if we didn't really compile
944 // anything right now, if we get here it means that this event
945 // handler has been compiled at some point, that's good enough for
948 *aDidCompile
= PR_TRUE
;
954 nsEventListenerManager::CompileEventHandlerInternal(nsIScriptContext
*aContext
,
956 nsISupports
*aObject
,
958 nsListenerStruct
*aListenerStruct
,
959 nsISupports
* aCurrentTarget
)
961 nsresult result
= NS_OK
;
963 nsCOMPtr
<nsIScriptEventHandlerOwner
> handlerOwner
=
964 do_QueryInterface(aObject
);
965 nsScriptObjectHolder
handler(aContext
);
968 result
= handlerOwner
->GetCompiledEventHandler(aName
,
970 if (NS_SUCCEEDED(result
) && handler
) {
971 // XXXmarkh - why do we bind here, but not after compilation below?
972 result
= aContext
->BindCompiledEventHandler(aObject
, aScope
, aName
, handler
);
973 aListenerStruct
->mHandlerIsString
= PR_FALSE
;
977 if (aListenerStruct
->mHandlerIsString
) {
978 // This should never happen for anything but content
979 // XXX I don't like that we have to reference content
980 // from here. The alternative is to store the event handler
981 // string on the JS object itself.
982 nsCOMPtr
<nsIContent
> content
= do_QueryInterface(aObject
);
983 NS_ASSERTION(content
, "only content should have event handler attributes");
985 nsAutoString handlerBody
;
986 nsIAtom
* attrName
= aName
;
988 if (aName
== nsGkAtoms::onSVGLoad
)
989 attrName
= nsGkAtoms::onload
;
990 else if (aName
== nsGkAtoms::onSVGUnload
)
991 attrName
= nsGkAtoms::onunload
;
992 else if (aName
== nsGkAtoms::onSVGAbort
)
993 attrName
= nsGkAtoms::onabort
;
994 else if (aName
== nsGkAtoms::onSVGError
)
995 attrName
= nsGkAtoms::onerror
;
996 else if (aName
== nsGkAtoms::onSVGResize
)
997 attrName
= nsGkAtoms::onresize
;
998 else if (aName
== nsGkAtoms::onSVGScroll
)
999 attrName
= nsGkAtoms::onscroll
;
1000 else if (aName
== nsGkAtoms::onSVGZoom
)
1001 attrName
= nsGkAtoms::onzoom
;
1004 content
->GetAttr(kNameSpaceID_None
, attrName
, handlerBody
);
1006 PRUint32 lineNo
= 0;
1007 nsCAutoString
url (NS_LITERAL_CSTRING("javascript:alert('TODO: FIXME')"));
1008 nsIDocument
* doc
= nsnull
;
1009 nsCOMPtr
<nsINode
> node
= do_QueryInterface(aCurrentTarget
);
1011 doc
= node
->GetOwnerDoc();
1014 nsIURI
*uri
= doc
->GetDocumentURI();
1022 // Always let the handler owner compile the event
1023 // handler, as it may want to use a special
1024 // context or scope object.
1025 result
= handlerOwner
->CompileEventHandler(aContext
, aObject
, aName
,
1032 const char **argNames
;
1033 nsContentUtils::GetEventArgNames(content
->GetNameSpaceID(), aName
,
1034 &argCount
, &argNames
);
1036 result
= aContext
->CompileEventHandler(aName
,
1040 SCRIPTVERSION_DEFAULT
, // for now?
1042 NS_ENSURE_SUCCESS(result
, result
);
1044 result
= aContext
->BindCompiledEventHandler(aObject
, aScope
,
1046 NS_ENSURE_SUCCESS(result
, result
);
1049 if (NS_SUCCEEDED(result
)) {
1050 aListenerStruct
->mHandlerIsString
= PR_FALSE
;
1059 nsEventListenerManager::HandleEventSubType(nsListenerStruct
* aListenerStruct
,
1060 nsIDOMEventListener
* aListener
,
1061 nsIDOMEvent
* aDOMEvent
,
1062 nsPIDOMEventTarget
* aCurrentTarget
,
1063 PRUint32 aPhaseFlags
)
1065 nsresult result
= NS_OK
;
1067 // If this is a script handler and we haven't yet
1068 // compiled the event handler itself
1069 if ((aListenerStruct
->mFlags
& NS_PRIV_EVENT_FLAG_SCRIPT
) &&
1070 aListenerStruct
->mHandlerIsString
) {
1071 nsCOMPtr
<nsIJSEventListener
> jslistener
= do_QueryInterface(aListener
);
1073 nsAutoString eventString
;
1074 if (NS_SUCCEEDED(aDOMEvent
->GetType(eventString
))) {
1075 nsCOMPtr
<nsIAtom
> atom
= do_GetAtom(NS_LITERAL_STRING("on") + eventString
);
1077 result
= CompileEventHandlerInternal(jslistener
->GetEventContext(),
1078 jslistener
->GetEventScope(),
1079 jslistener
->GetEventTarget(),
1080 atom
, aListenerStruct
,
1086 // nsCxPusher will push and pop (automatically) the current cx onto the
1089 if (NS_SUCCEEDED(result
) && pusher
.Push(aCurrentTarget
)) {
1090 // nsIDOMEvent::currentTarget is set in nsEventDispatcher.
1091 result
= aListener
->HandleEvent(aDOMEvent
);
1097 static PRUint32 sLatestEventType
= 0;
1098 static const EventTypeData
* sLatestEventTypeData
= nsnull
;
1099 static const EventDispatchData
* sLatestEventDispData
= nsnull
;
1102 * Causes a check for event listeners and processing by them if they exist.
1103 * @param an event listener
1107 nsEventListenerManager::HandleEvent(nsPresContext
* aPresContext
,
1108 nsEvent
* aEvent
, nsIDOMEvent
** aDOMEvent
,
1109 nsPIDOMEventTarget
* aCurrentTarget
,
1111 nsEventStatus
* aEventStatus
)
1113 if (mListeners
.IsEmpty() || aEvent
->flags
& NS_EVENT_FLAG_STOP_DISPATCH
) {
1117 // Check if we already know that there is no event listener for the event.
1118 if (mNoListenerForEvent
== aEvent
->message
&&
1119 (mNoListenerForEvent
!= NS_USER_DEFINED_EVENT
||
1120 mNoListenerForEventAtom
== aEvent
->userType
)) {
1124 //Set the value of the internal PreventDefault flag properly based on aEventStatus
1125 if (*aEventStatus
== nsEventStatus_eConsumeNoDefault
) {
1126 aEvent
->flags
|= NS_EVENT_FLAG_NO_DEFAULT
;
1128 PRUint16 currentGroup
= aFlags
& NS_EVENT_FLAG_SYSTEM_EVENT
;
1130 // Beware! This may flush notifications via synchronous
1131 // ScrollSelectionIntoView.
1132 if (aEvent
->message
== NS_CONTEXTMENU
&&
1133 NS_FAILED(FixContextMenuEvent(aPresContext
, aCurrentTarget
, aEvent
,
1135 NS_WARNING("failed to fix context menu event target");
1138 const EventTypeData
* typeData
= nsnull
;
1139 const EventDispatchData
* dispData
= nsnull
;
1140 if (aEvent
->message
!= NS_USER_DEFINED_EVENT
) {
1141 // Check if this is the same type of event as what a listener manager
1142 // handled last time.
1143 if (aEvent
->message
== sLatestEventType
) {
1144 typeData
= sLatestEventTypeData
;
1145 dispData
= sLatestEventDispData
;
1148 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(sEventTypes
); ++i
) {
1149 typeData
= &sEventTypes
[i
];
1150 for (PRInt32 j
= 0; j
< typeData
->numEvents
; ++j
) {
1151 dispData
= &(typeData
->events
[j
]);
1152 if (aEvent
->message
== dispData
->message
) {
1153 sLatestEventType
= aEvent
->message
;
1154 sLatestEventTypeData
= typeData
;
1155 sLatestEventDispData
= dispData
;
1166 nsAutoTObserverArray
<nsListenerStruct
, 2>::EndLimitedIterator
iter(mListeners
);
1167 nsAutoPopupStatePusher
popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent
));
1168 PRBool hasListener
= PR_FALSE
;
1169 while (iter
.HasMore()) {
1170 nsListenerStruct
* ls
= &iter
.GetNext();
1171 PRBool useTypeInterface
=
1172 EVENT_TYPE_DATA_EQUALS(ls
->mTypeData
, typeData
);
1173 PRBool useGenericInterface
=
1174 (!useTypeInterface
&& ListenerCanHandle(ls
, aEvent
));
1175 // Don't fire the listener if it's been removed.
1176 // Check that the phase is same in event and event listener.
1177 // Handle only trusted events, except when listener permits untrusted events.
1178 if (useTypeInterface
|| useGenericInterface
) {
1179 if (ls
->mListener
) {
1180 hasListener
= PR_TRUE
;
1181 if (ls
->mFlags
& aFlags
&&
1182 ls
->mGroupFlags
== currentGroup
&&
1183 (NS_IS_TRUSTED_EVENT(aEvent
) ||
1184 ls
->mFlags
& NS_PRIV_EVENT_UNTRUSTED_PERMITTED
)) {
1186 nsEventDispatcher::CreateEvent(aPresContext
, aEvent
,
1187 EmptyString(), aDOMEvent
);
1190 nsRefPtr
<nsIDOMEventListener
> kungFuDeathGrip
= ls
->mListener
;
1191 if (useTypeInterface
) {
1192 DispatchToInterface(*aDOMEvent
, ls
->mListener
,
1193 dispData
->method
, *typeData
->iid
);
1194 } else if (useGenericInterface
) {
1195 HandleEventSubType(ls
, ls
->mListener
, *aDOMEvent
,
1196 aCurrentTarget
, aFlags
);
1205 mNoListenerForEvent
= aEvent
->message
;
1206 mNoListenerForEventAtom
= aEvent
->userType
;
1209 if (aEvent
->flags
& NS_EVENT_FLAG_NO_DEFAULT
) {
1210 *aEventStatus
= nsEventStatus_eConsumeNoDefault
;
1217 nsEventListenerManager::Disconnect()
1220 return RemoveAllListeners();
1224 nsEventListenerManager::SetListenerTarget(nsISupports
* aTarget
)
1226 NS_PRECONDITION(aTarget
, "unexpected null pointer");
1228 //WEAK reference, must be set back to nsnull when done by calling Disconnect
1234 nsEventListenerManager::GetSystemEventGroupLM(nsIDOMEventGroup
**aGroup
)
1236 if (!gSystemEventGroup
) {
1238 nsCOMPtr
<nsIDOMEventGroup
> group(do_CreateInstance(kDOMEventGroupCID
,&result
));
1239 if (NS_FAILED(result
))
1242 gSystemEventGroup
= group
;
1243 NS_ADDREF(gSystemEventGroup
);
1246 *aGroup
= gSystemEventGroup
;
1252 nsEventListenerManager::GetDOM2EventGroup(nsIDOMEventGroup
**aGroup
)
1254 if (!gDOM2EventGroup
) {
1256 nsCOMPtr
<nsIDOMEventGroup
> group(do_CreateInstance(kDOMEventGroupCID
,&result
));
1257 if (NS_FAILED(result
))
1260 gDOM2EventGroup
= group
;
1261 NS_ADDREF(gDOM2EventGroup
);
1264 *aGroup
= gDOM2EventGroup
;
1269 // nsIDOMEventTarget interface
1271 nsEventListenerManager::AddEventListener(const nsAString
& aType
,
1272 nsIDOMEventListener
* aListener
,
1275 PRInt32 flags
= aUseCapture
? NS_EVENT_FLAG_CAPTURE
: NS_EVENT_FLAG_BUBBLE
;
1277 nsresult rv
= AddEventListenerByType(aListener
, aType
, flags
, nsnull
);
1278 NS_ASSERTION(NS_FAILED(rv
) || HasListenersFor(aType
),
1279 "Adding event listener didn't work!");
1284 nsEventListenerManager::RemoveEventListener(const nsAString
& aType
,
1285 nsIDOMEventListener
* aListener
,
1288 PRInt32 flags
= aUseCapture
? NS_EVENT_FLAG_CAPTURE
: NS_EVENT_FLAG_BUBBLE
;
1290 return RemoveEventListenerByType(aListener
, aType
, flags
, nsnull
);
1294 nsEventListenerManager::DispatchEvent(nsIDOMEvent
* aEvent
, PRBool
*_retval
)
1296 nsCOMPtr
<nsIContent
> targetContent(do_QueryInterface(mTarget
));
1297 if (!targetContent
) {
1298 // nothing to dispatch on -- bad!
1299 return NS_ERROR_FAILURE
;
1302 // XXX sXBL/XBL2 issue -- do we really want the owner here? What
1303 // if that's the XBL document? Would we want its presshell? Or what?
1304 nsCOMPtr
<nsIDocument
> document
= targetContent
->GetOwnerDoc();
1306 // Do nothing if the element does not belong to a document
1311 // Obtain a presentation shell
1312 nsIPresShell
*shell
= document
->GetPrimaryShell();
1313 nsCOMPtr
<nsPresContext
> context
;
1315 context
= shell
->GetPresContext();
1318 nsEventStatus status
= nsEventStatus_eIgnore
;
1320 nsEventDispatcher::DispatchDOMEvent(targetContent
, nsnull
, aEvent
,
1322 *_retval
= (status
!= nsEventStatus_eConsumeNoDefault
);
1326 // nsIDOM3EventTarget interface
1328 nsEventListenerManager::AddGroupedEventListener(const nsAString
& aType
,
1329 nsIDOMEventListener
* aListener
,
1331 nsIDOMEventGroup
* aEvtGrp
)
1333 PRInt32 flags
= aUseCapture
? NS_EVENT_FLAG_CAPTURE
: NS_EVENT_FLAG_BUBBLE
;
1335 return AddEventListenerByType(aListener
, aType
, flags
, aEvtGrp
);
1339 nsEventListenerManager::RemoveGroupedEventListener(const nsAString
& aType
,
1340 nsIDOMEventListener
* aListener
,
1342 nsIDOMEventGroup
* aEvtGrp
)
1344 PRInt32 flags
= aUseCapture
? NS_EVENT_FLAG_CAPTURE
: NS_EVENT_FLAG_BUBBLE
;
1346 return RemoveEventListenerByType(aListener
, aType
, flags
, aEvtGrp
);
1350 nsEventListenerManager::CanTrigger(const nsAString
& type
, PRBool
*_retval
)
1352 return NS_ERROR_NOT_IMPLEMENTED
;
1356 nsEventListenerManager::IsRegisteredHere(const nsAString
& type
, PRBool
*_retval
)
1358 return NS_ERROR_NOT_IMPLEMENTED
;
1362 nsEventListenerManager::FixContextMenuEvent(nsPresContext
* aPresContext
,
1363 nsISupports
* aCurrentTarget
,
1365 nsIDOMEvent
** aDOMEvent
)
1367 nsIPresShell
* shell
= aPresContext
? aPresContext
->GetPresShell() : nsnull
;
1373 nsresult ret
= NS_OK
;
1375 PRBool contextMenuKey
=
1376 static_cast<nsMouseEvent
*>(aEvent
)->context
== nsMouseEvent::eContextMenuKey
;
1377 if (nsnull
== *aDOMEvent
) {
1378 // If we're here because of the key-equiv for showing context menus, we
1379 // have to twiddle with the NS event to make sure the context menu comes
1380 // up in the upper left of the relevant content area before we create
1381 // the DOM event. Since we never call InitMouseEvent() on the event,
1382 // the client X/Y will be 0,0. We can make use of that if the widget is null.
1383 if (contextMenuKey
) {
1384 aPresContext
->GetViewManager()->GetWidget(getter_AddRefs(((nsGUIEvent
*)aEvent
)->widget
));
1385 aEvent
->refPoint
.x
= 0;
1386 aEvent
->refPoint
.y
= 0;
1388 ret
= NS_NewDOMMouseEvent(aDOMEvent
, aPresContext
, static_cast<nsInputEvent
*>(aEvent
));
1389 NS_ENSURE_SUCCESS(ret
, ret
);
1392 // see if we should use the caret position for the popup
1393 if (contextMenuKey
) {
1395 // Beware! This may flush notifications via synchronous
1396 // ScrollSelectionIntoView.
1397 if (PrepareToUseCaretPosition(((nsGUIEvent
*)aEvent
)->widget
,
1398 shell
, caretPoint
)) {
1399 // caret position is good
1400 aEvent
->refPoint
.x
= caretPoint
.x
;
1401 aEvent
->refPoint
.y
= caretPoint
.y
;
1406 // If we're here because of the key-equiv for showing context menus, we
1407 // have to reset the event target to the currently focused element. Get it
1408 // from the focus controller.
1409 nsCOMPtr
<nsIDOMEventTarget
> currentTarget
= do_QueryInterface(aCurrentTarget
);
1410 nsCOMPtr
<nsIDOMElement
> currentFocus
;
1412 if (contextMenuKey
) {
1413 nsIDocument
*doc
= shell
->GetDocument();
1415 nsPIDOMWindow
* privWindow
= doc
->GetWindow();
1417 nsIFocusController
*focusController
=
1418 privWindow
->GetRootFocusController();
1419 if (focusController
)
1420 focusController
->GetFocusedElement(getter_AddRefs(currentFocus
));
1426 // Reset event coordinates relative to focused frame in view
1428 GetCoordinatesFor(currentFocus
, aPresContext
, shell
, targetPt
);
1429 aEvent
->refPoint
.x
= targetPt
.x
;
1430 aEvent
->refPoint
.y
= targetPt
.y
;
1432 currentTarget
= do_QueryInterface(currentFocus
);
1433 nsCOMPtr
<nsIPrivateDOMEvent
> pEvent(do_QueryInterface(*aDOMEvent
));
1434 pEvent
->SetTarget(currentTarget
);
1440 // nsEventListenerManager::PrepareToUseCaretPosition
1442 // This checks to see if we should use the caret position for popup context
1443 // menus. Returns true if the caret position should be used, and the
1444 // coordinates of that position is returned in aTargetPt. This function
1445 // will also scroll the window as needed to make the caret visible.
1447 // The event widget should be the widget that generated the event, and
1448 // whose coordinate system the resulting event's refPoint should be
1449 // relative to. The returned point is in device pixels realtive to the
1450 // widget passed in.
1453 nsEventListenerManager::PrepareToUseCaretPosition(nsIWidget
* aEventWidget
,
1454 nsIPresShell
* aShell
,
1459 // check caret visibility
1460 nsRefPtr
<nsCaret
> caret
;
1461 rv
= aShell
->GetCaret(getter_AddRefs(caret
));
1462 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
1463 NS_ENSURE_TRUE(caret
, PR_FALSE
);
1465 PRBool caretVisible
= PR_FALSE
;
1466 rv
= caret
->GetCaretVisible(&caretVisible
);
1467 if (NS_FAILED(rv
) || ! caretVisible
)
1470 // caret selection, this is a temporary weak reference, so no refcounting is
1472 nsISelection
* domSelection
= caret
->GetCaretDOMSelection();
1473 NS_ENSURE_TRUE(domSelection
, PR_FALSE
);
1475 // since the match could be an anonymous textnode inside a
1476 // <textarea> or text <input>, we need to get the outer frame
1477 // note: frames are not refcounted
1478 nsIFrame
* frame
= nsnull
; // may be NULL
1479 nsCOMPtr
<nsIDOMNode
> node
;
1480 rv
= domSelection
->GetFocusNode(getter_AddRefs(node
));
1481 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
1482 NS_ENSURE_TRUE(node
, PR_FALSE
);
1483 nsCOMPtr
<nsIContent
> content(do_QueryInterface(node
));
1485 nsIContent
* nonNative
= content
->FindFirstNonNativeAnonymous();
1486 content
= nonNative
;
1490 // It seems like selCon->ScrollSelectionIntoView should be enough, but it's
1491 // not. The problem is that scrolling the selection into view when it is
1492 // below the current viewport will align the top line of the frame exactly
1493 // with the bottom of the window. This is fine, BUT, the popup event causes
1494 // the control to be re-focused which does this exact call to
1495 // ScrollContentIntoView, which has a one-pixel disagreement of whether the
1496 // frame is actually in view. The result is that the frame is aligned with
1497 // the top of the window, but the menu is still at the bottom.
1499 // Doing this call first forces the frame to be in view, eliminating the
1500 // problem. The only difference in the result is that if your cursor is in
1501 // an edit box below the current view, you'll get the edit box aligned with
1502 // the top of the window. This is arguably better behavior anyway.
1503 rv
= aShell
->ScrollContentIntoView(content
,
1504 NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE
,
1505 NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE
);
1506 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
1507 frame
= aShell
->GetPrimaryFrameFor(content
);
1508 NS_WARN_IF_FALSE(frame
, "No frame for focused content?");
1511 // Actually scroll the selection (ie caret) into view. Note that this must
1512 // be synchronous since we will be checking the caret position on the screen.
1514 // Be easy about errors, and just don't scroll in those cases. Better to have
1515 // the correct menu at a weird place than the wrong menu.
1516 nsCOMPtr
<nsISelectionController
> selCon
;
1518 frame
->GetSelectionController(aShell
->GetPresContext(),
1519 getter_AddRefs(selCon
));
1521 selCon
= do_QueryInterface(aShell
);
1523 // After ScrollSelectionIntoView(), the pending notifications might be
1524 // flushed and PresShell/PresContext/Frames may be dead. See bug 418470.
1525 rv
= selCon
->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL
,
1526 nsISelectionController::SELECTION_FOCUS_REGION
, PR_TRUE
);
1527 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
1530 // get caret position relative to some view (normally the same as the
1531 // event widget view, but this is not guaranteed)
1535 rv
= caret
->GetCaretCoordinates(nsCaret::eRenderingViewCoordinates
,
1536 domSelection
, &caretCoords
, &isCollapsed
,
1538 NS_ENSURE_SUCCESS(rv
, PR_FALSE
);
1540 // in case the view used for caret coordinates was something else, we need
1541 // to bring those coordinates into the space of the widget view
1542 nsIView
* widgetView
= nsIView::GetViewFor(aEventWidget
);
1543 NS_ENSURE_TRUE(widgetView
, PR_FALSE
);
1544 nsPoint viewToWidget
;
1545 widgetView
->GetNearestWidget(&viewToWidget
);
1546 nsPoint viewDelta
= view
->GetOffsetTo(widgetView
) + viewToWidget
;
1548 // caret coordinates are in twips, convert to pixels
1549 nsPresContext
* presContext
= aShell
->GetPresContext();
1550 aTargetPt
.x
= presContext
->AppUnitsToDevPixels(viewDelta
.x
+ caretCoords
.x
+ caretCoords
.width
);
1551 aTargetPt
.y
= presContext
->AppUnitsToDevPixels(viewDelta
.y
+ caretCoords
.y
+ caretCoords
.height
);
1556 // Get coordinates in device pixels relative to root view for element,
1557 // first ensuring the element is onscreen
1559 nsEventListenerManager::GetCoordinatesFor(nsIDOMElement
*aCurrentEl
,
1560 nsPresContext
*aPresContext
,
1561 nsIPresShell
*aPresShell
,
1564 nsCOMPtr
<nsIContent
> focusedContent(do_QueryInterface(aCurrentEl
));
1565 aPresShell
->ScrollContentIntoView(focusedContent
,
1566 NS_PRESSHELL_SCROLL_ANYWHERE
,
1567 NS_PRESSHELL_SCROLL_ANYWHERE
);
1569 PRBool istree
= PR_FALSE
, checkLineHeight
= PR_TRUE
;
1570 PRInt32 extraPixelsY
= 0, extraTreeY
= 0;
1573 // Set the position to just underneath the current item for multi-select
1574 // lists or just underneath the selected item for single-select lists. If
1575 // the element is not a list, or there is no selection, leave the position
1577 nsCOMPtr
<nsIDOMXULSelectControlItemElement
> item
;
1578 nsCOMPtr
<nsIDOMXULMultiSelectControlElement
> multiSelect
=
1579 do_QueryInterface(aCurrentEl
);
1581 checkLineHeight
= PR_FALSE
;
1583 PRInt32 currentIndex
;
1584 multiSelect
->GetCurrentIndex(¤tIndex
);
1585 if (currentIndex
>= 0) {
1586 nsCOMPtr
<nsIDOMXULElement
> xulElement(do_QueryInterface(aCurrentEl
));
1588 nsCOMPtr
<nsIBoxObject
> box
;
1589 xulElement
->GetBoxObject(getter_AddRefs(box
));
1590 nsCOMPtr
<nsITreeBoxObject
> treeBox(do_QueryInterface(box
));
1591 // Tree view special case (tree items have no frames)
1592 // Get the focused row and add its coordinates, which are already in pixels
1593 // XXX Boris, should we create a new interface so that event listener manager doesn't
1594 // need to know about trees? Something like nsINodelessChildCreator which
1595 // could provide the current focus coordinates?
1597 treeBox
->EnsureRowIsVisible(currentIndex
);
1598 PRInt32 firstVisibleRow
, rowHeight
;
1599 treeBox
->GetFirstVisibleRow(&firstVisibleRow
);
1600 treeBox
->GetRowHeight(&rowHeight
);
1602 extraPixelsY
= (currentIndex
- firstVisibleRow
+ 1) * rowHeight
;
1605 nsCOMPtr
<nsITreeColumns
> cols
;
1606 treeBox
->GetColumns(getter_AddRefs(cols
));
1608 nsCOMPtr
<nsITreeColumn
> col
;
1609 cols
->GetFirstColumn(getter_AddRefs(col
));
1611 nsCOMPtr
<nsIDOMElement
> colElement
;
1612 col
->GetElement(getter_AddRefs(colElement
));
1613 nsCOMPtr
<nsIContent
> colContent(do_QueryInterface(colElement
));
1615 nsIFrame
* frame
= aPresShell
->GetPrimaryFrameFor(colContent
);
1617 extraTreeY
= frame
->GetSize().height
;
1624 multiSelect
->GetCurrentItem(getter_AddRefs(item
));
1630 // don't check menulists as the selected item will be inside a popup.
1631 nsCOMPtr
<nsIDOMXULMenuListElement
> menulist
= do_QueryInterface(aCurrentEl
);
1633 checkLineHeight
= PR_FALSE
;
1634 nsCOMPtr
<nsIDOMXULSelectControlElement
> select
=
1635 do_QueryInterface(aCurrentEl
);
1637 select
->GetSelectedItem(getter_AddRefs(item
));
1642 focusedContent
= do_QueryInterface(item
);
1645 nsIFrame
*frame
= aPresShell
->GetPrimaryFrameFor(focusedContent
);
1647 nsPoint
frameOrigin(0, 0);
1649 // Get the frame's origin within its view
1650 nsIView
*view
= frame
->GetClosestView(&frameOrigin
);
1651 NS_ASSERTION(view
, "No view for frame");
1653 nsIViewManager
* vm
= aPresShell
->GetViewManager();
1654 nsIView
*rootView
= nsnull
;
1655 vm
->GetRootView(rootView
);
1656 NS_ASSERTION(rootView
, "No root view in pres shell");
1658 // View's origin within its root view
1659 frameOrigin
+= view
->GetOffsetTo(rootView
);
1661 // Start context menu down and to the right from top left of frame
1662 // use the lineheight. This is a good distance to move the context
1663 // menu away from the top left corner of the frame. If we always
1664 // used the frame height, the context menu could end up far away,
1665 // for example when we're focused on linked images.
1666 // On the other hand, we want to use the frame height if it's less
1667 // than the current line height, so that the context menu appears
1668 // associated with the correct frame.
1671 extra
= frame
->GetSize().height
;
1672 if (checkLineHeight
) {
1673 nsIScrollableView
*scrollView
=
1674 nsLayoutUtils::GetNearestScrollingView(view
, nsLayoutUtils::eEither
);
1676 nscoord scrollViewLineHeight
;
1677 scrollView
->GetLineHeight(&scrollViewLineHeight
);
1678 if (extra
> scrollViewLineHeight
) {
1679 extra
= scrollViewLineHeight
;
1685 aTargetPt
.x
= aPresContext
->AppUnitsToDevPixels(frameOrigin
.x
);
1686 aTargetPt
.y
= aPresContext
->AppUnitsToDevPixels(
1687 frameOrigin
.y
+ extra
+ extraTreeY
) + extraPixelsY
;
1692 nsEventListenerManager::HasMutationListeners(PRBool
* aListener
)
1694 *aListener
= PR_FALSE
;
1695 if (mMayHaveMutationListeners
) {
1696 PRUint32 count
= mListeners
.Length();
1697 for (PRUint32 i
= 0; i
< count
; ++i
) {
1698 nsListenerStruct
* ls
= &mListeners
.ElementAt(i
);
1699 if (ls
->mEventType
>= NS_MUTATION_START
&&
1700 ls
->mEventType
<= NS_MUTATION_END
) {
1701 *aListener
= PR_TRUE
;
1711 nsEventListenerManager::MutationListenerBits()
1714 if (mMayHaveMutationListeners
) {
1715 PRUint32 count
= mListeners
.Length();
1716 for (PRUint32 i
= 0; i
< count
; ++i
) {
1717 nsListenerStruct
* ls
= &mListeners
.ElementAt(i
);
1718 if (ls
->mEventType
>= NS_MUTATION_START
&&
1719 ls
->mEventType
<= NS_MUTATION_END
) {
1720 if (ls
->mEventType
== NS_MUTATION_SUBTREEMODIFIED
) {
1721 return kAllMutationBits
;
1723 bits
|= MutationBitForEventType(ls
->mEventType
);
1731 nsEventListenerManager::HasListenersFor(const nsAString
& aEventName
)
1733 nsCOMPtr
<nsIAtom
> atom
= do_GetAtom(NS_LITERAL_STRING("on") + aEventName
);
1734 PRUint32 type
= nsContentUtils::GetEventId(atom
);
1736 const EventTypeData
* typeData
= nsnull
;
1737 const EventDispatchData
* dispData
= nsnull
;
1738 if (type
!= NS_USER_DEFINED_EVENT
) {
1739 for (PRUint32 i
= 0; i
< NS_ARRAY_LENGTH(sEventTypes
); ++i
) {
1740 typeData
= &sEventTypes
[i
];
1741 for (PRInt32 j
= 0; j
< typeData
->numEvents
; ++j
) {
1742 dispData
= &(typeData
->events
[j
]);
1743 if (type
== dispData
->message
) {
1753 PRUint32 count
= mListeners
.Length();
1754 for (PRUint32 i
= 0; i
< count
; ++i
) {
1755 nsListenerStruct
* ls
= &mListeners
.ElementAt(i
);
1756 if (ls
->mTypeAtom
== atom
||
1757 EVENT_TYPE_DATA_EQUALS(ls
->mTypeData
, typeData
)) {
1765 nsEventListenerManager::HasListeners()
1767 return !mListeners
.IsEmpty();
1771 nsEventListenerManager::HasUnloadListeners()
1773 PRUint32 count
= mListeners
.Length();
1774 for (PRUint32 i
= 0; i
< count
; ++i
) {
1775 nsListenerStruct
* ls
= &mListeners
.ElementAt(i
);
1776 if (ls
->mEventType
== NS_PAGE_UNLOAD
||
1777 ls
->mEventType
== NS_BEFORE_PAGE_UNLOAD
||
1778 (ls
->mTypeData
&& ls
->mTypeData
->iid
&&
1779 ls
->mTypeData
->iid
->Equals(NS_GET_IID(nsIDOMLoadListener
)))) {
1787 NS_NewEventListenerManager(nsIEventListenerManager
** aInstancePtrResult
)
1789 nsIEventListenerManager
* l
= new nsEventListenerManager();
1792 return NS_ERROR_OUT_OF_MEMORY
;
1795 return CallQueryInterface(l
, aInstancePtrResult
);