Follow-on fix for bug 457825. Use sheet principal for agent and user sheets. r=dbaron...
[wine-gecko.git] / content / events / src / nsEventListenerManager.cpp
blobe1f4ef5a64b6506d624a4ef38f16ebb573647e32
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
13 * License.
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.
22 * Contributor(s):
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"
42 #include "nsCaret.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"
58 #ifdef MOZ_SVG
59 #include "nsIDOMSVGListener.h"
60 #include "nsIDOMSVGZoomListener.h"
61 #include "nsGkAtoms.h"
62 #endif // MOZ_SVG
63 #include "nsIEventStateManager.h"
64 #include "nsPIDOMWindow.h"
65 #include "nsIPrivateDOMEvent.h"
66 #include "nsIJSEventListener.h"
67 #include "prmem.h"
68 #include "nsIScriptGlobalObject.h"
69 #include "nsIScriptRuntime.h"
70 #include "nsLayoutUtils.h"
71 #ifdef MOZ_XUL
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"
79 #endif
80 #include "nsINameSpaceManager.h"
81 #include "nsIContent.h"
82 #include "nsIFrame.h"
83 #include "nsIView.h"
84 #include "nsIViewManager.h"
85 #include "nsIScrollableView.h"
86 #include "nsCOMPtr.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"
95 #include "nsDOMCID.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;
132 static PRUint32
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;
150 default:
151 break;
153 return 0;
156 typedef
157 NS_STDCALL_FUNCPROTO(nsresult,
158 GenericHandler,
159 nsIDOMEventListener, HandleEvent,
160 (nsIDOMEvent*));
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,
176 const nsIID& aIID)
178 nsIDOMEventListener* ifaceListener = nsnull;
179 nsresult rv = NS_OK;
180 aListener->QueryInterface(aIID, (void**) &ifaceListener);
181 NS_WARN_IF_FALSE(ifaceListener,
182 "DispatchToInterface couldn't QI to the right interface");
183 if (ifaceListener) {
184 rv = (ifaceListener->*aMethod)(aEvent);
185 NS_RELEASE(ifaceListener);
187 return rv;
190 struct EventDispatchData
192 PRUint32 message;
193 GenericHandler method;
196 struct EventTypeData
198 const EventDispatchData* events;
199 int numEvents;
200 const nsIID* iid;
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) }
291 #ifdef MOZ_SVG
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) }
304 #endif // MOZ_SVG
306 #define IMPL_EVENTTYPEDATA(type) \
308 s##type##Events, \
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)
328 #ifdef MOZ_SVG
330 IMPL_EVENTTYPEDATA(SVG),
331 IMPL_EVENTTYPEDATA(SVGZoom)
332 #endif // MOZ_SVG
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() :
345 mTarget(nsnull)
347 ++mInstanceCount;
348 ++sCreatedCount;
351 nsEventListenerManager::~nsEventListenerManager()
353 NS_ASSERTION(!mTarget, "didn't call Disconnect");
354 RemoveAllListeners();
356 --mInstanceCount;
357 if(mInstanceCount == 0) {
358 NS_IF_RELEASE(gSystemEventGroup);
359 NS_IF_RELEASE(gDOM2EventGroup);
360 delete gEventIdTable;
361 gEventIdTable = nsnull;
365 nsresult
366 nsEventListenerManager::RemoveAllListeners()
368 mListeners.Clear();
369 return NS_OK;
372 void
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)
386 NS_INTERFACE_MAP_END
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)
400 tmp->Disconnect();
401 NS_IMPL_CYCLE_COLLECTION_UNLINK_END
404 const EventTypeData*
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];
412 return nsnull;
415 const EventTypeData*
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];
428 return nsnull;
431 nsPIDOMWindow*
432 nsEventListenerManager::GetInnerWindowForTarget()
434 nsCOMPtr<nsINode> node = do_QueryInterface(mTarget);
435 if (node) {
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();
439 if (document)
440 return document->GetInnerWindow();
443 nsCOMPtr<nsPIDOMWindow> window = do_QueryInterface(mTarget);
444 if (window) {
445 NS_ASSERTION(window->IsInnerWindow(), "Target should not be an outer window");
446 return window;
449 return nsnull;
452 nsresult
453 nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
454 PRUint32 aType,
455 nsIAtom* aTypeAtom,
456 const EventTypeData* aTypeData,
457 PRInt32 aFlags,
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;
466 PRUint16 group = 0;
467 nsCOMPtr<nsIDOMEventGroup> sysGroup;
468 GetSystemEventGroupLM(getter_AddRefs(sysGroup));
469 if (sysGroup) {
470 sysGroup->IsSameEventGroup(aEvtGrp, &isSame);
471 if (isSame) {
472 group = NS_EVENT_FLAG_SYSTEM_EVENT;
476 if (!aTypeData) {
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
480 // would fail.
481 // @see also DispatchToInterface()
482 const EventTypeData* td = GetTypeDataForEventName(aTypeAtom);
483 if (td && td->iid) {
484 nsIDOMEventListener* ifaceListener = nsnull;
485 aListener->QueryInterface(*(td->iid), (void**) &ifaceListener);
486 if (ifaceListener) {
487 aTypeData = td;
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))) {
501 return NS_OK;
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;
514 ls->mFlags = aFlags;
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();
522 if (window) {
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();
531 if (window) {
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) ?
535 kAllMutationBits :
536 MutationBitForEventType(aType));
540 return NS_OK;
543 nsresult
544 nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
545 PRUint32 aType,
546 nsIAtom* aUserType,
547 const EventTypeData* aTypeData,
548 PRInt32 aFlags,
549 nsIDOMEventGroup* aEvtGrp)
551 if (!aListener || !(aType || aTypeData)) {
552 return NS_OK;
555 PRBool isSame = PR_FALSE;
556 PRUint16 group = 0;
557 nsCOMPtr<nsIDOMEventGroup> sysGroup;
558 GetSystemEventGroupLM(getter_AddRefs(sysGroup));
559 if (sysGroup) {
560 sysGroup->IsSameEventGroup(aEvtGrp, &isSame);
561 if (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;
581 break;
585 return NS_OK;
588 nsresult
589 nsEventListenerManager::AddEventListenerByIID(nsIDOMEventListener *aListener,
590 const nsIID& aIID,
591 PRInt32 aFlags)
593 AddEventListener(aListener, NS_EVENT_TYPE_NULL, nsnull,
594 GetTypeDataForIID(aIID), aFlags, nsnull);
595 return NS_OK;
598 NS_IMETHODIMP
599 nsEventListenerManager::RemoveEventListenerByIID(nsIDOMEventListener *aListener,
600 const nsIID& aIID,
601 PRInt32 aFlags)
603 RemoveEventListener(aListener, NS_EVENT_TYPE_NULL, nsnull,
604 GetTypeDataForIID(aIID), aFlags, nsnull);
605 return NS_OK;
608 PRBool
609 nsEventListenerManager::ListenerCanHandle(nsListenerStruct* aLs,
610 nsEvent* aEvent)
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);
619 NS_IMETHODIMP
620 nsEventListenerManager::AddEventListenerByType(nsIDOMEventListener *aListener,
621 const nsAString& aType,
622 PRInt32 aFlags,
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);
628 return NS_OK;
631 NS_IMETHODIMP
632 nsEventListenerManager::RemoveEventListenerByType(nsIDOMEventListener *aListener,
633 const nsAString& aType,
634 PRInt32 aFlags,
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);
640 return NS_OK;
643 nsListenerStruct*
644 nsEventListenerManager::FindJSEventListener(PRUint32 aEventType,
645 nsIAtom* aTypeAtom)
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) {
655 return ls;
658 return nsnull;
661 nsresult
662 nsEventListenerManager::SetJSEventListener(nsIScriptContext *aContext,
663 void *aScopeObject,
664 nsISupports *aObject,
665 nsIAtom* aName,
666 PRBool aIsString,
667 PRBool aPermitUntrustedEvents)
669 nsresult rv = NS_OK;
670 PRUint32 eventType = nsContentUtils::GetEventId(aName);
671 nsListenerStruct* ls = FindJSEventListener(eventType, aName);
673 if (!ls) {
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;
696 return rv;
699 NS_IMETHODIMP
700 nsEventListenerManager::AddScriptEventListener(nsISupports *aObject,
701 nsIAtom *aName,
702 const nsAString& aBody,
703 PRUint32 aLanguage,
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
715 // of the event.
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;
729 if (node) {
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();
734 if (doc)
735 global = doc->GetScriptGlobalObject();
736 } else {
737 nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aObject));
738 if (win) {
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);
746 } else {
747 global = do_QueryInterface(aObject);
751 if (!global) {
752 // This can happen; for example this document might have been
753 // loaded as data.
754 return NS_OK;
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);
768 nsresult rv;
770 if (!aDeferCompilation) {
771 nsCOMPtr<nsIScriptEventHandlerOwner> handlerOwner =
772 do_QueryInterface(aObject);
774 nsScriptObjectHolder handler(context);
775 PRBool done = PR_FALSE;
777 if (handlerOwner) {
778 rv = handlerOwner->GetCompiledEventHandler(aName, handler);
779 if (NS_SUCCEEDED(rv) && handler) {
780 rv = context->BindCompiledEventHandler(aObject, scope, aName, handler);
781 if (NS_FAILED(rv))
782 return rv;
783 done = PR_TRUE;
787 if (!done) {
788 PRUint32 lineNo = 0;
789 nsCAutoString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
790 if (doc) {
791 nsIURI *uri = doc->GetDocumentURI();
792 if (uri) {
793 uri->GetSpec(url);
794 lineNo = 1;
798 if (handlerOwner) {
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);
804 else {
805 PRInt32 nameSpace = kNameSpaceID_Unknown;
806 if (node && node->IsNodeOfType(nsINode::eCONTENT)) {
807 nsIContent* content = static_cast<nsIContent*>(node.get());
808 nameSpace = content->GetNameSpaceID();
810 else if (doc) {
811 nsCOMPtr<nsIContent> root = doc->GetRootContent();
812 if (root)
813 nameSpace = root->GetNameSpaceID();
815 PRUint32 argCount;
816 const char **argNames;
817 nsContentUtils::GetEventArgNames(nameSpace, aName, &argCount,
818 &argNames);
820 rv = context->CompileEventHandler(aName, argCount, argNames,
821 aBody,
822 url.get(), lineNo,
823 SCRIPTVERSION_DEFAULT, // for now?
824 handler);
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);
831 // And bind it.
832 rv = context->BindCompiledEventHandler(aObject, scope,
833 aName, handler);
835 if (NS_FAILED(rv)) return rv;
839 return SetJSEventListener(context, scope, objiSupp, aName, aDeferCompilation,
840 aPermitUntrustedEvents);
843 nsresult
844 nsEventListenerManager::RemoveScriptEventListener(nsIAtom* aName)
846 PRUint32 eventType = nsContentUtils::GetEventId(aName);
847 nsListenerStruct* ls = FindJSEventListener(eventType, aName);
849 if (ls) {
850 mListeners.RemoveElementAt(PRUint32(ls - &mListeners.ElementAt(0)));
851 mNoListenerForEvent = NS_EVENT_TYPE_NULL;
852 mNoListenerForEventAtom = nsnull;
855 return NS_OK;
858 jsval
859 nsEventListenerManager::sAddListenerID = JSVAL_VOID;
861 NS_IMETHODIMP
862 nsEventListenerManager::RegisterScriptEventListener(nsIScriptContext *aContext,
863 void *aScope,
864 nsISupports *aObject,
865 nsIAtom *aName)
867 // Check that we have access to set an event listener. Prevents
868 // snooping attacks across domains by setting onkeypress handlers,
869 // for instance.
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.
873 nsresult rv;
874 nsCOMPtr<nsIJSContextStack> stack =
875 do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
876 if (NS_FAILED(rv))
877 return rv;
878 JSContext *cx;
879 if (NS_FAILED(rv = stack->Peek(&cx)))
880 return rv;
882 if (cx) {
883 if (sAddListenerID == JSVAL_VOID) {
884 JSAutoRequest ar(cx);
885 sAddListenerID =
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,
902 "EventTarget",
903 sAddListenerID,
904 nsIXPCSecurityManager::ACCESS_SET_PROPERTY);
905 if (NS_FAILED(rv)) {
906 // XXX set pending exception on the native call context?
907 return rv;
909 } else {
910 NS_WARNING("Skipping CheckPropertyAccess for non JS language");
915 // Untrusted events are always permitted for non-chrome script
916 // handlers.
917 return SetJSEventListener(aContext, aScope, aObject, aName,
918 PR_FALSE, !nsContentUtils::IsCallerChrome());
921 nsresult
922 nsEventListenerManager::CompileScriptEventListener(nsIScriptContext *aContext,
923 void *aScope,
924 nsISupports *aObject,
925 nsIAtom *aName,
926 PRBool *aDidCompile)
928 nsresult rv = NS_OK;
929 *aDidCompile = PR_FALSE;
930 PRUint32 eventType = nsContentUtils::GetEventId(aName);
931 nsListenerStruct* ls = FindJSEventListener(eventType, aName);
933 if (!ls) {
934 //nothing to compile
935 return NS_OK;
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
946 // us.
948 *aDidCompile = PR_TRUE;
950 return rv;
953 nsresult
954 nsEventListenerManager::CompileEventHandlerInternal(nsIScriptContext *aContext,
955 void *aScope,
956 nsISupports *aObject,
957 nsIAtom *aName,
958 nsListenerStruct *aListenerStruct,
959 nsISupports* aCurrentTarget)
961 nsresult result = NS_OK;
963 nsCOMPtr<nsIScriptEventHandlerOwner> handlerOwner =
964 do_QueryInterface(aObject);
965 nsScriptObjectHolder handler(aContext);
967 if (handlerOwner) {
968 result = handlerOwner->GetCompiledEventHandler(aName,
969 handler);
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");
984 if (content) {
985 nsAutoString handlerBody;
986 nsIAtom* attrName = aName;
987 #ifdef MOZ_SVG
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;
1002 #endif // MOZ_SVG
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);
1010 if (node) {
1011 doc = node->GetOwnerDoc();
1013 if (doc) {
1014 nsIURI *uri = doc->GetDocumentURI();
1015 if (uri) {
1016 uri->GetSpec(url);
1017 lineNo = 1;
1021 if (handlerOwner) {
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,
1026 handlerBody,
1027 url.get(), lineNo,
1028 handler);
1030 else {
1031 PRUint32 argCount;
1032 const char **argNames;
1033 nsContentUtils::GetEventArgNames(content->GetNameSpaceID(), aName,
1034 &argCount, &argNames);
1036 result = aContext->CompileEventHandler(aName,
1037 argCount, argNames,
1038 handlerBody,
1039 url.get(), lineNo,
1040 SCRIPTVERSION_DEFAULT, // for now?
1041 handler);
1042 NS_ENSURE_SUCCESS(result, result);
1043 // And bind it.
1044 result = aContext->BindCompiledEventHandler(aObject, aScope,
1045 aName, handler);
1046 NS_ENSURE_SUCCESS(result, result);
1049 if (NS_SUCCEEDED(result)) {
1050 aListenerStruct->mHandlerIsString = PR_FALSE;
1055 return result;
1058 nsresult
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);
1072 if (jslistener) {
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,
1081 aCurrentTarget);
1086 // nsCxPusher will push and pop (automatically) the current cx onto the
1087 // context stack
1088 nsCxPusher pusher;
1089 if (NS_SUCCEEDED(result) && pusher.Push(aCurrentTarget)) {
1090 // nsIDOMEvent::currentTarget is set in nsEventDispatcher.
1091 result = aListener->HandleEvent(aDOMEvent);
1094 return result;
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
1106 nsresult
1107 nsEventListenerManager::HandleEvent(nsPresContext* aPresContext,
1108 nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
1109 nsPIDOMEventTarget* aCurrentTarget,
1110 PRUint32 aFlags,
1111 nsEventStatus* aEventStatus)
1113 if (mListeners.IsEmpty() || aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) {
1114 return NS_OK;
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)) {
1121 return NS_OK;
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,
1134 aDOMEvent))) {
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;
1146 goto found;
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;
1156 goto found;
1159 typeData = nsnull;
1160 dispData = nsnull;
1164 found:
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)) {
1185 if (!*aDOMEvent) {
1186 nsEventDispatcher::CreateEvent(aPresContext, aEvent,
1187 EmptyString(), aDOMEvent);
1189 if (*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);
1204 if (!hasListener) {
1205 mNoListenerForEvent = aEvent->message;
1206 mNoListenerForEventAtom = aEvent->userType;
1209 if (aEvent->flags & NS_EVENT_FLAG_NO_DEFAULT) {
1210 *aEventStatus = nsEventStatus_eConsumeNoDefault;
1213 return NS_OK;
1216 NS_IMETHODIMP
1217 nsEventListenerManager::Disconnect()
1219 mTarget = nsnull;
1220 return RemoveAllListeners();
1223 NS_IMETHODIMP
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
1229 mTarget = aTarget;
1230 return NS_OK;
1233 NS_IMETHODIMP
1234 nsEventListenerManager::GetSystemEventGroupLM(nsIDOMEventGroup **aGroup)
1236 if (!gSystemEventGroup) {
1237 nsresult result;
1238 nsCOMPtr<nsIDOMEventGroup> group(do_CreateInstance(kDOMEventGroupCID,&result));
1239 if (NS_FAILED(result))
1240 return result;
1242 gSystemEventGroup = group;
1243 NS_ADDREF(gSystemEventGroup);
1246 *aGroup = gSystemEventGroup;
1247 NS_ADDREF(*aGroup);
1248 return NS_OK;
1251 nsresult
1252 nsEventListenerManager::GetDOM2EventGroup(nsIDOMEventGroup **aGroup)
1254 if (!gDOM2EventGroup) {
1255 nsresult result;
1256 nsCOMPtr<nsIDOMEventGroup> group(do_CreateInstance(kDOMEventGroupCID,&result));
1257 if (NS_FAILED(result))
1258 return result;
1260 gDOM2EventGroup = group;
1261 NS_ADDREF(gDOM2EventGroup);
1264 *aGroup = gDOM2EventGroup;
1265 NS_ADDREF(*aGroup);
1266 return NS_OK;
1269 // nsIDOMEventTarget interface
1270 NS_IMETHODIMP
1271 nsEventListenerManager::AddEventListener(const nsAString& aType,
1272 nsIDOMEventListener* aListener,
1273 PRBool aUseCapture)
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!");
1280 return rv;
1283 NS_IMETHODIMP
1284 nsEventListenerManager::RemoveEventListener(const nsAString& aType,
1285 nsIDOMEventListener* aListener,
1286 PRBool aUseCapture)
1288 PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
1290 return RemoveEventListenerByType(aListener, aType, flags, nsnull);
1293 NS_IMETHODIMP
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
1307 if (!document) {
1308 return NS_OK;
1311 // Obtain a presentation shell
1312 nsIPresShell *shell = document->GetPrimaryShell();
1313 nsCOMPtr<nsPresContext> context;
1314 if (shell) {
1315 context = shell->GetPresContext();
1318 nsEventStatus status = nsEventStatus_eIgnore;
1319 nsresult rv =
1320 nsEventDispatcher::DispatchDOMEvent(targetContent, nsnull, aEvent,
1321 context, &status);
1322 *_retval = (status != nsEventStatus_eConsumeNoDefault);
1323 return rv;
1326 // nsIDOM3EventTarget interface
1327 NS_IMETHODIMP
1328 nsEventListenerManager::AddGroupedEventListener(const nsAString& aType,
1329 nsIDOMEventListener* aListener,
1330 PRBool aUseCapture,
1331 nsIDOMEventGroup* aEvtGrp)
1333 PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
1335 return AddEventListenerByType(aListener, aType, flags, aEvtGrp);
1338 NS_IMETHODIMP
1339 nsEventListenerManager::RemoveGroupedEventListener(const nsAString& aType,
1340 nsIDOMEventListener* aListener,
1341 PRBool aUseCapture,
1342 nsIDOMEventGroup* aEvtGrp)
1344 PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
1346 return RemoveEventListenerByType(aListener, aType, flags, aEvtGrp);
1349 NS_IMETHODIMP
1350 nsEventListenerManager::CanTrigger(const nsAString & type, PRBool *_retval)
1352 return NS_ERROR_NOT_IMPLEMENTED;
1355 NS_IMETHODIMP
1356 nsEventListenerManager::IsRegisteredHere(const nsAString & type, PRBool *_retval)
1358 return NS_ERROR_NOT_IMPLEMENTED;
1361 nsresult
1362 nsEventListenerManager::FixContextMenuEvent(nsPresContext* aPresContext,
1363 nsISupports* aCurrentTarget,
1364 nsEvent* aEvent,
1365 nsIDOMEvent** aDOMEvent)
1367 nsIPresShell* shell = aPresContext ? aPresContext->GetPresShell() : nsnull;
1368 if (!shell) {
1369 // Nothing to do.
1370 return NS_OK;
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) {
1394 nsPoint caretPoint;
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;
1402 return NS_OK;
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();
1414 if (doc) {
1415 nsPIDOMWindow* privWindow = doc->GetWindow();
1416 if (privWindow) {
1417 nsIFocusController *focusController =
1418 privWindow->GetRootFocusController();
1419 if (focusController)
1420 focusController->GetFocusedElement(getter_AddRefs(currentFocus));
1425 if (currentFocus) {
1426 // Reset event coordinates relative to focused frame in view
1427 nsPoint targetPt;
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);
1437 return ret;
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.
1452 PRBool
1453 nsEventListenerManager::PrepareToUseCaretPosition(nsIWidget* aEventWidget,
1454 nsIPresShell* aShell,
1455 nsPoint& aTargetPt)
1457 nsresult rv;
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)
1468 return PR_FALSE;
1470 // caret selection, this is a temporary weak reference, so no refcounting is
1471 // needed
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));
1484 if (content) {
1485 nsIContent* nonNative = content->FindFirstNonNativeAnonymous();
1486 content = nonNative;
1489 if (content) {
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;
1517 if (frame)
1518 frame->GetSelectionController(aShell->GetPresContext(),
1519 getter_AddRefs(selCon));
1520 else
1521 selCon = do_QueryInterface(aShell);
1522 if (selCon) {
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)
1532 PRBool isCollapsed;
1533 nsIView* view;
1534 nsRect caretCoords;
1535 rv = caret->GetCaretCoordinates(nsCaret::eRenderingViewCoordinates,
1536 domSelection, &caretCoords, &isCollapsed,
1537 &view);
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);
1553 return PR_TRUE;
1556 // Get coordinates in device pixels relative to root view for element,
1557 // first ensuring the element is onscreen
1558 void
1559 nsEventListenerManager::GetCoordinatesFor(nsIDOMElement *aCurrentEl,
1560 nsPresContext *aPresContext,
1561 nsIPresShell *aPresShell,
1562 nsPoint& aTargetPt)
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;
1572 #ifdef MOZ_XUL
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
1576 // as is.
1577 nsCOMPtr<nsIDOMXULSelectControlItemElement> item;
1578 nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
1579 do_QueryInterface(aCurrentEl);
1580 if (multiSelect) {
1581 checkLineHeight = PR_FALSE;
1583 PRInt32 currentIndex;
1584 multiSelect->GetCurrentIndex(&currentIndex);
1585 if (currentIndex >= 0) {
1586 nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl));
1587 if (xulElement) {
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?
1596 if (treeBox) {
1597 treeBox->EnsureRowIsVisible(currentIndex);
1598 PRInt32 firstVisibleRow, rowHeight;
1599 treeBox->GetFirstVisibleRow(&firstVisibleRow);
1600 treeBox->GetRowHeight(&rowHeight);
1602 extraPixelsY = (currentIndex - firstVisibleRow + 1) * rowHeight;
1603 istree = PR_TRUE;
1605 nsCOMPtr<nsITreeColumns> cols;
1606 treeBox->GetColumns(getter_AddRefs(cols));
1607 if (cols) {
1608 nsCOMPtr<nsITreeColumn> col;
1609 cols->GetFirstColumn(getter_AddRefs(col));
1610 if (col) {
1611 nsCOMPtr<nsIDOMElement> colElement;
1612 col->GetElement(getter_AddRefs(colElement));
1613 nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement));
1614 if (colContent) {
1615 nsIFrame* frame = aPresShell->GetPrimaryFrameFor(colContent);
1616 if (frame) {
1617 extraTreeY = frame->GetSize().height;
1623 else {
1624 multiSelect->GetCurrentItem(getter_AddRefs(item));
1629 else {
1630 // don't check menulists as the selected item will be inside a popup.
1631 nsCOMPtr<nsIDOMXULMenuListElement> menulist = do_QueryInterface(aCurrentEl);
1632 if (!menulist) {
1633 checkLineHeight = PR_FALSE;
1634 nsCOMPtr<nsIDOMXULSelectControlElement> select =
1635 do_QueryInterface(aCurrentEl);
1636 if (select)
1637 select->GetSelectedItem(getter_AddRefs(item));
1641 if (item)
1642 focusedContent = do_QueryInterface(item);
1643 #endif
1645 nsIFrame *frame = aPresShell->GetPrimaryFrameFor(focusedContent);
1646 if (frame) {
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.
1669 nscoord extra = 0;
1670 if (!istree) {
1671 extra = frame->GetSize().height;
1672 if (checkLineHeight) {
1673 nsIScrollableView *scrollView =
1674 nsLayoutUtils::GetNearestScrollingView(view, nsLayoutUtils::eEither);
1675 if (scrollView) {
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;
1691 NS_IMETHODIMP
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;
1702 break;
1707 return NS_OK;
1710 PRUint32
1711 nsEventListenerManager::MutationListenerBits()
1713 PRUint32 bits = 0;
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);
1727 return bits;
1730 PRBool
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) {
1744 goto found;
1747 typeData = nsnull;
1748 dispData = nsnull;
1751 found:
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)) {
1758 return PR_TRUE;
1761 return PR_FALSE;
1764 PRBool
1765 nsEventListenerManager::HasListeners()
1767 return !mListeners.IsEmpty();
1770 PRBool
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)))) {
1780 return PR_TRUE;
1783 return PR_FALSE;
1786 nsresult
1787 NS_NewEventListenerManager(nsIEventListenerManager** aInstancePtrResult)
1789 nsIEventListenerManager* l = new nsEventListenerManager();
1791 if (!l) {
1792 return NS_ERROR_OUT_OF_MEMORY;
1795 return CallQueryInterface(l, aInstancePtrResult);