RetroZilla/content/events/src/nsEventListenerManager.cpp

2549 lines
87 KiB
C++

/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
/* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
*
* The contents of this file are subject to the Mozilla Public License Version
* 1.1 (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
* http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
* WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
* for the specific language governing rights and limitations under the
* License.
*
* The Original Code is mozilla.org code.
*
* The Initial Developer of the Original Code is
* Netscape Communications Corporation.
* Portions created by the Initial Developer are Copyright (C) 1998
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
*
* Alternatively, the contents of this file may be used under the terms of
* either of the GNU General Public License Version 2 or later (the "GPL"),
* or the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
* in which case the provisions of the GPL or the LGPL are applicable instead
* of those above. If you wish to allow use of your version of this file only
* under the terms of either the GPL or the LGPL, and not to allow others to
* use your version of this file under the terms of the MPL, indicate your
* decision by deleting the provisions above and replace them with the notice
* and other provisions required by the GPL or the LGPL. If you do not delete
* the provisions above, a recipient may use your version of this file under
* the terms of any one of the MPL, the GPL or the LGPL.
*
* ***** END LICENSE BLOCK ***** */
#include "nsISupports.h"
#include "nsGUIEvent.h"
#include "nsDOMEvent.h"
#include "nsEventListenerManager.h"
#include "nsICaret.h"
#include "nsIFrameSelection.h"
#include "nsIDOMNSEvent.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMMouseListener.h"
#include "nsIDOMMouseMotionListener.h"
#include "nsIDOMContextMenuListener.h"
#include "nsIDOMKeyListener.h"
#include "nsIDOMFocusListener.h"
#include "nsIDOMFormListener.h"
#include "nsIDOMLoadListener.h"
#include "nsIDOMDragListener.h"
#include "nsIDOMPaintListener.h"
#include "nsIDOMTextListener.h"
#include "nsIDOMCompositionListener.h"
#include "nsIDOMXULListener.h"
#include "nsIDOMScrollListener.h"
#include "nsIDOMMutationListener.h"
#include "nsIDOMUIListener.h"
#include "nsIDOMPageTransitionListener.h"
#include "nsITextControlFrame.h"
#ifdef MOZ_SVG
#include "nsIDOMSVGListener.h"
#include "nsIDOMSVGZoomListener.h"
#include "nsSVGAtoms.h"
#endif // MOZ_SVG
#include "nsIEventStateManager.h"
#include "nsPIDOMWindow.h"
#include "nsIPrivateDOMEvent.h"
#include "nsIJSEventListener.h"
#include "prmem.h"
#include "nsIScriptGlobalObject.h"
#include "nsLayoutAtoms.h"
#include "nsLayoutUtils.h"
#ifdef MOZ_XUL
// XXXbz the fact that this is ifdef MOZ_XUL is good indication that
// it doesn't belong here...
#include "nsITreeBoxObject.h"
#include "nsITreeColumns.h"
#include "nsIDOMXULMultSelectCntrlEl.h"
#endif
#include "nsINameSpaceManager.h"
#include "nsIContent.h"
#include "nsINodeInfo.h"
#include "nsIFrame.h"
#include "nsIView.h"
#include "nsIViewManager.h"
#include "nsIScrollableView.h"
#include "nsCOMPtr.h"
#include "nsIServiceManager.h"
#include "nsIScriptSecurityManager.h"
#include "nsDOMError.h"
#include "nsIJSContextStack.h"
#include "nsIDocument.h"
#include "nsIPresShell.h"
#include "nsMutationEvent.h"
#include "nsIXPConnect.h"
#include "nsDOMCID.h"
#include "nsIScriptObjectOwner.h" // for nsIScriptEventHandlerOwner
#include "nsIClassInfo.h"
#include "nsIFocusController.h"
#include "nsIDOMElement.h"
#include "nsIBoxObject.h"
#include "nsIDOMNSDocument.h"
#include "nsIWidget.h"
#include "nsContentUtils.h"
#include "nsJSUtils.h"
#include "nsIDOMEventGroup.h"
#include "nsContentCID.h"
static NS_DEFINE_CID(kDOMScriptObjectFactoryCID,
NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
static NS_DEFINE_CID(kDOMEventGroupCID, NS_DOMEVENTGROUP_CID);
typedef
NS_STDCALL_FUNCPROTO(nsresult,
GenericHandler,
nsIDOMEventListener, HandleEvent,
(nsIDOMEvent*));
/*
* Things here are not as they appear. Namely, |ifaceListener| below is
* not really a pointer to the nsIDOMEventListener interface, and aMethod is
* not really a pointer-to-member for nsIDOMEventListener. They both
* actually refer to the event-type-specific listener interface. The casting
* magic allows us to use a single dispatch method. This relies on the
* assumption that nsIDOMEventListener and the event type listener interfaces
* have the same object layout and will therefore have compatible
* pointer-to-member implementations.
*/
static nsresult DispatchToInterface(nsIDOMEvent* aEvent,
nsIDOMEventListener* aListener,
GenericHandler aMethod,
const nsIID& aIID,
PRBool* aHasInterface)
{
nsIDOMEventListener* ifaceListener = nsnull;
nsresult rv = NS_OK;
aListener->QueryInterface(aIID, (void**) &ifaceListener);
if (ifaceListener) {
*aHasInterface = PR_TRUE;
rv = (ifaceListener->*aMethod)(aEvent);
NS_RELEASE(ifaceListener);
}
return rv;
}
struct EventDispatchData
{
PRUint32 message;
GenericHandler method;
PRUint8 bits;
};
struct EventTypeData
{
const EventDispatchData* events;
int numEvents;
const nsIID* iid;
};
#define HANDLER(x) NS_REINTERPRET_CAST(GenericHandler, x)
static const EventDispatchData sMouseEvents[] = {
{ NS_MOUSE_LEFT_BUTTON_DOWN, HANDLER(&nsIDOMMouseListener::MouseDown),
NS_EVENT_BITS_MOUSE_MOUSEDOWN },
{ NS_MOUSE_MIDDLE_BUTTON_DOWN, HANDLER(&nsIDOMMouseListener::MouseDown),
NS_EVENT_BITS_MOUSE_MOUSEDOWN },
{ NS_MOUSE_RIGHT_BUTTON_DOWN, HANDLER(&nsIDOMMouseListener::MouseDown),
NS_EVENT_BITS_MOUSE_MOUSEDOWN },
{ NS_MOUSE_LEFT_BUTTON_UP, HANDLER(&nsIDOMMouseListener::MouseUp),
NS_EVENT_BITS_MOUSE_MOUSEUP },
{ NS_MOUSE_MIDDLE_BUTTON_UP, HANDLER(&nsIDOMMouseListener::MouseUp),
NS_EVENT_BITS_MOUSE_MOUSEUP },
{ NS_MOUSE_RIGHT_BUTTON_UP, HANDLER(&nsIDOMMouseListener::MouseUp),
NS_EVENT_BITS_MOUSE_MOUSEUP },
{ NS_MOUSE_LEFT_CLICK, HANDLER(&nsIDOMMouseListener::MouseClick),
NS_EVENT_BITS_MOUSE_CLICK },
{ NS_MOUSE_MIDDLE_CLICK, HANDLER(&nsIDOMMouseListener::MouseClick),
NS_EVENT_BITS_MOUSE_CLICK },
{ NS_MOUSE_RIGHT_CLICK, HANDLER(&nsIDOMMouseListener::MouseClick),
NS_EVENT_BITS_MOUSE_CLICK },
{ NS_MOUSE_LEFT_DOUBLECLICK, HANDLER(&nsIDOMMouseListener::MouseDblClick),
NS_EVENT_BITS_MOUSE_DBLCLICK },
{ NS_MOUSE_MIDDLE_DOUBLECLICK, HANDLER(&nsIDOMMouseListener::MouseDblClick),
NS_EVENT_BITS_MOUSE_DBLCLICK },
{ NS_MOUSE_RIGHT_DOUBLECLICK, HANDLER(&nsIDOMMouseListener::MouseDblClick),
NS_EVENT_BITS_MOUSE_DBLCLICK },
{ NS_MOUSE_ENTER_SYNTH, HANDLER(&nsIDOMMouseListener::MouseOver),
NS_EVENT_BITS_MOUSE_MOUSEOVER },
{ NS_MOUSE_EXIT_SYNTH, HANDLER(&nsIDOMMouseListener::MouseOut),
NS_EVENT_BITS_MOUSE_MOUSEOUT }
};
static const EventDispatchData sMouseMotionEvents[] = {
{ NS_MOUSE_MOVE, HANDLER(&nsIDOMMouseMotionListener::MouseMove),
NS_EVENT_BITS_MOUSEMOTION_MOUSEMOVE }
};
static const EventDispatchData sContextMenuEvents[] = {
{ NS_CONTEXTMENU, HANDLER(&nsIDOMContextMenuListener::ContextMenu),
NS_EVENT_BITS_CONTEXT_MENU },
{ NS_CONTEXTMENU_KEY, HANDLER(&nsIDOMContextMenuListener::ContextMenu),
NS_EVENT_BITS_CONTEXT_MENU }
};
static const EventDispatchData sCompositionEvents[] = {
{ NS_COMPOSITION_START, HANDLER(&nsIDOMCompositionListener::HandleStartComposition),
NS_EVENT_BITS_COMPOSITION_START },
{ NS_COMPOSITION_END, HANDLER(&nsIDOMCompositionListener::HandleEndComposition),
NS_EVENT_BITS_COMPOSITION_END },
{ NS_COMPOSITION_QUERY, HANDLER(&nsIDOMCompositionListener::HandleQueryComposition),
NS_EVENT_BITS_COMPOSITION_QUERY },
{ NS_RECONVERSION_QUERY, HANDLER(&nsIDOMCompositionListener::HandleQueryReconversion),
NS_EVENT_BITS_COMPOSITION_RECONVERSION },
{ NS_QUERYCARETRECT, HANDLER(&nsIDOMCompositionListener::HandleQueryCaretRect),
NS_EVENT_BITS_COMPOSITION_QUERYCARETRECT }
};
static const EventDispatchData sTextEvents[] = {
{NS_TEXT_TEXT,HANDLER(&nsIDOMTextListener::HandleText),NS_EVENT_BITS_TEXT_TEXT},
};
static const EventDispatchData sKeyEvents[] = {
{NS_KEY_UP, HANDLER(&nsIDOMKeyListener::KeyUp), NS_EVENT_BITS_KEY_KEYUP},
{NS_KEY_DOWN, HANDLER(&nsIDOMKeyListener::KeyDown), NS_EVENT_BITS_KEY_KEYDOWN},
{NS_KEY_PRESS,HANDLER(&nsIDOMKeyListener::KeyPress),NS_EVENT_BITS_KEY_KEYPRESS},
};
static const EventDispatchData sFocusEvents[] = {
{NS_FOCUS_CONTENT,HANDLER(&nsIDOMFocusListener::Focus),NS_EVENT_BITS_FOCUS_FOCUS},
{NS_BLUR_CONTENT, HANDLER(&nsIDOMFocusListener::Blur), NS_EVENT_BITS_FOCUS_BLUR }
};
static const EventDispatchData sFormEvents[] = {
{NS_FORM_SUBMIT, HANDLER(&nsIDOMFormListener::Submit),NS_EVENT_BITS_FORM_SUBMIT},
{NS_FORM_RESET, HANDLER(&nsIDOMFormListener::Reset), NS_EVENT_BITS_FORM_RESET},
{NS_FORM_CHANGE, HANDLER(&nsIDOMFormListener::Change),NS_EVENT_BITS_FORM_CHANGE},
{NS_FORM_SELECTED,HANDLER(&nsIDOMFormListener::Select),NS_EVENT_BITS_FORM_SELECT},
{NS_FORM_INPUT, HANDLER(&nsIDOMFormListener::Input), NS_EVENT_BITS_FORM_INPUT}
};
static const EventDispatchData sLoadEvents[] = {
{NS_PAGE_LOAD, HANDLER(&nsIDOMLoadListener::Load), NS_EVENT_BITS_LOAD_LOAD},
{NS_IMAGE_LOAD, HANDLER(&nsIDOMLoadListener::Load), NS_EVENT_BITS_LOAD_LOAD},
{NS_SCRIPT_LOAD, HANDLER(&nsIDOMLoadListener::Load), NS_EVENT_BITS_LOAD_LOAD},
{NS_PAGE_UNLOAD, HANDLER(&nsIDOMLoadListener::Unload),NS_EVENT_BITS_LOAD_UNLOAD},
{NS_IMAGE_ERROR, HANDLER(&nsIDOMLoadListener::Error), NS_EVENT_BITS_LOAD_ERROR},
{NS_SCRIPT_ERROR,HANDLER(&nsIDOMLoadListener::Error), NS_EVENT_BITS_LOAD_ERROR},
{NS_BEFORE_PAGE_UNLOAD,HANDLER(&nsIDOMLoadListener::BeforeUnload), NS_EVENT_BITS_LOAD_BEFORE_UNLOAD},
};
static const EventDispatchData sPaintEvents[] = {
{NS_PAINT, HANDLER(&nsIDOMPaintListener::Paint), NS_EVENT_BITS_PAINT_PAINT},
{NS_RESIZE_EVENT,HANDLER(&nsIDOMPaintListener::Resize),NS_EVENT_BITS_PAINT_RESIZE},
{NS_SCROLL_EVENT,HANDLER(&nsIDOMPaintListener::Scroll),NS_EVENT_BITS_PAINT_SCROLL}
};
static const EventDispatchData sDragEvents[] = {
{NS_DRAGDROP_ENTER, HANDLER(&nsIDOMDragListener::DragEnter), NS_EVENT_BITS_DRAG_ENTER},
{NS_DRAGDROP_OVER_SYNTH,HANDLER(&nsIDOMDragListener::DragOver), NS_EVENT_BITS_DRAG_OVER},
{NS_DRAGDROP_EXIT_SYNTH,HANDLER(&nsIDOMDragListener::DragExit), NS_EVENT_BITS_DRAG_EXIT},
{NS_DRAGDROP_DROP, HANDLER(&nsIDOMDragListener::DragDrop), NS_EVENT_BITS_DRAG_DROP},
{NS_DRAGDROP_GESTURE, HANDLER(&nsIDOMDragListener::DragGesture),NS_EVENT_BITS_DRAG_GESTURE}
};
static const EventDispatchData sScrollEvents[] = {
{ NS_SCROLLPORT_OVERFLOW, HANDLER(&nsIDOMScrollListener::Overflow),
NS_EVENT_BITS_SCROLLPORT_OVERFLOW },
{ NS_SCROLLPORT_UNDERFLOW, HANDLER(&nsIDOMScrollListener::Underflow),
NS_EVENT_BITS_SCROLLPORT_UNDERFLOW },
{ NS_SCROLLPORT_OVERFLOWCHANGED, HANDLER(&nsIDOMScrollListener::OverflowChanged),
NS_EVENT_BITS_SCROLLPORT_OVERFLOWCHANGED }
};
static const EventDispatchData sXULEvents[] = {
{ NS_XUL_POPUP_SHOWING, HANDLER(&nsIDOMXULListener::PopupShowing),
NS_EVENT_BITS_XUL_POPUP_SHOWING },
{ NS_XUL_POPUP_SHOWN, HANDLER(&nsIDOMXULListener::PopupShown),
NS_EVENT_BITS_XUL_POPUP_SHOWN },
{ NS_XUL_POPUP_HIDING, HANDLER(&nsIDOMXULListener::PopupHiding),
NS_EVENT_BITS_XUL_POPUP_HIDING },
{ NS_XUL_POPUP_HIDDEN, HANDLER(&nsIDOMXULListener::PopupHidden),
NS_EVENT_BITS_XUL_POPUP_HIDDEN },
{ NS_XUL_CLOSE, HANDLER(&nsIDOMXULListener::Close),
NS_EVENT_BITS_XUL_CLOSE },
{ NS_XUL_COMMAND, HANDLER(&nsIDOMXULListener::Command),
NS_EVENT_BITS_XUL_COMMAND },
{ NS_XUL_BROADCAST, HANDLER(&nsIDOMXULListener::Broadcast),
NS_EVENT_BITS_XUL_BROADCAST },
{ NS_XUL_COMMAND_UPDATE, HANDLER(&nsIDOMXULListener::CommandUpdate),
NS_EVENT_BITS_XUL_COMMAND_UPDATE }
};
static const EventDispatchData sMutationEvents[] = {
{ NS_MUTATION_SUBTREEMODIFIED,
HANDLER(&nsIDOMMutationListener::SubtreeModified),
NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED },
{ NS_MUTATION_NODEINSERTED,
HANDLER(&nsIDOMMutationListener::NodeInserted),
NS_EVENT_BITS_MUTATION_NODEINSERTED },
{ NS_MUTATION_NODEREMOVED,
HANDLER(&nsIDOMMutationListener::NodeRemoved),
NS_EVENT_BITS_MUTATION_NODEREMOVED },
{ NS_MUTATION_NODEINSERTEDINTODOCUMENT,
HANDLER(&nsIDOMMutationListener::NodeInsertedIntoDocument),
NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT },
{ NS_MUTATION_NODEREMOVEDFROMDOCUMENT,
HANDLER(&nsIDOMMutationListener::NodeRemovedFromDocument),
NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT },
{ NS_MUTATION_ATTRMODIFIED,
HANDLER(&nsIDOMMutationListener::AttrModified),
NS_EVENT_BITS_MUTATION_ATTRMODIFIED },
{ NS_MUTATION_CHARACTERDATAMODIFIED,
HANDLER(&nsIDOMMutationListener::CharacterDataModified),
NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED }
};
static const EventDispatchData sUIEvents[] = {
{ NS_UI_ACTIVATE, HANDLER(&nsIDOMUIListener::Activate),
NS_EVENT_BITS_UI_ACTIVATE },
{ NS_UI_FOCUSIN, HANDLER(&nsIDOMUIListener::FocusIn),
NS_EVENT_BITS_UI_FOCUSIN },
{ NS_UI_FOCUSOUT, HANDLER(&nsIDOMUIListener::FocusOut),
NS_EVENT_BITS_UI_FOCUSOUT }
};
static const EventDispatchData sPageTransitionEvents[] = {
{ NS_PAGE_SHOW, HANDLER(&nsIDOMPageTransitionListener::PageShow),
NS_EVENT_BITS_PAGETRANSITION_SHOW },
{ NS_PAGE_HIDE, HANDLER(&nsIDOMPageTransitionListener::PageHide),
NS_EVENT_BITS_PAGETRANSITION_HIDE }
};
#ifdef MOZ_SVG
static const EventDispatchData sSVGEvents[] = {
{ NS_SVG_LOAD, HANDLER(&nsIDOMSVGListener::Load),
NS_EVENT_BITS_SVG_LOAD },
{ NS_SVG_UNLOAD, HANDLER(&nsIDOMSVGListener::Unload),
NS_EVENT_BITS_SVG_UNLOAD },
{ NS_SVG_ABORT, HANDLER(&nsIDOMSVGListener::Abort),
NS_EVENT_BITS_SVG_ABORT },
{ NS_SVG_ERROR, HANDLER(&nsIDOMSVGListener::Error),
NS_EVENT_BITS_SVG_ERROR },
{ NS_SVG_RESIZE, HANDLER(&nsIDOMSVGListener::Resize),
NS_EVENT_BITS_SVG_RESIZE },
{ NS_SVG_SCROLL, HANDLER(&nsIDOMSVGListener::Scroll),
NS_EVENT_BITS_SVG_SCROLL }
};
static const EventDispatchData sSVGZoomEvents[] = {
{ NS_SVG_ZOOM, HANDLER(&nsIDOMSVGZoomListener::Zoom),
NS_EVENT_BITS_SVGZOOM_ZOOM }
};
#endif // MOZ_SVG
#define IMPL_EVENTTYPEDATA(type) \
{ \
s##type##Events, \
NS_ARRAY_LENGTH(s##type##Events), \
&NS_GET_IID(nsIDOM##type##Listener) \
}
// IMPORTANT: indices match up with eEventArrayType_ enum values
static const EventTypeData sEventTypes[] = {
IMPL_EVENTTYPEDATA(Mouse),
IMPL_EVENTTYPEDATA(MouseMotion),
IMPL_EVENTTYPEDATA(ContextMenu),
IMPL_EVENTTYPEDATA(Key),
IMPL_EVENTTYPEDATA(Load),
IMPL_EVENTTYPEDATA(Focus),
IMPL_EVENTTYPEDATA(Form),
IMPL_EVENTTYPEDATA(Drag),
IMPL_EVENTTYPEDATA(Paint),
IMPL_EVENTTYPEDATA(Text),
IMPL_EVENTTYPEDATA(Composition),
IMPL_EVENTTYPEDATA(XUL),
IMPL_EVENTTYPEDATA(Scroll),
IMPL_EVENTTYPEDATA(Mutation),
IMPL_EVENTTYPEDATA(UI),
IMPL_EVENTTYPEDATA(PageTransition)
#ifdef MOZ_SVG
,
IMPL_EVENTTYPEDATA(SVG),
IMPL_EVENTTYPEDATA(SVGZoom)
#endif // MOZ_SVG
};
// Strong references to event groups
nsIDOMEventGroup* gSystemEventGroup;
nsIDOMEventGroup* gDOM2EventGroup;
PRUint32 nsEventListenerManager::mInstanceCount = 0;
nsEventListenerManager::nsEventListenerManager() :
mManagerType(NS_ELM_NONE),
mListenersRemoved(PR_FALSE),
mSingleListenerType(eEventArrayType_None),
mSingleListener(nsnull),
mMultiListeners(nsnull),
mGenericListeners(nsnull),
mTarget(nsnull)
{
++mInstanceCount;
}
static PRBool PR_CALLBACK
GenericListenersHashEnum(nsHashKey *aKey, void *aData, void* closure)
{
nsVoidArray* listeners = NS_STATIC_CAST(nsVoidArray*, aData);
if (listeners) {
PRInt32 i, count = listeners->Count();
nsListenerStruct *ls;
for (i = count-1; i >= 0; --i) {
ls = (nsListenerStruct*)listeners->ElementAt(i);
if (ls) {
delete ls;
}
}
delete listeners;
}
return PR_TRUE;
}
nsEventListenerManager::~nsEventListenerManager()
{
RemoveAllListeners();
--mInstanceCount;
if(mInstanceCount == 0) {
NS_IF_RELEASE(gSystemEventGroup);
NS_IF_RELEASE(gDOM2EventGroup);
}
}
nsresult
nsEventListenerManager::RemoveAllListeners()
{
mListenersRemoved = PR_TRUE;
ReleaseListeners(&mSingleListener);
if (!mSingleListener) {
mSingleListenerType = eEventArrayType_None;
mManagerType &= ~NS_ELM_SINGLE;
}
if (mMultiListeners) {
// XXX probably should just be i < Count()
for (PRInt32 i=0; i<EVENT_ARRAY_TYPE_LENGTH && i < mMultiListeners->Count(); i++) {
nsVoidArray* listeners;
listeners = NS_STATIC_CAST(nsVoidArray*, mMultiListeners->ElementAt(i));
ReleaseListeners(&listeners);
}
delete mMultiListeners;
mMultiListeners = nsnull;
mManagerType &= ~NS_ELM_MULTI;
}
if (mGenericListeners) {
mGenericListeners->Enumerate(GenericListenersHashEnum, nsnull);
//hash destructor
delete mGenericListeners;
mGenericListeners = nsnull;
mManagerType &= ~NS_ELM_HASH;
}
return NS_OK;
}
void
nsEventListenerManager::Shutdown()
{
sAddListenerID = JSVAL_VOID;
nsDOMEvent::Shutdown();
}
NS_IMPL_ADDREF(nsEventListenerManager)
NS_IMPL_RELEASE(nsEventListenerManager)
NS_INTERFACE_MAP_BEGIN(nsEventListenerManager)
NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIEventListenerManager)
NS_INTERFACE_MAP_ENTRY(nsIEventListenerManager)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventTarget)
NS_INTERFACE_MAP_ENTRY(nsIDOM3EventTarget)
NS_INTERFACE_MAP_ENTRY(nsIDOMEventReceiver)
NS_INTERFACE_MAP_END
nsVoidArray*
nsEventListenerManager::GetListenersByType(EventArrayType aType,
nsHashKey* aKey, PRBool aCreate)
{
NS_ASSERTION(aType >= 0,"Negative EventListenerType?");
//Look for existing listeners
if (aType == eEventArrayType_Hash && aKey && (mManagerType & NS_ELM_HASH)) {
if (mGenericListeners && mGenericListeners->Exists(aKey)) {
nsVoidArray* listeners = NS_STATIC_CAST(nsVoidArray*, mGenericListeners->Get(aKey));
return listeners;
}
}
else if (mManagerType & NS_ELM_SINGLE) {
if (mSingleListenerType == aType) {
return mSingleListener;
}
}
else if (mManagerType & NS_ELM_MULTI) {
if (mMultiListeners) {
PRInt32 index = aType;
if (index < mMultiListeners->Count()) {
nsVoidArray* listeners;
listeners = NS_STATIC_CAST(nsVoidArray*, mMultiListeners->ElementAt(index));
if (listeners) {
return listeners;
}
}
}
}
//If we've gotten here we didn't find anything. See if we should create something.
if (aCreate) {
if (aType == eEventArrayType_Hash && aKey) {
if (!mGenericListeners) {
mGenericListeners = new nsHashtable();
if (!mGenericListeners) {
//out of memory
return nsnull;
}
}
NS_ASSERTION(!(mGenericListeners->Get(aKey)), "Found existing generic listeners, should be none");
nsVoidArray* listeners;
listeners = new nsAutoVoidArray();
if (!listeners) {
//out of memory
return nsnull;
}
mGenericListeners->Put(aKey, listeners);
mManagerType |= NS_ELM_HASH;
return listeners;
}
else {
if (mManagerType & NS_ELM_SINGLE) {
//Change single type into multi, then add new listener with the code for the
//multi type below
NS_ASSERTION(!mMultiListeners, "Found existing multi listener array, should be none");
mMultiListeners = new nsAutoVoidArray();
if (!mMultiListeners) {
//out of memory
return nsnull;
}
//Move single listener to multi array
mMultiListeners->ReplaceElementAt((void*)mSingleListener, mSingleListenerType);
mSingleListener = nsnull;
mManagerType &= ~NS_ELM_SINGLE;
mManagerType |= NS_ELM_MULTI;
// we'll fall through into the multi case
}
if (mManagerType & NS_ELM_MULTI) {
PRInt32 index = aType;
if (index >= 0) {
nsVoidArray* listeners;
NS_ASSERTION(index >= mMultiListeners->Count() || !mMultiListeners->ElementAt(index), "Found existing listeners, should be none");
listeners = new nsAutoVoidArray();
if (!listeners) {
//out of memory
return nsnull;
}
mMultiListeners->ReplaceElementAt((void*)listeners, index);
return listeners;
}
}
else {
//We had no pre-existing type. This is our first non-hash listener.
//Create the single listener type
NS_ASSERTION(!mSingleListener, "Found existing single listener array, should be none");
mSingleListener = new nsAutoVoidArray();
if (!mSingleListener) {
//out of memory
return nsnull;
}
mSingleListenerType = aType;
mManagerType |= NS_ELM_SINGLE;
return mSingleListener;
}
}
}
return nsnull;
}
EventArrayType
nsEventListenerManager::GetTypeForIID(const nsIID& aIID)
{
if (aIID.Equals(NS_GET_IID(nsIDOMMouseListener)))
return eEventArrayType_Mouse;
if (aIID.Equals(NS_GET_IID(nsIDOMMouseMotionListener)))
return eEventArrayType_MouseMotion;
if (aIID.Equals(NS_GET_IID(nsIDOMContextMenuListener)))
return eEventArrayType_ContextMenu;
if (aIID.Equals(NS_GET_IID(nsIDOMKeyListener)))
return eEventArrayType_Key;
if (aIID.Equals(NS_GET_IID(nsIDOMLoadListener)))
return eEventArrayType_Load;
if (aIID.Equals(NS_GET_IID(nsIDOMFocusListener)))
return eEventArrayType_Focus;
if (aIID.Equals(NS_GET_IID(nsIDOMFormListener)))
return eEventArrayType_Form;
if (aIID.Equals(NS_GET_IID(nsIDOMDragListener)))
return eEventArrayType_Drag;
if (aIID.Equals(NS_GET_IID(nsIDOMPaintListener)))
return eEventArrayType_Paint;
if (aIID.Equals(NS_GET_IID(nsIDOMTextListener)))
return eEventArrayType_Text;
if (aIID.Equals(NS_GET_IID(nsIDOMCompositionListener)))
return eEventArrayType_Composition;
if (aIID.Equals(NS_GET_IID(nsIDOMXULListener)))
return eEventArrayType_XUL;
if (aIID.Equals(NS_GET_IID(nsIDOMScrollListener)))
return eEventArrayType_Scroll;
if (aIID.Equals(NS_GET_IID(nsIDOMMutationListener)))
return eEventArrayType_Mutation;
if (aIID.Equals(NS_GET_IID(nsIDOMUIListener)))
return eEventArrayType_DOMUI;
#ifdef MOZ_SVG
if (aIID.Equals(NS_GET_IID(nsIDOMSVGListener)))
return eEventArrayType_SVG;
if (aIID.Equals(NS_GET_IID(nsIDOMSVGZoomListener)))
return eEventArrayType_SVGZoom;
#endif // MOZ_SVG
return eEventArrayType_None;
}
void
nsEventListenerManager::ReleaseListeners(nsVoidArray** aListeners)
{
if (nsnull != *aListeners) {
PRInt32 i, count = (*aListeners)->Count();
nsListenerStruct *ls;
for (i = 0; i < count; i++) {
ls = (nsListenerStruct*)(*aListeners)->ElementAt(i);
if (ls) {
delete ls;
}
}
delete *aListeners;
*aListeners = nsnull;
}
}
/**
* Sets events listeners of all types.
* @param an event listener
*/
nsresult
nsEventListenerManager::AddEventListener(nsIDOMEventListener *aListener,
EventArrayType aType,
PRInt32 aSubType,
nsHashKey* aKey,
PRInt32 aFlags,
nsIDOMEventGroup* aEvtGrp)
{
NS_ENSURE_TRUE(aListener, NS_ERROR_FAILURE);
nsVoidArray* listeners = GetListenersByType(aType, aKey, PR_TRUE);
//We asked the GetListenersByType to create the array if it had to. If it didn't
//then we're out of memory (or a bug was added which passed in an unsupported
//event type)
if (!listeners) {
return NS_ERROR_OUT_OF_MEMORY;
}
// For mutation listeners, we need to update the global bit on the DOM window.
// Otherwise we won't actually fire the mutation event.
if (aType == eEventArrayType_Mutation) {
// Go from our target to the nearest enclosing DOM window.
nsCOMPtr<nsIScriptGlobalObject> global;
nsCOMPtr<nsIDocument> document;
nsCOMPtr<nsIContent> content(do_QueryInterface(mTarget));
if (content)
document = content->GetOwnerDoc();
else document = do_QueryInterface(mTarget);
if (document)
global = document->GetScriptGlobalObject();
else global = do_QueryInterface(mTarget);
if (global) {
nsCOMPtr<nsPIDOMWindow> window(do_QueryInterface(global));
window->SetMutationListeners(aSubType);
}
}
PRBool isSame = PR_FALSE;
PRUint16 group = 0;
nsCOMPtr<nsIDOMEventGroup> sysGroup;
GetSystemEventGroupLM(getter_AddRefs(sysGroup));
if (sysGroup) {
sysGroup->IsSameEventGroup(aEvtGrp, &isSame);
if (isSame) {
group = NS_EVENT_FLAG_SYSTEM_EVENT;
}
}
PRBool found = PR_FALSE;
nsListenerStruct* ls;
for (PRInt32 i=0; i<listeners->Count(); i++) {
ls = (nsListenerStruct*)listeners->ElementAt(i);
nsRefPtr<nsIDOMEventListener> iListener = ls->mListener.Get();
if (iListener == aListener && ls->mFlags == aFlags &&
ls->mGroupFlags == group) {
ls->mSubType |= aSubType;
found = PR_TRUE;
break;
}
}
if (!found) {
ls = new nsListenerStruct;
if (!ls) {
return NS_ERROR_OUT_OF_MEMORY;
}
nsCOMPtr<nsIDOMGCParticipant> participant = do_QueryInterface(mTarget);
NS_ASSERTION(participant, "must implement nsIDOMGCParticipant");
ls->mListener.Set(aListener, participant);
ls->mFlags = aFlags;
ls->mSubType = aSubType;
ls->mSubTypeCapture = NS_EVENT_BITS_NONE;
ls->mHandlerIsString = 0;
ls->mGroupFlags = group;
listeners->AppendElement((void*)ls);
}
return NS_OK;
}
nsresult
nsEventListenerManager::RemoveEventListener(nsIDOMEventListener *aListener,
EventArrayType aType,
PRInt32 aSubType,
nsHashKey* aKey,
PRInt32 aFlags,
nsIDOMEventGroup* aEvtGrp)
{
nsVoidArray* listeners = GetListenersByType(aType, aKey, PR_FALSE);
if (!listeners) {
return NS_OK;
}
nsListenerStruct* ls;
aFlags &= ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
for (PRInt32 i=0; i<listeners->Count(); i++) {
ls = (nsListenerStruct*)listeners->ElementAt(i);
nsRefPtr<nsIDOMEventListener> iListener = ls->mListener.Get();
if (iListener == aListener &&
(ls->mFlags & ~NS_PRIV_EVENT_UNTRUSTED_PERMITTED) == aFlags) {
ls->mSubType &= ~aSubType;
if (ls->mSubType == NS_EVENT_BITS_NONE) {
listeners->RemoveElement((void*)ls);
delete ls;
}
break;
}
}
return NS_OK;
}
nsresult
nsEventListenerManager::AddEventListenerByIID(nsIDOMEventListener *aListener,
const nsIID& aIID,
PRInt32 aFlags)
{
AddEventListener(aListener, GetTypeForIID(aIID), NS_EVENT_BITS_NONE, nsnull,
aFlags, nsnull);
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerManager::RemoveEventListenerByIID(nsIDOMEventListener *aListener,
const nsIID& aIID,
PRInt32 aFlags)
{
RemoveEventListener(aListener, GetTypeForIID(aIID), NS_EVENT_BITS_NONE,
nsnull, aFlags, nsnull);
return NS_OK;
}
nsresult
nsEventListenerManager::GetIdentifiersForType(nsIAtom* aType,
EventArrayType* aArrayType,
PRInt32* aFlags)
{
if (aType == nsLayoutAtoms::onmousedown) {
*aArrayType = eEventArrayType_Mouse;
*aFlags = NS_EVENT_BITS_MOUSE_MOUSEDOWN;
}
else if (aType == nsLayoutAtoms::onmouseup) {
*aArrayType = eEventArrayType_Mouse;
*aFlags = NS_EVENT_BITS_MOUSE_MOUSEUP;
}
else if (aType == nsLayoutAtoms::onclick) {
*aArrayType = eEventArrayType_Mouse;
*aFlags = NS_EVENT_BITS_MOUSE_CLICK;
}
else if (aType == nsLayoutAtoms::ondblclick) {
*aArrayType = eEventArrayType_Mouse;
*aFlags = NS_EVENT_BITS_MOUSE_DBLCLICK;
}
else if (aType == nsLayoutAtoms::onmouseover) {
*aArrayType = eEventArrayType_Mouse;
*aFlags = NS_EVENT_BITS_MOUSE_MOUSEOVER;
}
else if (aType == nsLayoutAtoms::onmouseout) {
*aArrayType = eEventArrayType_Mouse;
*aFlags = NS_EVENT_BITS_MOUSE_MOUSEOUT;
}
else if (aType == nsLayoutAtoms::onkeydown) {
*aArrayType = eEventArrayType_Key;
*aFlags = NS_EVENT_BITS_KEY_KEYDOWN;
}
else if (aType == nsLayoutAtoms::onkeyup) {
*aArrayType = eEventArrayType_Key;
*aFlags = NS_EVENT_BITS_KEY_KEYUP;
}
else if (aType == nsLayoutAtoms::onkeypress) {
*aArrayType = eEventArrayType_Key;
*aFlags = NS_EVENT_BITS_KEY_KEYPRESS;
}
else if (aType == nsLayoutAtoms::onmousemove) {
*aArrayType = eEventArrayType_MouseMotion;
*aFlags = NS_EVENT_BITS_MOUSEMOTION_MOUSEMOVE;
}
else if (aType == nsLayoutAtoms::oncontextmenu) {
*aArrayType = eEventArrayType_ContextMenu;
*aFlags = NS_EVENT_BITS_CONTEXTMENU;
}
else if (aType == nsLayoutAtoms::onfocus) {
*aArrayType = eEventArrayType_Focus;
*aFlags = NS_EVENT_BITS_FOCUS_FOCUS;
}
else if (aType == nsLayoutAtoms::onblur) {
*aArrayType = eEventArrayType_Focus;
*aFlags = NS_EVENT_BITS_FOCUS_BLUR;
}
else if (aType == nsLayoutAtoms::onsubmit) {
*aArrayType = eEventArrayType_Form;
*aFlags = NS_EVENT_BITS_FORM_SUBMIT;
}
else if (aType == nsLayoutAtoms::onreset) {
*aArrayType = eEventArrayType_Form;
*aFlags = NS_EVENT_BITS_FORM_RESET;
}
else if (aType == nsLayoutAtoms::onchange) {
*aArrayType = eEventArrayType_Form;
*aFlags = NS_EVENT_BITS_FORM_CHANGE;
}
else if (aType == nsLayoutAtoms::onselect) {
*aArrayType = eEventArrayType_Form;
*aFlags = NS_EVENT_BITS_FORM_SELECT;
}
else if (aType == nsLayoutAtoms::oninput) {
*aArrayType = eEventArrayType_Form;
*aFlags = NS_EVENT_BITS_FORM_INPUT;
}
else if (aType == nsLayoutAtoms::onload) {
*aArrayType = eEventArrayType_Load;
*aFlags = NS_EVENT_BITS_LOAD_LOAD;
}
else if (aType == nsLayoutAtoms::onbeforeunload) {
*aArrayType = eEventArrayType_Load;
*aFlags = NS_EVENT_BITS_LOAD_BEFORE_UNLOAD;
}
else if (aType == nsLayoutAtoms::onunload) {
*aArrayType = eEventArrayType_Load;
*aFlags = NS_EVENT_BITS_LOAD_UNLOAD;
}
else if (aType == nsLayoutAtoms::onabort) {
*aArrayType = eEventArrayType_Load;
*aFlags = NS_EVENT_BITS_LOAD_ABORT;
}
else if (aType == nsLayoutAtoms::onerror) {
*aArrayType = eEventArrayType_Load;
*aFlags = NS_EVENT_BITS_LOAD_ERROR;
}
else if (aType == nsLayoutAtoms::onpaint) {
*aArrayType = eEventArrayType_Paint;
*aFlags = NS_EVENT_BITS_PAINT_PAINT;
}
else if (aType == nsLayoutAtoms::onresize) {
*aArrayType = eEventArrayType_Paint;
*aFlags = NS_EVENT_BITS_PAINT_RESIZE;
}
else if (aType == nsLayoutAtoms::onscroll) {
*aArrayType = eEventArrayType_Paint;
*aFlags = NS_EVENT_BITS_PAINT_SCROLL;
} // extened this to handle IME related events
else if (aType == nsLayoutAtoms::onpopupshowing) {
*aArrayType = eEventArrayType_XUL;
*aFlags = NS_EVENT_BITS_XUL_POPUP_SHOWING;
}
else if (aType == nsLayoutAtoms::onpopupshown) {
*aArrayType = eEventArrayType_XUL;
*aFlags = NS_EVENT_BITS_XUL_POPUP_SHOWN;
}
else if (aType == nsLayoutAtoms::onpopuphiding) {
*aArrayType = eEventArrayType_XUL;
*aFlags = NS_EVENT_BITS_XUL_POPUP_HIDING;
}
else if (aType == nsLayoutAtoms::onpopuphidden) {
*aArrayType = eEventArrayType_XUL;
*aFlags = NS_EVENT_BITS_XUL_POPUP_HIDDEN;
}
else if (aType == nsLayoutAtoms::onclose) {
*aArrayType = eEventArrayType_XUL;
*aFlags = NS_EVENT_BITS_XUL_CLOSE;
}
else if (aType == nsLayoutAtoms::oncommand) {
*aArrayType = eEventArrayType_XUL;
*aFlags = NS_EVENT_BITS_XUL_COMMAND;
}
else if (aType == nsLayoutAtoms::onbroadcast) {
*aArrayType = eEventArrayType_XUL;
*aFlags = NS_EVENT_BITS_XUL_BROADCAST;
}
else if (aType == nsLayoutAtoms::oncommandupdate) {
*aArrayType = eEventArrayType_XUL;
*aFlags = NS_EVENT_BITS_XUL_COMMAND_UPDATE;
}
else if (aType == nsLayoutAtoms::onoverflow) {
*aArrayType = eEventArrayType_Scroll;
*aFlags = NS_EVENT_BITS_SCROLLPORT_OVERFLOW;
}
else if (aType == nsLayoutAtoms::onunderflow) {
*aArrayType = eEventArrayType_Scroll;
*aFlags = NS_EVENT_BITS_SCROLLPORT_UNDERFLOW;
}
else if (aType == nsLayoutAtoms::onoverflowchanged) {
*aArrayType = eEventArrayType_Scroll;
*aFlags = NS_EVENT_BITS_SCROLLPORT_OVERFLOWCHANGED;
}
else if (aType == nsLayoutAtoms::ondragenter) {
*aArrayType = eEventArrayType_Drag;
*aFlags = NS_EVENT_BITS_DRAG_ENTER;
}
else if (aType == nsLayoutAtoms::ondragover) {
*aArrayType = eEventArrayType_Drag;
*aFlags = NS_EVENT_BITS_DRAG_OVER;
}
else if (aType == nsLayoutAtoms::ondragexit) {
*aArrayType = eEventArrayType_Drag;
*aFlags = NS_EVENT_BITS_DRAG_EXIT;
}
else if (aType == nsLayoutAtoms::ondragdrop) {
*aArrayType = eEventArrayType_Drag;
*aFlags = NS_EVENT_BITS_DRAG_DROP;
}
else if (aType == nsLayoutAtoms::ondraggesture) {
*aArrayType = eEventArrayType_Drag;
*aFlags = NS_EVENT_BITS_DRAG_GESTURE;
}
else if (aType == nsLayoutAtoms::onDOMSubtreeModified) {
*aArrayType = eEventArrayType_Mutation;
*aFlags = NS_EVENT_BITS_MUTATION_SUBTREEMODIFIED;
}
else if (aType == nsLayoutAtoms::onDOMNodeInserted) {
*aArrayType = eEventArrayType_Mutation;
*aFlags = NS_EVENT_BITS_MUTATION_NODEINSERTED;
}
else if (aType == nsLayoutAtoms::onDOMNodeRemoved) {
*aArrayType = eEventArrayType_Mutation;
*aFlags = NS_EVENT_BITS_MUTATION_NODEREMOVED;
}
else if (aType == nsLayoutAtoms::onDOMNodeInsertedIntoDocument) {
*aArrayType = eEventArrayType_Mutation;
*aFlags = NS_EVENT_BITS_MUTATION_NODEINSERTEDINTODOCUMENT;
}
else if (aType == nsLayoutAtoms::onDOMNodeRemovedFromDocument) {
*aArrayType = eEventArrayType_Mutation;
*aFlags = NS_EVENT_BITS_MUTATION_NODEREMOVEDFROMDOCUMENT;
}
else if (aType == nsLayoutAtoms::onDOMAttrModified) {
*aArrayType = eEventArrayType_Mutation;
*aFlags = NS_EVENT_BITS_MUTATION_ATTRMODIFIED;
}
else if (aType == nsLayoutAtoms::onDOMCharacterDataModified) {
*aArrayType = eEventArrayType_Mutation;
*aFlags = NS_EVENT_BITS_MUTATION_CHARACTERDATAMODIFIED;
}
else if (aType == nsLayoutAtoms::onDOMActivate) {
*aArrayType = eEventArrayType_DOMUI;
*aFlags = NS_EVENT_BITS_UI_ACTIVATE;
}
else if (aType == nsLayoutAtoms::onDOMFocusIn) {
*aArrayType = eEventArrayType_DOMUI;
*aFlags = NS_EVENT_BITS_UI_FOCUSIN;
}
else if (aType == nsLayoutAtoms::onDOMFocusOut) {
*aArrayType = eEventArrayType_DOMUI;
*aFlags = NS_EVENT_BITS_UI_FOCUSOUT;
}
else if (aType == nsLayoutAtoms::oncompositionstart) {
*aArrayType = eEventArrayType_Composition;
*aFlags = NS_EVENT_BITS_COMPOSITION_START;
}
else if (aType == nsLayoutAtoms::oncompositionend) {
*aArrayType = eEventArrayType_Composition;
*aFlags = NS_EVENT_BITS_COMPOSITION_END;
}
else if (aType == nsLayoutAtoms::ontext) {
*aArrayType = eEventArrayType_Text;
*aFlags = NS_EVENT_BITS_TEXT_TEXT;
}
else if (aType == nsLayoutAtoms::onpageshow) {
*aArrayType = eEventArrayType_PageTransition;
*aFlags = NS_EVENT_BITS_PAGETRANSITION_SHOW;
}
else if (aType == nsLayoutAtoms::onpagehide) {
*aArrayType = eEventArrayType_PageTransition;
*aFlags = NS_EVENT_BITS_PAGETRANSITION_HIDE;
}
#ifdef MOZ_SVG
else if (aType == nsLayoutAtoms::onSVGLoad) {
*aArrayType = eEventArrayType_SVG;
*aFlags = NS_EVENT_BITS_SVG_LOAD;
}
else if (aType == nsLayoutAtoms::onSVGUnload) {
*aArrayType = eEventArrayType_SVG;
*aFlags = NS_EVENT_BITS_SVG_UNLOAD;
}
else if (aType == nsLayoutAtoms::onSVGAbort) {
*aArrayType = eEventArrayType_SVG;
*aFlags = NS_EVENT_BITS_SVG_ABORT;
}
else if (aType == nsLayoutAtoms::onSVGError) {
*aArrayType = eEventArrayType_SVG;
*aFlags = NS_EVENT_BITS_SVG_ERROR;
}
else if (aType == nsLayoutAtoms::onSVGResize) {
*aArrayType = eEventArrayType_SVG;
*aFlags = NS_EVENT_BITS_SVG_RESIZE;
}
else if (aType == nsLayoutAtoms::onSVGScroll) {
*aArrayType = eEventArrayType_SVG;
*aFlags = NS_EVENT_BITS_SVG_SCROLL;
}
else if (aType == nsLayoutAtoms::onSVGZoom) {
*aArrayType = eEventArrayType_SVGZoom;
*aFlags = NS_EVENT_BITS_SVGZOOM_ZOOM;
}
#endif // MOZ_SVG
else {
return NS_ERROR_FAILURE;
}
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerManager::AddEventListenerByType(nsIDOMEventListener *aListener,
const nsAString& aType,
PRInt32 aFlags,
nsIDOMEventGroup* aEvtGrp)
{
PRInt32 subType;
EventArrayType arrayType;
nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aType);
if (NS_OK == GetIdentifiersForType(atom, &arrayType, &subType)) {
AddEventListener(aListener, arrayType, subType, nsnull, aFlags, aEvtGrp);
}
else {
const nsPromiseFlatString& flatString = PromiseFlatString(aType);
nsStringKey key(flatString);
AddEventListener(aListener, eEventArrayType_Hash, NS_EVENT_BITS_NONE, &key, aFlags, aEvtGrp);
}
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerManager::RemoveEventListenerByType(nsIDOMEventListener *aListener,
const nsAString& aType,
PRInt32 aFlags,
nsIDOMEventGroup* aEvtGrp)
{
PRInt32 subType;
EventArrayType arrayType;
nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + aType);
if (NS_OK == GetIdentifiersForType(atom, &arrayType, &subType)) {
RemoveEventListener(aListener, arrayType, subType, nsnull, aFlags, aEvtGrp);
}
else {
const nsPromiseFlatString& flatString = PromiseFlatString(aType);
nsStringKey key(flatString);
RemoveEventListener(aListener, eEventArrayType_Hash, NS_EVENT_BITS_NONE, &key, aFlags, aEvtGrp);
}
return NS_OK;
}
nsListenerStruct*
nsEventListenerManager::FindJSEventListener(EventArrayType aType)
{
nsVoidArray *listeners = GetListenersByType(aType, nsnull, PR_FALSE);
if (listeners) {
// Run through the listeners for this type and see if a script
// listener is registered
nsListenerStruct *ls;
for (PRInt32 i=0; i < listeners->Count(); i++) {
ls = (nsListenerStruct*)listeners->ElementAt(i);
if (ls->mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) {
return ls;
}
}
}
return nsnull;
}
nsresult
nsEventListenerManager::SetJSEventListener(nsIScriptContext *aContext,
JSObject *aScopeObject,
nsISupports *aObject,
nsIAtom* aName,
PRBool aIsString,
PRBool aPermitUntrustedEvents)
{
nsresult rv = NS_OK;
nsListenerStruct *ls;
PRInt32 flags;
EventArrayType arrayType;
NS_ENSURE_SUCCESS(GetIdentifiersForType(aName, &arrayType, &flags),
NS_ERROR_FAILURE);
ls = FindJSEventListener(arrayType);
if (nsnull == ls) {
// If we didn't find a script listener or no listeners existed
// create and add a new one.
nsCOMPtr<nsIDOMEventListener> scriptListener;
rv = NS_NewJSEventListener(aContext, aScopeObject, aObject,
getter_AddRefs(scriptListener));
if (NS_SUCCEEDED(rv)) {
AddEventListener(scriptListener, arrayType, NS_EVENT_BITS_NONE, nsnull,
NS_EVENT_FLAG_BUBBLE | NS_PRIV_EVENT_FLAG_SCRIPT, nsnull);
ls = FindJSEventListener(arrayType);
}
}
if (NS_SUCCEEDED(rv) && ls) {
// Set flag to indicate possible need for compilation later
if (aIsString) {
ls->mHandlerIsString |= flags;
}
else {
ls->mHandlerIsString &= ~flags;
}
// Set subtype flags based on event
ls->mSubType |= flags;
if (aPermitUntrustedEvents) {
ls->mFlags |= NS_PRIV_EVENT_UNTRUSTED_PERMITTED;
}
}
return rv;
}
NS_IMETHODIMP
nsEventListenerManager::AddScriptEventListener(nsISupports *aObject,
nsIAtom *aName,
const nsAString& aBody,
PRBool aDeferCompilation,
PRBool aPermitUntrustedEvents)
{
nsIScriptContext *context = nsnull;
JSContext* cx = nsnull;
nsCOMPtr<nsIContent> content(do_QueryInterface(aObject));
nsCOMPtr<nsIDocument> doc;
nsISupports *objiSupp = aObject;
JSObject *scope = nsnull;
if (content) {
// Try to get context from doc
doc = content->GetOwnerDoc();
nsIScriptGlobalObject *global;
if (doc && (global = doc->GetScriptGlobalObject())) {
context = global->GetContext();
scope = global->GetGlobalJSObject();
}
} else {
nsCOMPtr<nsPIDOMWindow> win(do_QueryInterface(aObject));
nsCOMPtr<nsIScriptGlobalObject> global;
if (win) {
NS_ASSERTION(win->IsInnerWindow(),
"Event listener added to outer window!");
nsCOMPtr<nsIDOMDocument> domdoc;
win->GetDocument(getter_AddRefs(domdoc));
doc = do_QueryInterface(domdoc);
global = do_QueryInterface(win);
} else {
doc = do_QueryInterface(aObject);
if (doc) {
global = doc->GetScriptGlobalObject();
} else {
global = do_QueryInterface(aObject);
}
}
if (global) {
context = global->GetContext();
scope = global->GetGlobalJSObject();
}
}
if (!context) {
// Get JSContext from stack, or use the safe context (and hidden
// window global) if no JS is running.
nsCOMPtr<nsIThreadJSContextStack> stack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1");
NS_ENSURE_TRUE(stack, NS_ERROR_FAILURE);
NS_ENSURE_SUCCESS(stack->Peek(&cx), NS_ERROR_FAILURE);
if (!cx) {
stack->GetSafeJSContext(&cx);
NS_ENSURE_TRUE(cx, NS_ERROR_FAILURE);
}
context = nsJSUtils::GetDynamicScriptContext(cx);
NS_ENSURE_TRUE(context, NS_ERROR_FAILURE);
scope = ::JS_GetGlobalObject(cx);
} else if (!scope) {
NS_ERROR("Context reachable, but no scope reachable in "
"AddScriptEventListener()!");
return NS_ERROR_NOT_AVAILABLE;
}
nsresult rv;
if (!aDeferCompilation) {
JSContext *cx = (JSContext *)context->GetNativeContext();
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = nsContentUtils::XPConnect()->WrapNative(cx, scope, aObject,
NS_GET_IID(nsISupports),
getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
// Since JSEventListeners only have a raw nsISupports pointer, it's
// important that it point to the same object that the WrappedNative wraps.
// (In the case of a tearoff, the tearoff will not persist).
nsCOMPtr<nsIXPConnectWrappedNative> wrapper = do_QueryInterface(holder);
NS_ASSERTION(wrapper, "wrapper must impl nsIXPConnectWrappedNative");
objiSupp = wrapper->Native();
JSObject *scriptObject = nsnull;
rv = holder->GetJSObject(&scriptObject);
NS_ENSURE_SUCCESS(rv, rv);
nsCOMPtr<nsIScriptEventHandlerOwner> handlerOwner =
do_QueryInterface(aObject);
void *handler = nsnull;
PRBool done = PR_FALSE;
if (handlerOwner) {
rv = handlerOwner->GetCompiledEventHandler(aName, &handler);
if (NS_SUCCEEDED(rv) && handler) {
rv = context->BindCompiledEventHandler(scriptObject, aName, handler);
if (NS_FAILED(rv))
return rv;
done = PR_TRUE;
}
}
if (!done) {
PRUint32 lineNo = 0;
nsCAutoString url (NS_LITERAL_CSTRING("-moz-evil:lying-event-listener"));
if (doc) {
nsIURI *uri = doc->GetDocumentURI();
if (uri) {
uri->GetSpec(url);
lineNo = 1;
}
}
if (handlerOwner) {
// Always let the handler owner compile the event handler, as
// it may want to use a special context or scope object.
rv = handlerOwner->CompileEventHandler(context, scriptObject, aName,
aBody, url.get(), lineNo, &handler);
}
else {
PRInt32 nameSpace = kNameSpaceID_Unknown;
if (content)
nameSpace = content->GetNameSpaceID();
else if (doc) {
nsCOMPtr<nsIContent> root = doc->GetRootContent();
if (root)
nameSpace = root->GetNameSpaceID();
}
const char *eventName = nsContentUtils::GetEventArgName(nameSpace);
rv = context->CompileEventHandler(scriptObject, aName, eventName,
aBody,
url.get(), lineNo,
(handlerOwner != nsnull),
&handler);
}
if (NS_FAILED(rv)) return rv;
}
}
return SetJSEventListener(context, scope, objiSupp, aName, aDeferCompilation,
aPermitUntrustedEvents);
}
nsresult
nsEventListenerManager::RemoveScriptEventListener(nsIAtom *aName)
{
nsresult result = NS_OK;
nsListenerStruct *ls;
PRInt32 flags;
EventArrayType arrayType;
NS_ENSURE_SUCCESS(GetIdentifiersForType(aName, &arrayType, &flags),
NS_ERROR_FAILURE);
ls = FindJSEventListener(arrayType);
if (ls) {
ls->mSubType &= ~flags;
if (ls->mSubType == NS_EVENT_BITS_NONE) {
//Get the listeners array so we can remove ourselves from it
nsVoidArray* listeners;
listeners = GetListenersByType(arrayType, nsnull, PR_FALSE);
NS_ENSURE_TRUE(listeners, NS_ERROR_FAILURE);
listeners->RemoveElement((void*)ls);
delete ls;
}
}
return result;
}
jsval
nsEventListenerManager::sAddListenerID = JSVAL_VOID;
NS_IMETHODIMP
nsEventListenerManager::RegisterScriptEventListener(nsIScriptContext *aContext,
JSObject *aScopeObject,
nsISupports *aObject,
nsIAtom *aName)
{
// Check that we have access to set an event listener. Prevents
// snooping attacks across domains by setting onkeypress handlers,
// for instance.
// You'd think it'd work just to get the JSContext from aContext,
// but that's actually the JSContext whose private object parents
// the object in aObject.
nsresult rv;
nsCOMPtr<nsIJSContextStack> stack =
do_GetService("@mozilla.org/js/xpc/ContextStack;1", &rv);
if (NS_FAILED(rv))
return rv;
JSContext *cx;
if (NS_FAILED(rv = stack->Peek(&cx)))
return rv;
JSContext *current_cx = (JSContext *)aContext->GetNativeContext();
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
rv = nsContentUtils::XPConnect()->
WrapNative(current_cx, aScopeObject, aObject, NS_GET_IID(nsISupports),
getter_AddRefs(holder));
NS_ENSURE_SUCCESS(rv, rv);
// Since JSEventListeners only have a raw nsISupports pointer, it's
// important that it point to the same object that the WrappedNative wraps.
// (In the case of a tearoff, the tearoff will not persist).
nsCOMPtr<nsIXPConnectWrappedNative> wrapper = do_QueryInterface(holder);
NS_ASSERTION(wrapper, "wrapper must impl nsIXPConnectWrappedNative");
JSObject *jsobj = nsnull;
rv = holder->GetJSObject(&jsobj);
NS_ENSURE_SUCCESS(rv, rv);
if (cx) {
if (sAddListenerID == JSVAL_VOID) {
sAddListenerID =
STRING_TO_JSVAL(::JS_InternString(cx, "addEventListener"));
}
rv = nsContentUtils::GetSecurityManager()->
CheckPropertyAccess(cx, jsobj,
"EventTarget",
sAddListenerID,
nsIXPCSecurityManager::ACCESS_SET_PROPERTY);
if (NS_FAILED(rv)) {
// XXX set pending exception on the native call context?
return rv;
}
}
// Untrusted events are always permitted for non-chrome script
// handlers.
return SetJSEventListener(aContext, aScopeObject, wrapper->Native(), aName,
PR_FALSE, !nsContentUtils::IsCallerChrome());
}
nsresult
nsEventListenerManager::CompileScriptEventListener(nsIScriptContext *aContext,
JSObject *aScopeObject,
nsISupports *aObject,
nsIAtom *aName,
PRBool *aDidCompile)
{
nsresult rv = NS_OK;
nsListenerStruct *ls;
PRInt32 subType;
EventArrayType arrayType;
*aDidCompile = PR_FALSE;
rv = GetIdentifiersForType(aName, &arrayType, &subType);
NS_ENSURE_SUCCESS(rv, rv);
ls = FindJSEventListener(arrayType);
if (!ls) {
//nothing to compile
return NS_OK;
}
if (ls->mHandlerIsString & subType) {
rv = CompileEventHandlerInternal(aContext, aScopeObject, aObject, aName,
ls, /*XXX fixme*/nsnull, subType);
}
// Set *aDidCompile to true even if we didn't really compile
// anything right now, if we get here it means that this event
// handler has been compiled at some point, that's good enough for
// us.
*aDidCompile = PR_TRUE;
return rv;
}
nsresult
nsEventListenerManager::CompileEventHandlerInternal(nsIScriptContext *aContext,
JSObject *aScopeObject,
nsISupports *aObject,
nsIAtom *aName,
nsListenerStruct *aListenerStruct,
nsIDOMEventTarget* aCurrentTarget,
PRUint32 aSubType)
{
nsresult result = NS_OK;
JSContext *cx = (JSContext *)aContext->GetNativeContext();
nsCOMPtr<nsIXPConnectJSObjectHolder> holder;
result = nsContentUtils::XPConnect()->WrapNative(cx, aScopeObject, aObject,
NS_GET_IID(nsISupports),
getter_AddRefs(holder));
NS_ENSURE_SUCCESS(result, result);
JSObject *jsobj = nsnull;
result = holder->GetJSObject(&jsobj);
NS_ENSURE_SUCCESS(result, result);
nsCOMPtr<nsIScriptEventHandlerOwner> handlerOwner =
do_QueryInterface(aObject);
void* handler = nsnull;
if (handlerOwner) {
result = handlerOwner->GetCompiledEventHandler(aName, &handler);
if (NS_SUCCEEDED(result) && handler) {
result = aContext->BindCompiledEventHandler(jsobj, aName, handler);
aListenerStruct->mHandlerIsString &= ~aSubType;
}
}
if (aListenerStruct->mHandlerIsString & aSubType) {
// This should never happen for anything but content
// XXX I don't like that we have to reference content
// from here. The alternative is to store the event handler
// string on the JS object itself.
nsCOMPtr<nsIContent> content = do_QueryInterface(aObject);
NS_ASSERTION(content, "only content should have event handler attributes");
if (content) {
nsAutoString handlerBody;
nsIAtom* attrName = aName;
#ifdef MOZ_SVG
if (aName == nsLayoutAtoms::onSVGLoad)
attrName = nsSVGAtoms::onload;
else if (aName == nsLayoutAtoms::onSVGUnload)
attrName = nsSVGAtoms::onunload;
else if (aName == nsLayoutAtoms::onSVGAbort)
attrName = nsSVGAtoms::onabort;
else if (aName == nsLayoutAtoms::onSVGError)
attrName = nsSVGAtoms::onerror;
else if (aName == nsLayoutAtoms::onSVGResize)
attrName = nsSVGAtoms::onresize;
else if (aName == nsLayoutAtoms::onSVGScroll)
attrName = nsSVGAtoms::onscroll;
else if (aName == nsLayoutAtoms::onSVGZoom)
attrName = nsSVGAtoms::onzoom;
#endif // MOZ_SVG
result = content->GetAttr(kNameSpaceID_None, attrName, handlerBody);
if (NS_SUCCEEDED(result)) {
PRUint32 lineNo = 0;
nsCAutoString url (NS_LITERAL_CSTRING("javascript:alert('TODO: FIXME')"));
nsCOMPtr<nsIDocument> doc = do_QueryInterface(aCurrentTarget);
if (!doc) {
nsCOMPtr<nsIContent> content = do_QueryInterface(aCurrentTarget);
if (content)
doc = content->GetOwnerDoc();
}
if (doc) {
nsIURI *uri = doc->GetDocumentURI();
if (uri) {
uri->GetSpec(url);
lineNo = 1;
}
}
if (handlerOwner) {
// Always let the handler owner compile the event
// handler, as it may want to use a special
// context or scope object.
result = handlerOwner->CompileEventHandler(aContext, jsobj, aName,
handlerBody,
url.get(), lineNo,
&handler);
}
else {
const char *eventName =
nsContentUtils::GetEventArgName(content->GetNameSpaceID());
result = aContext->CompileEventHandler(jsobj, aName, eventName,
handlerBody,
url.get(), lineNo,
(handlerOwner != nsnull),
&handler);
}
if (NS_SUCCEEDED(result)) {
aListenerStruct->mHandlerIsString &= ~aSubType;
}
}
}
}
return result;
}
nsresult
nsEventListenerManager::HandleEventSubType(nsListenerStruct* aListenerStruct,
nsIDOMEventListener* aListener,
nsIDOMEvent* aDOMEvent,
nsIDOMEventTarget* aCurrentTarget,
PRUint32 aSubType,
PRUint32 aPhaseFlags)
{
nsresult result = NS_OK;
// If this is a script handler and we haven't yet
// compiled the event handler itself
if (aListenerStruct->mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) {
// If we're not in the capture phase we must *NOT* have capture flags
// set. Compiled script handlers are one or the other, not both.
if (aPhaseFlags & NS_EVENT_FLAG_BUBBLE && !aPhaseFlags & NS_EVENT_FLAG_INIT) {
if (aListenerStruct->mSubTypeCapture & aSubType) {
return result;
}
}
// If we're in the capture phase we must have capture flags set.
else if (aPhaseFlags & NS_EVENT_FLAG_CAPTURE && !aPhaseFlags & NS_EVENT_FLAG_INIT) {
if (!(aListenerStruct->mSubTypeCapture & aSubType)) {
return result;
}
}
if (aListenerStruct->mHandlerIsString & aSubType) {
nsCOMPtr<nsIJSEventListener> jslistener = do_QueryInterface(aListener);
if (jslistener) {
nsAutoString eventString;
if (NS_SUCCEEDED(aDOMEvent->GetType(eventString))) {
nsCOMPtr<nsIAtom> atom = do_GetAtom(NS_LITERAL_STRING("on") + eventString);
result = CompileEventHandlerInternal(jslistener->GetEventContext(),
jslistener->GetEventScope(),
jslistener->GetEventTarget(),
atom, aListenerStruct,
aCurrentTarget,
aSubType);
}
}
}
}
if (NS_SUCCEEDED(result)) {
nsCOMPtr<nsIPrivateDOMEvent> aPrivDOMEvent(do_QueryInterface(aDOMEvent));
aPrivDOMEvent->SetCurrentTarget(aCurrentTarget);
result = aListener->HandleEvent(aDOMEvent);
aPrivDOMEvent->SetCurrentTarget(nsnull);
}
return result;
}
/**
* Causes a check for event listeners and processing by them if they exist.
* @param an event listener
*/
nsresult
nsEventListenerManager::HandleEvent(nsPresContext* aPresContext,
nsEvent* aEvent, nsIDOMEvent** aDOMEvent,
nsIDOMEventTarget* aCurrentTarget,
PRUint32 aFlags,
nsEventStatus* aEventStatus)
{
NS_ENSURE_ARG_POINTER(aEventStatus);
nsresult ret = NS_OK;
if (aEvent->flags & NS_EVENT_FLAG_STOP_DISPATCH) {
return ret;
}
if (aFlags & NS_EVENT_FLAG_INIT) {
aFlags |= (NS_EVENT_FLAG_BUBBLE | NS_EVENT_FLAG_CAPTURE);
}
//Set the value of the internal PreventDefault flag properly based on aEventStatus
if (*aEventStatus == nsEventStatus_eConsumeNoDefault) {
aEvent->flags |= NS_EVENT_FLAG_NO_DEFAULT;
}
PRUint16 currentGroup = aFlags & NS_EVENT_FLAG_SYSTEM_EVENT;
/* Without this addref, certain events, notably ones bound to
keys which cause window deletion, can destroy this object
before we're ready. */
nsCOMPtr<nsIEventListenerManager> kungFuDeathGrip(this);
nsVoidArray *listeners = nsnull;
if (aEvent->message == NS_CONTEXTMENU || aEvent->message == NS_CONTEXTMENU_KEY) {
ret = FixContextMenuEvent(aPresContext, aCurrentTarget, aEvent, aDOMEvent);
if (NS_FAILED(ret)) {
NS_WARNING("failed to fix context menu event target");
ret = NS_OK;
}
}
const EventTypeData* typeData = nsnull;
const EventDispatchData* dispData = nsnull;
if (aEvent->message == NS_USER_DEFINED_EVENT) {
listeners = GetListenersByType(eEventArrayType_Hash, aEvent->userType, PR_FALSE);
} else {
for (PRInt32 i = 0; i < eEventArrayType_Hash; ++i) {
typeData = &sEventTypes[i];
for (PRInt32 j = 0; j < typeData->numEvents; ++j) {
dispData = &(typeData->events[j]);
if (aEvent->message == dispData->message) {
listeners = GetListenersByType((EventArrayType)i, nsnull, PR_FALSE);
goto found;
}
}
}
}
found:
if (listeners) {
if (!*aDOMEvent) {
ret = CreateEvent(aPresContext, aEvent, EmptyString(), aDOMEvent);
}
if (NS_SUCCEEDED(ret)) {
nsCxPusher pusher;
PRInt32 count = listeners->Count();
nsVoidArray originalListeners(count);
originalListeners = *listeners;
nsAutoPopupStatePusher popupStatePusher(nsDOMEvent::GetEventPopupControlState(aEvent));
for (PRInt32 k = 0; !mListenersRemoved && listeners && k < count; ++k) {
nsListenerStruct* ls = NS_STATIC_CAST(nsListenerStruct*, originalListeners.FastElementAt(k));
// Don't fire the listener if it's been removed
if (listeners->IndexOf(ls) != -1 && ls->mFlags & aFlags &&
ls->mGroupFlags == currentGroup &&
(NS_IS_TRUSTED_EVENT(aEvent) ||
ls->mFlags & NS_PRIV_EVENT_UNTRUSTED_PERMITTED)) {
nsRefPtr<nsIDOMEventListener> eventListener = ls->mListener.Get();
NS_ASSERTION(eventListener, "listener wasn't preserved properly");
if (eventListener) {
// Try the type-specific listener interface
PRBool hasInterface = PR_FALSE;
if (typeData) {
pusher.Pop();
DispatchToInterface(*aDOMEvent, eventListener,
dispData->method, *typeData->iid,
&hasInterface);
}
// If it doesn't implement that, call the generic HandleEvent()
if (!hasInterface &&
(ls->mSubType == NS_EVENT_BITS_NONE || ls->mSubType & dispData->bits) &&
pusher.RePush(aCurrentTarget)) {
HandleEventSubType(ls, eventListener, *aDOMEvent, aCurrentTarget,
dispData ? dispData->bits : NS_EVENT_BITS_NONE,
aFlags);
}
}
}
}
}
}
if (aEvent->flags & NS_EVENT_FLAG_NO_DEFAULT) {
*aEventStatus = nsEventStatus_eConsumeNoDefault;
}
return NS_OK;
}
/**
* Creates a DOM event
*/
NS_IMETHODIMP
nsEventListenerManager::CreateEvent(nsPresContext* aPresContext,
nsEvent* aEvent,
const nsAString& aEventType,
nsIDOMEvent** aDOMEvent)
{
*aDOMEvent = nsnull;
if (aEvent) {
switch(aEvent->eventStructType) {
case NS_MUTATION_EVENT:
return NS_NewDOMMutationEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsMutationEvent*,aEvent));
case NS_GUI_EVENT:
case NS_COMPOSITION_EVENT:
case NS_RECONVERSION_EVENT:
case NS_QUERYCARETRECT_EVENT:
case NS_SCROLLPORT_EVENT:
return NS_NewDOMUIEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsGUIEvent*,aEvent));
case NS_KEY_EVENT:
return NS_NewDOMKeyboardEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsKeyEvent*,aEvent));
case NS_MOUSE_EVENT:
case NS_MOUSE_SCROLL_EVENT:
case NS_POPUP_EVENT:
return NS_NewDOMMouseEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsInputEvent*,aEvent));
case NS_POPUPBLOCKED_EVENT:
return NS_NewDOMPopupBlockedEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsPopupBlockedEvent*,
aEvent));
case NS_TEXT_EVENT:
return NS_NewDOMTextEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsTextEvent*,aEvent));
case NS_BEFORE_PAGE_UNLOAD_EVENT:
return
NS_NewDOMBeforeUnloadEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsBeforePageUnloadEvent*,
aEvent));
case NS_PAGETRANSITION_EVENT:
return NS_NewDOMPageTransitionEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsPageTransitionEvent*,
aEvent));
#ifdef MOZ_SVG
case NS_SVG_EVENT:
return NS_NewDOMSVGEvent(aDOMEvent, aPresContext,
aEvent);
case NS_SVGZOOM_EVENT:
return NS_NewDOMSVGZoomEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsGUIEvent*,aEvent));
#endif // MOZ_SVG
case NS_XUL_COMMAND_EVENT:
return NS_NewDOMXULCommandEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsXULCommandEvent*,
aEvent));
}
// For all other types of events, create a vanilla event object.
return NS_NewDOMEvent(aDOMEvent, aPresContext, aEvent);
}
// And if we didn't get an event, check the type argument.
if (aEventType.LowerCaseEqualsLiteral("mouseevent") ||
aEventType.LowerCaseEqualsLiteral("mouseevents") ||
aEventType.LowerCaseEqualsLiteral("mousescrollevents") ||
aEventType.LowerCaseEqualsLiteral("popupevents"))
return NS_NewDOMMouseEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsInputEvent*,aEvent));
if (aEventType.LowerCaseEqualsLiteral("keyboardevent") ||
aEventType.LowerCaseEqualsLiteral("keyevents"))
return NS_NewDOMKeyboardEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsKeyEvent*,aEvent));
if (aEventType.LowerCaseEqualsLiteral("mutationevent") ||
aEventType.LowerCaseEqualsLiteral("mutationevents"))
return NS_NewDOMMutationEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsMutationEvent*,aEvent));
if (aEventType.LowerCaseEqualsLiteral("textevent") ||
aEventType.LowerCaseEqualsLiteral("textevents"))
return NS_NewDOMTextEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsTextEvent*,aEvent));
if (aEventType.LowerCaseEqualsLiteral("popupblockedevents"))
return NS_NewDOMPopupBlockedEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsPopupBlockedEvent*,
aEvent));
if (aEventType.LowerCaseEqualsLiteral("uievent") ||
aEventType.LowerCaseEqualsLiteral("uievents"))
return NS_NewDOMUIEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsGUIEvent*,aEvent));
if (aEventType.LowerCaseEqualsLiteral("event") ||
aEventType.LowerCaseEqualsLiteral("events") ||
aEventType.LowerCaseEqualsLiteral("htmlevents"))
return NS_NewDOMEvent(aDOMEvent, aPresContext, aEvent);
#ifdef MOZ_SVG
if (aEventType.LowerCaseEqualsLiteral("svgevent") ||
aEventType.LowerCaseEqualsLiteral("svgevents"))
return NS_NewDOMSVGEvent(aDOMEvent, aPresContext,
aEvent);
if (aEventType.LowerCaseEqualsLiteral("svgzoomevent") ||
aEventType.LowerCaseEqualsLiteral("svgzoomevents"))
return NS_NewDOMSVGZoomEvent(aDOMEvent, aPresContext,
NS_STATIC_CAST(nsGUIEvent*,aEvent));
#endif // MOZ_SVG
if (aEventType.LowerCaseEqualsLiteral("xulcommandevent") ||
aEventType.LowerCaseEqualsLiteral("xulcommandevents"))
return NS_NewDOMXULCommandEvent(aDOMEvent, aPresContext, nsnull);
return NS_ERROR_DOM_NOT_SUPPORTED_ERR;
}
/**
* Captures all events designated for descendant objects at the current level.
* @param an event listener
*/
NS_IMETHODIMP
nsEventListenerManager::CaptureEvent(PRInt32 aEventTypes)
{
return FlipCaptureBit(aEventTypes, PR_TRUE);
}
/**
* Releases all events designated for descendant objects at the current level.
* @param an event listener
*/
NS_IMETHODIMP
nsEventListenerManager::ReleaseEvent(PRInt32 aEventTypes)
{
return FlipCaptureBit(aEventTypes, PR_FALSE);
}
nsresult
nsEventListenerManager::FlipCaptureBit(PRInt32 aEventTypes,
PRBool aInitCapture)
{
// This method exists for Netscape 4.x event handling compatibility.
// New events do not need to be added here.
EventArrayType arrayType = eEventArrayType_None;
PRUint8 bits = 0;
if (aEventTypes & nsIDOMNSEvent::MOUSEDOWN) {
arrayType = eEventArrayType_Mouse;
bits = NS_EVENT_BITS_MOUSE_MOUSEDOWN;
}
if (aEventTypes & nsIDOMNSEvent::MOUSEUP) {
arrayType = eEventArrayType_Mouse;
bits = NS_EVENT_BITS_MOUSE_MOUSEUP;
}
if (aEventTypes & nsIDOMNSEvent::MOUSEOVER) {
arrayType = eEventArrayType_Mouse;
bits = NS_EVENT_BITS_MOUSE_MOUSEOVER;
}
if (aEventTypes & nsIDOMNSEvent::MOUSEOUT) {
arrayType = eEventArrayType_Mouse;
bits = NS_EVENT_BITS_MOUSE_MOUSEOUT;
}
if (aEventTypes & nsIDOMNSEvent::MOUSEMOVE) {
arrayType = eEventArrayType_MouseMotion;
bits = NS_EVENT_BITS_MOUSEMOTION_MOUSEMOVE;
}
if (aEventTypes & nsIDOMNSEvent::CLICK) {
arrayType = eEventArrayType_Mouse;
bits = NS_EVENT_BITS_MOUSE_CLICK;
}
if (aEventTypes & nsIDOMNSEvent::DBLCLICK) {
arrayType = eEventArrayType_Mouse;
bits = NS_EVENT_BITS_MOUSE_DBLCLICK;
}
if (aEventTypes & nsIDOMNSEvent::KEYDOWN) {
arrayType = eEventArrayType_Key;
bits = NS_EVENT_BITS_KEY_KEYDOWN;
}
if (aEventTypes & nsIDOMNSEvent::KEYUP) {
arrayType = eEventArrayType_Key;
bits = NS_EVENT_BITS_KEY_KEYUP;
}
if (aEventTypes & nsIDOMNSEvent::KEYPRESS) {
arrayType = eEventArrayType_Key;
bits = NS_EVENT_BITS_KEY_KEYPRESS;
}
if (aEventTypes & nsIDOMNSEvent::DRAGDROP) {
arrayType = eEventArrayType_Drag;
bits = NS_EVENT_BITS_DRAG_ENTER;
}
/*if (aEventTypes & nsIDOMNSEvent::MOUSEDRAG) {
arrayType = kIDOMMouseListenerarrayType;
bits = NS_EVENT_BITS_MOUSE_MOUSEDOWN;
}*/
if (aEventTypes & nsIDOMNSEvent::FOCUS) {
arrayType = eEventArrayType_Focus;
bits = NS_EVENT_BITS_FOCUS_FOCUS;
}
if (aEventTypes & nsIDOMNSEvent::BLUR) {
arrayType = eEventArrayType_Focus;
bits = NS_EVENT_BITS_FOCUS_BLUR;
}
if (aEventTypes & nsIDOMNSEvent::SELECT) {
arrayType = eEventArrayType_Form;
bits = NS_EVENT_BITS_FORM_SELECT;
}
if (aEventTypes & nsIDOMNSEvent::CHANGE) {
arrayType = eEventArrayType_Form;
bits = NS_EVENT_BITS_FORM_CHANGE;
}
if (aEventTypes & nsIDOMNSEvent::RESET) {
arrayType = eEventArrayType_Form;
bits = NS_EVENT_BITS_FORM_RESET;
}
if (aEventTypes & nsIDOMNSEvent::SUBMIT) {
arrayType = eEventArrayType_Form;
bits = NS_EVENT_BITS_FORM_SUBMIT;
}
if (aEventTypes & nsIDOMNSEvent::LOAD) {
arrayType = eEventArrayType_Load;
bits = NS_EVENT_BITS_LOAD_LOAD;
}
if (aEventTypes & nsIDOMNSEvent::UNLOAD) {
arrayType = eEventArrayType_Load;
bits = NS_EVENT_BITS_LOAD_UNLOAD;
}
if (aEventTypes & nsIDOMNSEvent::ABORT) {
arrayType = eEventArrayType_Load;
bits = NS_EVENT_BITS_LOAD_ABORT;
}
if (aEventTypes & nsIDOMNSEvent::ERROR) {
arrayType = eEventArrayType_Load;
bits = NS_EVENT_BITS_LOAD_ERROR;
}
if (aEventTypes & nsIDOMNSEvent::RESIZE) {
arrayType = eEventArrayType_Paint;
bits = NS_EVENT_BITS_PAINT_RESIZE;
}
if (aEventTypes & nsIDOMNSEvent::SCROLL) {
arrayType = eEventArrayType_Scroll;
bits = NS_EVENT_BITS_PAINT_RESIZE;
}
if (arrayType != eEventArrayType_None) {
nsListenerStruct *ls = FindJSEventListener(arrayType);
if (ls) {
if (aInitCapture)
ls->mSubTypeCapture |= bits;
else
ls->mSubTypeCapture &= ~bits;
ls->mFlags |= NS_EVENT_FLAG_CAPTURE;
}
}
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerManager::Disconnect(PRBool)
{
mTarget = nsnull;
// Bug 323807: nsDOMClassInfo::PreserveWrapper (and
// nsIDOMGCParticipant) require that we remove all event listeners now
// to remove any weak references in the nsDOMClassInfo's preserved
// wrapper table to the target.
return RemoveAllListeners();
}
NS_IMETHODIMP
nsEventListenerManager::SetListenerTarget(nsISupports* aTarget)
{
NS_PRECONDITION(aTarget, "unexpected null pointer");
//WEAK reference, must be set back to nsnull when done by calling Disconnect
mTarget = aTarget;
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerManager::GetSystemEventGroupLM(nsIDOMEventGroup **aGroup)
{
if (!gSystemEventGroup) {
nsresult result;
nsCOMPtr<nsIDOMEventGroup> group(do_CreateInstance(kDOMEventGroupCID,&result));
if (NS_FAILED(result))
return result;
gSystemEventGroup = group;
NS_ADDREF(gSystemEventGroup);
}
*aGroup = gSystemEventGroup;
NS_ADDREF(*aGroup);
return NS_OK;
}
nsresult
nsEventListenerManager::GetDOM2EventGroup(nsIDOMEventGroup **aGroup)
{
if (!gDOM2EventGroup) {
nsresult result;
nsCOMPtr<nsIDOMEventGroup> group(do_CreateInstance(kDOMEventGroupCID,&result));
if (NS_FAILED(result))
return result;
gDOM2EventGroup = group;
NS_ADDREF(gDOM2EventGroup);
}
*aGroup = gDOM2EventGroup;
NS_ADDREF(*aGroup);
return NS_OK;
}
// nsIDOMEventTarget interface
NS_IMETHODIMP
nsEventListenerManager::AddEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
PRBool aUseCapture)
{
PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
return AddEventListenerByType(aListener, aType, flags, nsnull);
}
NS_IMETHODIMP
nsEventListenerManager::RemoveEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
PRBool aUseCapture)
{
PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
return RemoveEventListenerByType(aListener, aType, flags, nsnull);
}
NS_IMETHODIMP
nsEventListenerManager::DispatchEvent(nsIDOMEvent* aEvent, PRBool *_retval)
{
nsCOMPtr<nsIContent> targetContent(do_QueryInterface(mTarget));
if (!targetContent) {
// nothing to dispatch on -- bad!
return NS_ERROR_FAILURE;
}
nsCOMPtr<nsIDocument> document = targetContent->GetOwnerDoc();
// Do nothing if the element does not belong to a document
if (!document) {
return NS_OK;
}
// Obtain a presentation shell
nsIPresShell *shell = document->GetShellAt(0);
if (!shell) {
return NS_OK;
}
nsCOMPtr<nsPresContext> context = shell->GetPresContext();
return context->EventStateManager()->
DispatchNewEvent(mTarget, aEvent, _retval);
}
// nsIDOM3EventTarget interface
NS_IMETHODIMP
nsEventListenerManager::AddGroupedEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
PRBool aUseCapture,
nsIDOMEventGroup* aEvtGrp)
{
PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
return AddEventListenerByType(aListener, aType, flags, aEvtGrp);
}
NS_IMETHODIMP
nsEventListenerManager::RemoveGroupedEventListener(const nsAString& aType,
nsIDOMEventListener* aListener,
PRBool aUseCapture,
nsIDOMEventGroup* aEvtGrp)
{
PRInt32 flags = aUseCapture ? NS_EVENT_FLAG_CAPTURE : NS_EVENT_FLAG_BUBBLE;
return RemoveEventListenerByType(aListener, aType, flags, aEvtGrp);
}
NS_IMETHODIMP
nsEventListenerManager::CanTrigger(const nsAString & type, PRBool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
NS_IMETHODIMP
nsEventListenerManager::IsRegisteredHere(const nsAString & type, PRBool *_retval)
{
return NS_ERROR_NOT_IMPLEMENTED;
}
// nsIDOMEventReceiver interface
NS_IMETHODIMP
nsEventListenerManager::AddEventListenerByIID(nsIDOMEventListener *aListener,
const nsIID& aIID)
{
return AddEventListenerByIID(aListener, aIID, NS_EVENT_FLAG_BUBBLE);
}
NS_IMETHODIMP
nsEventListenerManager::RemoveEventListenerByIID(nsIDOMEventListener *aListener, const nsIID& aIID)
{
return RemoveEventListenerByIID(aListener, aIID, NS_EVENT_FLAG_BUBBLE);
}
NS_IMETHODIMP
nsEventListenerManager::GetListenerManager(nsIEventListenerManager** aInstancePtrResult)
{
NS_ENSURE_ARG_POINTER(aInstancePtrResult);
*aInstancePtrResult = NS_STATIC_CAST(nsIEventListenerManager*, this);
NS_ADDREF(*aInstancePtrResult);
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerManager::HandleEvent(nsIDOMEvent *aEvent)
{
PRBool defaultActionEnabled;
return DispatchEvent(aEvent, &defaultActionEnabled);
}
NS_IMETHODIMP
nsEventListenerManager::GetSystemEventGroup(nsIDOMEventGroup **aGroup)
{
return GetSystemEventGroupLM(aGroup);
}
nsresult
nsEventListenerManager::FixContextMenuEvent(nsPresContext* aPresContext,
nsIDOMEventTarget* aCurrentTarget,
nsEvent* aEvent,
nsIDOMEvent** aDOMEvent)
{
nsIPresShell* shell = aPresContext->GetPresShell();
if (!shell) {
// Nothing to do.
return NS_OK;
}
nsresult ret = NS_OK;
if (nsnull == *aDOMEvent) {
// If we're here because of the key-equiv for showing context menus, we
// have to twiddle with the NS event to make sure the context menu comes
// up in the upper left of the relevant content area before we create
// the DOM event. Since we never call InitMouseEvent() on the event,
// the client X/Y will be 0,0. We can make use of that if the widget is null.
if (aEvent->message == NS_CONTEXTMENU_KEY) {
NS_IF_RELEASE(((nsGUIEvent*)aEvent)->widget);
aPresContext->GetViewManager()->GetWidget(&((nsGUIEvent*)aEvent)->widget);
aEvent->refPoint.x = 0;
aEvent->refPoint.y = 0;
}
ret = NS_NewDOMMouseEvent(aDOMEvent, aPresContext, NS_STATIC_CAST(nsInputEvent*, aEvent));
NS_ENSURE_SUCCESS(ret, ret);
}
// see if we should use the caret position for the popup
if (aEvent->message == NS_CONTEXTMENU_KEY) {
if (PrepareToUseCaretPosition(((nsGUIEvent*)aEvent)->widget, aEvent, shell))
return NS_OK;
}
// If we're here because of the key-equiv for showing context menus, we
// have to reset the event target to the currently focused element. Get it
// from the focus controller.
nsCOMPtr<nsIDOMEventTarget> currentTarget(aCurrentTarget);
nsCOMPtr<nsIDOMElement> currentFocus;
if (aEvent->message == NS_CONTEXTMENU_KEY) {
nsIDocument *doc = shell->GetDocument();
if (doc) {
nsCOMPtr<nsPIDOMWindow> privWindow = do_QueryInterface(doc->GetScriptGlobalObject());
if (privWindow) {
nsIFocusController *focusController =
privWindow->GetRootFocusController();
if (focusController)
focusController->GetFocusedElement(getter_AddRefs(currentFocus));
}
}
}
if (currentFocus) {
// Reset event coordinates relative to focused frame in view
nsPoint targetPt;
GetCoordinatesFor(currentFocus, aPresContext, shell, targetPt);
aEvent->refPoint.x = targetPt.x;
aEvent->refPoint.y = targetPt.y;
currentTarget = do_QueryInterface(currentFocus);
nsCOMPtr<nsIPrivateDOMEvent> pEvent(do_QueryInterface(*aDOMEvent));
pEvent->SetTarget(currentTarget);
}
return ret;
}
// nsEventListenerManager::PrepareToUseCaretPosition
//
// This checks to see if we should use the caret position for popup context
// menus. If we should, it fills in refpoint and point on the event and
// returns true. This function will also scroll the window as needed to make
// the caret visible.
//
// The event widget should be the widget that generated the event, and
// whose coordinate system the resulting event's refPoint should be
// relative to.
PRBool
nsEventListenerManager::PrepareToUseCaretPosition(nsIWidget* aEventWidget,
nsEvent* aEvent,
nsIPresShell* aShell)
{
nsresult rv;
NS_ASSERTION(aEventWidget, "Event widget is null");
NS_ASSERTION(aShell, "Shell is null");
// check caret visibility
nsCOMPtr<nsICaret> caret;
rv = aShell->GetCaret(getter_AddRefs(caret));
NS_ENSURE_SUCCESS(rv, PR_FALSE);
NS_ENSURE_TRUE(caret, PR_FALSE);
PRBool caretVisible = PR_FALSE;
rv = caret->GetCaretVisible(&caretVisible);
if (NS_FAILED(rv) || ! caretVisible)
return PR_FALSE;
// caret selection, watch out: GetCaretDOMSelection can return null but NS_OK
nsCOMPtr<nsISelection> domSelection;
rv = caret->GetCaretDOMSelection(getter_AddRefs(domSelection));
NS_ENSURE_SUCCESS(rv, PR_FALSE);
NS_ENSURE_TRUE(domSelection, PR_FALSE);
// since the match could be an anonymous textnode inside a
// <textarea> or text <input>, we need to get the outer frame
// note: frames are not refcounted
nsIFrame* frame = nsnull; // may be NULL
nsITextControlFrame* tcFrame = nsnull; // may be NULL
nsCOMPtr<nsIDOMNode> node;
rv = domSelection->GetFocusNode(getter_AddRefs(node));
NS_ENSURE_SUCCESS(rv, PR_FALSE);
NS_ENSURE_TRUE(node, PR_FALSE);
nsCOMPtr<nsIContent> content(do_QueryInterface(node));
for ( ; content; content = content->GetParent()) {
if (!content->IsNativeAnonymous()) {
rv = aShell->GetPrimaryFrameFor(content, &frame);
if (NS_FAILED(rv) || frame) {
// not refcounted, will be NULL for some elements
CallQueryInterface(frame, &tcFrame);
}
break;
}
}
// It seems like selCon->ScrollSelectionIntoView should be enough, but it's
// not. The problem is that scrolling the selection into view when it is
// below the current viewport will align the top line of the frame exactly
// with the bottom of the window. This is fine, BUT, the popup event causes
// the control to be re-focused which does this exact call to
// ScrollFrameIntoView, which has a one-pixel disagreement of whether the
// frame is actually in view. The result is that the frame is aligned with
// the top of the window, but the menu is still at the bottom.
//
// Doing this call first forces the frame to be in view, eliminating the
// problem. The only difference in the result is that if your cursor is in
// an edit box below the current view, you'll get the edit box aligned with
// the top of the window. This is arguably better behavior anyway.
if (frame) {
rv = aShell->ScrollFrameIntoView(frame, NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE,
NS_PRESSHELL_SCROLL_IF_NOT_VISIBLE);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
}
// Actually scroll the selection (ie caret) into view. Note that this must
// be synchronous since we will be checking the caret position on the screen.
//
// Be easy about errors, and just don't scroll in those cases. Better to have
// the correct menu at a weird place than the wrong menu.
nsCOMPtr<nsISelectionController> selCon;
if (tcFrame)
tcFrame->GetSelectionContr(getter_AddRefs(selCon));
else
selCon = do_QueryInterface(aShell);
if (selCon) {
rv = selCon->ScrollSelectionIntoView(nsISelectionController::SELECTION_NORMAL,
nsISelectionController::SELECTION_FOCUS_REGION, PR_TRUE);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
}
// get caret position relative to some view (normally the same as the
// event widget view, but this is not guaranteed)
PRBool isCollapsed;
nsIView* view;
nsRect caretCoords;
rv = caret->GetCaretCoordinates(nsICaret::eRenderingViewCoordinates,
domSelection, &caretCoords, &isCollapsed,
&view);
NS_ENSURE_SUCCESS(rv, PR_FALSE);
// in case the view used for caret coordinates was something else, we need
// to bring those coordinates into the space of the widget view
nsIView* widgetView = nsIView::GetViewFor(aEventWidget);
NS_ENSURE_TRUE(widgetView, PR_FALSE);
nsPoint viewToWidget;
widgetView->GetNearestWidget(&viewToWidget);
nsPoint viewDelta = view->GetOffsetTo(widgetView) + viewToWidget;
// caret coordinates are in twips, convert to pixels for refpoint
float t2p = aShell->GetPresContext()->TwipsToPixels();
aEvent->refPoint.x = NSTwipsToIntPixels(viewDelta.x + caretCoords.x + caretCoords.width, t2p);
aEvent->refPoint.y = NSTwipsToIntPixels(viewDelta.y + caretCoords.y + caretCoords.height, t2p);
// convert to coordinate system for point
aEvent->point.x = aEvent->point.y = 0;
nsPresContext* context = aShell->GetPresContext();
if (context) {
nsIFrame* eventFrame;
context->EventStateManager()->GetEventTarget(&eventFrame);
if (eventFrame) {
aEvent->point = nsLayoutUtils::GetEventCoordinatesForNearestView(
aEvent, eventFrame);
}
}
return PR_TRUE;
}
// Get coordinates relative to root view for element,
// first ensuring the element is onscreen
void
nsEventListenerManager::GetCoordinatesFor(nsIDOMElement *aCurrentEl,
nsPresContext *aPresContext,
nsIPresShell *aPresShell,
nsPoint& aTargetPt)
{
nsCOMPtr<nsIContent> focusedContent(do_QueryInterface(aCurrentEl));
nsIFrame *frame = nsnull;
aPresShell->GetPrimaryFrameFor(focusedContent, &frame);
if (frame) {
aPresShell->ScrollFrameIntoView(frame, NS_PRESSHELL_SCROLL_ANYWHERE,
NS_PRESSHELL_SCROLL_ANYWHERE);
nsPoint frameOrigin(0, 0);
// Get the frame's origin within its view
nsIView *view = frame->GetClosestView(&frameOrigin);
NS_ASSERTION(view, "No view for frame");
nsIViewManager* vm = aPresShell->GetViewManager();
nsIView *rootView = nsnull;
vm->GetRootView(rootView);
NS_ASSERTION(rootView, "No root view in pres shell");
// View's origin within its root view
frameOrigin += view->GetOffsetTo(rootView);
// Start context menu down and to the right from top left of frame
// use the lineheight. This is a good distance to move the context
// menu away from the top left corner of the frame. If we always
// used the frame height, the context menu could end up far away,
// for example when we're focused on linked images.
// On the other hand, we want to use the frame height if it's less
// than the current line height, so that the context menu appears
// associated with the correct frame.
nscoord extra = frame->GetSize().height;
nsIScrollableView *scrollView =
nsLayoutUtils::GetNearestScrollingView(view, nsLayoutUtils::eEither);
if (scrollView) {
nscoord scrollViewLineHeight;
scrollView->GetLineHeight(&scrollViewLineHeight);
if (extra > scrollViewLineHeight) {
extra = scrollViewLineHeight;
}
}
PRInt32 extraPixelsY = 0;
#ifdef MOZ_XUL
// Tree view special case (tree items have no frames)
// Get the focused row and add its coordinates, which are already in pixels
// XXX Boris, should we create a new interface so that event listener manager doesn't
// need to know about trees? Something like nsINodelessChildCreator which
// could provide the current focus coordinates?
nsCOMPtr<nsIDOMXULElement> xulElement(do_QueryInterface(aCurrentEl));
if (xulElement) {
nsCOMPtr<nsIBoxObject> box;
xulElement->GetBoxObject(getter_AddRefs(box));
nsCOMPtr<nsITreeBoxObject> treeBox(do_QueryInterface(box));
if (treeBox) {
// Factor in focused row
nsCOMPtr<nsIDOMXULMultiSelectControlElement> multiSelect =
do_QueryInterface(aCurrentEl);
NS_ASSERTION(multiSelect, "No multi select interface for tree");
PRInt32 currentIndex;
multiSelect->GetCurrentIndex(&currentIndex);
if (currentIndex >= 0) {
treeBox->EnsureRowIsVisible(currentIndex);
PRInt32 firstVisibleRow, rowHeight;
treeBox->GetFirstVisibleRow(&firstVisibleRow);
treeBox->GetRowHeight(&rowHeight);
extraPixelsY = (currentIndex - firstVisibleRow + 1) * rowHeight;
extra = 0;
nsCOMPtr<nsITreeColumns> cols;
treeBox->GetColumns(getter_AddRefs(cols));
if (cols) {
nsCOMPtr<nsITreeColumn> col;
cols->GetFirstColumn(getter_AddRefs(col));
if (col) {
nsCOMPtr<nsIDOMElement> colElement;
col->GetElement(getter_AddRefs(colElement));
nsCOMPtr<nsIContent> colContent(do_QueryInterface(colElement));
if (colContent) {
aPresShell->GetPrimaryFrameFor(colContent, &frame);
if (frame) {
frameOrigin.y += frame->GetSize().height;
}
}
}
}
}
}
}
#endif
// Convert from twips to pixels
float t2p = aPresContext->TwipsToPixels();
aTargetPt.x = NSTwipsToIntPixels(frameOrigin.x + extra, t2p);
aTargetPt.y = NSTwipsToIntPixels(frameOrigin.y + extra, t2p) + extraPixelsY;
}
}
PRBool
nsEventListenerManager::HasUnloadListeners()
{
nsVoidArray *listeners = GetListenersByType(eEventArrayType_Load, nsnull,
PR_FALSE);
if (listeners) {
PRInt32 count = listeners->Count();
for (PRInt32 i = 0; i < count; ++i) {
PRUint32 subtype = NS_STATIC_CAST(nsListenerStruct*,
listeners->FastElementAt(i))->mSubType;
if (subtype == NS_EVENT_BITS_NONE ||
subtype & (NS_EVENT_BITS_LOAD_UNLOAD |
NS_EVENT_BITS_LOAD_BEFORE_UNLOAD))
return PR_TRUE;
}
}
return PR_FALSE;
}
nsresult
NS_NewEventListenerManager(nsIEventListenerManager** aInstancePtrResult)
{
nsIEventListenerManager* l = new nsEventListenerManager();
if (!l) {
return NS_ERROR_OUT_OF_MEMORY;
}
return CallQueryInterface(l, aInstancePtrResult);
}