2549 lines
87 KiB
C++
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(¤tIndex);
|
|
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);
|
|
}
|
|
|