Bug 448602 - Have a way to enumerate event listeners, r=bz

--HG--
extra : rebase_source : 77ed96b23db73ae78ed7f34ed231daf84fae1f54
This commit is contained in:
Olli Pettay 2009-10-16 11:57:32 +03:00
parent 881607eb48
commit 2d83c2550e
21 changed files with 676 additions and 26 deletions

View file

@ -122,6 +122,7 @@
@BINPATH@/components/commandlines.xpt
@BINPATH@/components/composer.xpt
@BINPATH@/components/content_base.xpt
@BINPATH@/components/content_events.xpt
@BINPATH@/components/content_canvas.xpt
@BINPATH@/components/content_htmldoc.xpt
@BINPATH@/components/content_html.xpt

View file

@ -301,6 +301,13 @@
#define NS_SYNCLOADDOMSERVICE_CONTRACTID \
"@mozilla.org/content/syncload-dom-service;1"
#define NS_EVENTLISTENERSERVICE_CID \
{ /* baa34652-f1f1-4185-b224-244ee82a413a */ \
0xbaa34652, 0xf1f1, 0x4185, \
{0xb2, 0x24, 0x24, 0x4e, 0xe8, 0x2a, 0x41, 0x3a } }
#define NS_EVENTLISTENERSERVICE_CONTRACTID \
"@mozilla.org/eventlistenerservice;1"
// {f96f5ec9-755b-447e-b1f3-717d1a84bb41}
#define NS_PLUGINDOCUMENT_CID \
{ 0xf96f5ec9, 0x755b, 0x447e, { 0xb1, 0xf3, 0x71, 0x7d, 0x1a, 0x84, 0xbb, 0x41 } }

View file

@ -43,6 +43,7 @@ VPATH = @srcdir@
include $(DEPTH)/config/autoconf.mk
MODULE = content
XPIDL_MODULE = content_events
EXPORTS = \
nsMutationEvent.h \
@ -57,5 +58,9 @@ EXPORTS = \
nsPIDOMEventTarget.h \
$(NULL)
XPIDLSRCS = \
nsIEventListenerService.idl \
$(NULL)
include $(topsrcdir)/config/rules.mk

View file

@ -49,7 +49,7 @@ class nsIDOMEvent;
class nsPIDOMEventTarget;
class nsIScriptGlobalObject;
class nsEventTargetChainItem;
template<class E> class nsCOMArray;
/**
* About event dispatching:
@ -230,6 +230,10 @@ public:
* In other words, aEvent->target is only a property of the event and it has
* nothing to do with the construction of the event target chain.
* Neither aTarget nor aEvent is allowed to be nsnull.
*
* If aTargets is non-null, event target chain will be created, but
* event won't be handled. In this case aEvent->message should be
* NS_EVENT_TYPE_NULL.
* @note Use this method when dispatching an nsEvent.
*/
static nsresult Dispatch(nsISupports* aTarget,
@ -237,7 +241,8 @@ public:
nsEvent* aEvent,
nsIDOMEvent* aDOMEvent = nsnull,
nsEventStatus* aEventStatus = nsnull,
nsDispatchingCallback* aCallback = nsnull);
nsDispatchingCallback* aCallback = nsnull,
nsCOMArray<nsPIDOMEventTarget>* aTargets = nsnull);
/**
* Dispatches an event.

View file

@ -48,13 +48,15 @@ class nsIDOMEventTarget;
class nsIDOMEventGroup;
class nsIAtom;
class nsPIDOMEventTarget;
class nsIEventListenerInfo;
template<class E> class nsCOMArray;
/*
* Event listener manager interface.
*/
#define NS_IEVENTLISTENERMANAGER_IID \
{ 0xadfdc265, 0xea1c, 0x4c0b, \
{ 0x91, 0xca, 0x37, 0x67, 0x2c, 0x83, 0x92, 0x1f } }
{ 0xac49ce4e, 0xaecf, 0x45db, \
{ 0xa1, 0xe0, 0xea, 0x1d, 0x38, 0x73, 0x39, 0xa3 } }
class nsIEventListenerManager : public nsISupports {
@ -189,6 +191,12 @@ public:
*/
virtual PRUint32 MutationListenerBits() = 0;
/**
* Sets aList to the list of nsIEventListenerInfo objects representing the
* listeners managed by this listener manager.
*/
virtual nsresult GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList) = 0;
/**
* Returns PR_TRUE if there is at least one event listener for aEventName.
*/

View file

@ -0,0 +1,89 @@
/* -*- 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 Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Olli Pettay <Olli.Pettay@helsinki.fi> (Original Author)
*
* 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 "nsIDOMEventListener.idl"
interface nsIDOMEventTarget;
/**
* An instance of this interface describes how an event listener
* was added to an event target.
*/
[scriptable, uuid(4f132988-4709-44e5-985c-9e16d0f7c954)]
interface nsIEventListenerInfo : nsISupports
{
/**
* The type of the event for which the listener was added.
*/
readonly attribute AString type;
readonly attribute boolean capturing;
readonly attribute boolean allowsUntrusted;
readonly attribute boolean inSystemEventGroup;
/**
* Tries to serialize event listener to a string.
* Returns null if serialization isn't possible
* (for example with C++ listeners).
*/
AString toSource();
};
[scriptable, uuid(551cac0f-31ed-45e0-8d67-bc0d6e117b31)]
interface nsIEventListenerService : nsISupports
{
/**
* Returns an array of nsIEventListenerInfo objects.
* If aEventTarget doesn't have any listeners, this returns null.
*/
void getListenerInfoFor(in nsIDOMEventTarget aEventTarget,
out unsigned long aCount,
[retval, array, size_is(aCount)] out
nsIEventListenerInfo aOutArray);
/**
* Returns an array of event targets.
* aEventTarget will be at index 0.
* The objects are the ones that would be used as DOMEvent.currentTarget while
* dispatching an event to aEventTarget
* @note Some events, especially 'load', may actually have a shorter
* event target chain than what this methods returns.
*/
void getEventTargetChainFor(in nsIDOMEventTarget aEventTarget,
out unsigned long aCount,
[retval, array, size_is(aCount)] out
nsIDOMEventTarget aOutArray);
};

View file

@ -74,6 +74,7 @@ CPPSRCS = \
nsEventDispatcher.cpp \
nsIMEStateManager.cpp \
nsContentEventHandler.cpp \
nsEventListenerService.cpp \
nsDOMProgressEvent.cpp \
nsDOMDataTransfer.cpp \
nsDOMNotifyPaintEvent.cpp \

View file

@ -200,11 +200,11 @@ public:
static void Shutdown();
static const char* GetEventName(PRUint32 aEventType);
protected:
// Internal helper functions
nsresult SetEventType(const nsAString& aEventTypeArg);
static const char* GetEventName(PRUint32 aEventType);
already_AddRefed<nsIDOMEventTarget> GetTargetFromFrame();
nsresult ReportWrongPropertyAccessWarning(const char* aPropertyName);

View file

@ -427,11 +427,13 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
nsEvent* aEvent,
nsIDOMEvent* aDOMEvent,
nsEventStatus* aEventStatus,
nsDispatchingCallback* aCallback)
nsDispatchingCallback* aCallback,
nsCOMArray<nsPIDOMEventTarget>* aTargets)
{
NS_ASSERTION(aEvent, "Trying to dispatch without nsEvent!");
NS_ENSURE_TRUE(!NS_IS_EVENT_IN_DISPATCH(aEvent),
NS_ERROR_ILLEGAL_VALUE);
NS_ASSERTION(!aTargets || !aEvent->message, "Wrong parameters!");
nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(aTarget);
#ifdef DEBUG
@ -530,18 +532,27 @@ nsEventDispatcher::Dispatch(nsISupports* aTarget,
}
}
if (NS_SUCCEEDED(rv)) {
// Event target chain is created. Handle the chain.
nsEventChainPostVisitor postVisitor(preVisitor);
rv = topEtci->HandleEventTargetChain(postVisitor,
NS_EVENT_FLAG_BUBBLE |
NS_EVENT_FLAG_CAPTURE,
aCallback,
PR_TRUE);
preVisitor.mEventStatus = postVisitor.mEventStatus;
// If the DOM event was created during event flow.
if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
preVisitor.mDOMEvent = postVisitor.mDOMEvent;
if (aTargets) {
aTargets->Clear();
nsEventTargetChainItem* item = targetEtci;
while(item) {
aTargets->AppendObject(item->CurrentTarget()->GetTargetForDOMEvent());
item = item->mParent;
}
} else {
// Event target chain is created. Handle the chain.
nsEventChainPostVisitor postVisitor(preVisitor);
rv = topEtci->HandleEventTargetChain(postVisitor,
NS_EVENT_FLAG_BUBBLE |
NS_EVENT_FLAG_CAPTURE,
aCallback,
PR_TRUE);
preVisitor.mEventStatus = postVisitor.mEventStatus;
// If the DOM event was created during event flow.
if (!preVisitor.mDOMEvent && postVisitor.mDOMEvent) {
preVisitor.mDOMEvent = postVisitor.mDOMEvent;
}
}
}
}

View file

@ -92,6 +92,9 @@
#include "nsDOMJSUtils.h"
#include "nsDOMScriptObjectHolder.h"
#include "nsDataHashtable.h"
#include "nsCOMArray.h"
#include "nsEventListenerService.h"
#include "nsDOMEvent.h"
#define EVENT_TYPE_EQUALS( ls, type, userType ) \
(ls->mEventType && ls->mEventType == type && \
@ -1383,6 +1386,74 @@ nsEventListenerManager::HasListeners()
return !mListeners.IsEmpty();
}
nsresult
nsEventListenerManager::GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList)
{
nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(mTarget);
NS_ENSURE_STATE(target);
aList->Clear();
PRUint32 count = mListeners.Length();
for (PRUint32 i = 0; i < count; ++i) {
const nsListenerStruct& ls = mListeners.ElementAt(i);
PRBool capturing = !!(ls.mFlags & NS_EVENT_FLAG_CAPTURE);
PRBool systemGroup = !!(ls.mGroupFlags & NS_EVENT_FLAG_SYSTEM_EVENT);
PRBool allowsUntrusted = !!(ls.mFlags & NS_PRIV_EVENT_UNTRUSTED_PERMITTED);
// If this is a script handler and we haven't yet
// compiled the event handler itself
if ((ls.mFlags & NS_PRIV_EVENT_FLAG_SCRIPT) && ls.mHandlerIsString) {
nsCOMPtr<nsIJSEventListener> jslistener = do_QueryInterface(ls.mListener);
if (jslistener) {
CompileEventHandlerInternal(jslistener->GetEventContext(),
jslistener->GetEventScope(),
jslistener->GetEventTarget(),
ls.mTypeAtom,
const_cast<nsListenerStruct*>(&ls),
mTarget);
}
}
if (ls.mTypeData) {
// Handle special event listener interfaces, like nsIDOMFocusListener.
for (PRInt32 j = 0; j < ls.mTypeData->numEvents; ++j) {
const EventDispatchData* dispData = &(ls.mTypeData->events[j]);
const char* eventName = nsDOMEvent::GetEventName(dispData->message);
if (eventName) {
NS_ConvertASCIItoUTF16 eventType(eventName);
nsRefPtr<nsEventListenerInfo> info =
new nsEventListenerInfo(eventType, ls.mListener, capturing,
allowsUntrusted, systemGroup);
NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
aList->AppendObject(info);
}
}
} else if (ls.mEventType == NS_USER_DEFINED_EVENT) {
// Handle user defined event types.
if (ls.mTypeAtom) {
nsAutoString atomName;
ls.mTypeAtom->ToString(atomName);
const nsDependentSubstring& eventType =
Substring(atomName, 2, atomName.Length() - 2);
nsRefPtr<nsEventListenerInfo> info =
new nsEventListenerInfo(eventType, ls.mListener, capturing,
allowsUntrusted, systemGroup);
NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
aList->AppendObject(info);
}
} else {
// Handle normal events.
const char* eventName = nsDOMEvent::GetEventName(ls.mEventType);
if (eventName) {
NS_ConvertASCIItoUTF16 eventType(eventName);
nsRefPtr<nsEventListenerInfo> info =
new nsEventListenerInfo(eventType, ls.mListener, capturing,
allowsUntrusted, systemGroup);
NS_ENSURE_TRUE(info, NS_ERROR_OUT_OF_MEMORY);
aList->AppendObject(info);
}
}
}
return NS_OK;
}
PRBool
nsEventListenerManager::HasUnloadListeners()
{

View file

@ -134,6 +134,8 @@ public:
virtual PRBool HasListeners();
virtual nsresult GetListenerInfo(nsCOMArray<nsIEventListenerInfo>* aList);
static PRUint32 GetIdentifierForEvent(nsIAtom* aEvent);
// nsIDOMEventTarget

View file

@ -0,0 +1,204 @@
/* -*- 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 Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Olli Pettay <Olli.Pettay@helsinki.fi> (Original Author)
*
* 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 "nsEventListenerService.h"
#include "nsCOMArray.h"
#include "nsIEventListenerManager.h"
#include "nsPIDOMEventTarget.h"
#include "nsIVariant.h"
#include "nsIServiceManager.h"
#include "nsMemory.h"
#include "nsContentUtils.h"
#include "nsIXPConnect.h"
#include "nsIDOMWindow.h"
#include "nsPIDOMWindow.h"
#include "nsJSUtils.h"
#include "nsIPrivateDOMEvent.h"
#include "nsIJSContextStack.h"
#include "nsGUIEvent.h"
#include "nsEventDispatcher.h"
#include "nsIJSEventListener.h"
NS_IMPL_CYCLE_COLLECTION_1(nsEventListenerInfo, mListener)
NS_INTERFACE_MAP_BEGIN_CYCLE_COLLECTION(nsEventListenerInfo)
NS_INTERFACE_MAP_ENTRY(nsIEventListenerInfo)
NS_INTERFACE_MAP_ENTRY(nsISupports)
NS_INTERFACE_MAP_ENTRY_CONTENT_CLASSINFO(EventListenerInfo)
NS_INTERFACE_MAP_END
NS_IMPL_CYCLE_COLLECTING_ADDREF(nsEventListenerInfo)
NS_IMPL_CYCLE_COLLECTING_RELEASE(nsEventListenerInfo)
NS_IMETHODIMP
nsEventListenerInfo::GetType(nsAString& aType)
{
aType = mType;
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerInfo::GetCapturing(PRBool* aCapturing)
{
*aCapturing = mCapturing;
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerInfo::GetAllowsUntrusted(PRBool* aAllowsUntrusted)
{
*aAllowsUntrusted = mAllowsUntrusted;
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerInfo::GetInSystemEventGroup(PRBool* aInSystemEventGroup)
{
*aInSystemEventGroup = mInSystemEventGroup;
return NS_OK;
}
NS_IMPL_ISUPPORTS1(nsEventListenerService, nsIEventListenerService)
NS_IMETHODIMP
nsEventListenerInfo::ToSource(nsAString& aResult)
{
aResult.SetIsVoid(PR_TRUE);
nsCOMPtr<nsIXPConnectWrappedJS> wrappedJS = do_QueryInterface(mListener);
if (wrappedJS) {
JSObject* object = nsnull;
wrappedJS->GetJSObject(&object);
if (object) {
nsCOMPtr<nsIThreadJSContextStack> stack =
nsContentUtils::ThreadJSContextStack();
if (stack) {
JSContext* cx = nsnull;
stack->GetSafeJSContext(&cx);
if (cx && NS_SUCCEEDED(stack->Push(cx))) {
JSAutoRequest ar(cx);
jsval v = OBJECT_TO_JSVAL(object);
JSString* str = JS_ValueToSource(cx, v);
if (str) {
aResult.Assign(nsDependentJSString(str));
}
stack->Pop(&cx);
}
}
}
} else {
nsCOMPtr<nsIJSEventListener> jsl = do_QueryInterface(mListener);
if (jsl) {
jsl->ToString(mType, aResult);
}
}
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerService::GetListenerInfoFor(nsIDOMEventTarget* aEventTarget,
PRUint32* aCount,
nsIEventListenerInfo*** aOutArray)
{
*aCount = 0;
*aOutArray = nsnull;
nsCOMArray<nsIEventListenerInfo> listenerInfos;
nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(aEventTarget);
if (target) {
nsCOMPtr<nsIEventListenerManager> elm =
target->GetListenerManager(PR_FALSE);
if (elm) {
elm->GetListenerInfo(&listenerInfos);
}
}
PRInt32 count = listenerInfos.Count();
if (count == 0) {
return NS_OK;
}
*aOutArray =
static_cast<nsIEventListenerInfo**>(
nsMemory::Alloc(sizeof(nsIEventListenerInfo*) * count));
NS_ENSURE_TRUE(*aOutArray, NS_ERROR_OUT_OF_MEMORY);
for (PRInt32 i = 0; i < count; ++i) {
NS_ADDREF((*aOutArray)[i] = listenerInfos[i]);
}
*aCount = count;
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerService::GetEventTargetChainFor(nsIDOMEventTarget* aEventTarget,
PRUint32* aCount,
nsIDOMEventTarget*** aOutArray)
{
*aCount = 0;
*aOutArray = nsnull;
nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(aEventTarget);
NS_ENSURE_ARG(target);
nsEvent event(PR_TRUE, NS_EVENT_TYPE_NULL);
nsCOMArray<nsPIDOMEventTarget> targets;
nsresult rv = nsEventDispatcher::Dispatch(target, nsnull, &event,
nsnull, nsnull, nsnull, &targets);
NS_ENSURE_SUCCESS(rv, rv);
PRInt32 count = targets.Count();
if (count == 0) {
return NS_OK;
}
*aOutArray =
static_cast<nsIDOMEventTarget**>(
nsMemory::Alloc(sizeof(nsPIDOMEventTarget*) * count));
NS_ENSURE_TRUE(*aOutArray, NS_ERROR_OUT_OF_MEMORY);
for (PRInt32 i = 0; i < count; ++i) {
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(targets[i]);
(*aOutArray)[i] = target.forget().get();
}
*aCount = count;
return NS_OK;
}
nsresult
NS_NewEventListenerService(nsIEventListenerService** aResult)
{
*aResult = new nsEventListenerService();
NS_ENSURE_TRUE(*aResult, NS_ERROR_OUT_OF_MEMORY);
NS_ADDREF(*aResult);
return NS_OK;
}

View file

@ -0,0 +1,76 @@
/* -*- 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 Mozilla Foundation.
* Portions created by the Initial Developer are Copyright (C) 2009
* the Initial Developer. All Rights Reserved.
*
* Contributor(s):
* Olli Pettay <Olli.Pettay@helsinki.fi> (Original Author)
*
* 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 ***** */
#ifndef nsEventListenerService_h__
#define nsEventListenerService_h__
#include "nsIEventListenerService.h"
#include "nsAutoPtr.h"
#include "nsIDOMEventListener.h"
#include "nsIDOMEventTarget.h"
#include "nsString.h"
#include "nsPIDOMEventTarget.h"
#include "nsCycleCollectionParticipant.h"
class nsEventListenerInfo : public nsIEventListenerInfo
{
public:
nsEventListenerInfo(const nsAString& aType, nsIDOMEventListener* aListener,
PRBool aCapturing, PRBool aAllowsUntrusted,
PRBool aInSystemEventGroup)
: mType(aType), mListener(aListener), mCapturing(aCapturing),
mAllowsUntrusted(aAllowsUntrusted),
mInSystemEventGroup(aInSystemEventGroup) {}
virtual ~nsEventListenerInfo() {}
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_CLASS(nsEventListenerInfo)
NS_DECL_NSIEVENTLISTENERINFO
protected:
nsString mType;
// nsReftPtr because that is what nsListenerStruct uses too.
nsRefPtr<nsIDOMEventListener> mListener;
PRPackedBool mCapturing;
PRPackedBool mAllowsUntrusted;
PRPackedBool mInSystemEventGroup;
};
class nsEventListenerService : public nsIEventListenerService
{
public:
NS_DECL_ISUPPORTS
NS_DECL_NSIEVENTLISTENERSERVICE
};
#endif

View file

@ -69,6 +69,7 @@ _TEST_FILES = \
test_bug412567.html \
test_bug443985.html \
test_bug447736.html \
test_bug448602.html \
test_bug450876.html \
test_bug456273.html \
test_bug457672.html \

View file

@ -0,0 +1,118 @@
<!DOCTYPE HTML>
<html>
<!--
https://bugzilla.mozilla.org/show_bug.cgi?id=448602
-->
<head>
<title>Test for Bug 448602</title>
<script type="application/javascript" src="/MochiKit/packed.js"></script>
<script type="application/javascript" src="/tests/SimpleTest/SimpleTest.js"></script>
<link rel="stylesheet" type="text/css" href="/tests/SimpleTest/test.css"/>
</head>
<body>
<a target="_blank" href="https://bugzilla.mozilla.org/show_bug.cgi?id=448602">Mozilla Bug 448602</a>
<p id="display"></p>
<div id="content" style="display: none">
</div>
<pre id="test">
<script type="application/javascript">
/** Test for Bug 448602 **/
function runTests() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var els = Components.classes["@mozilla.org/eventlistenerservice;1"]
.getService(Components.interfaces.nsIEventListenerService);
// Event listener info tests
var root = document.getElementById("testroot");
var infos = els.getListenerInfoFor(root, {});
is(infos.length, 0, "Element shouldn't have listeners (1)");
var listenerSource = 'alert(event);';
root.setAttribute("onclick", listenerSource);
infos = els.getListenerInfoFor(root, {});
is(infos.length, 1, "Element should have listeners (1)");
is(infos[0].toSource(), 'function onclick(event) {' + listenerSource + '}',
"Unexpected serialization (1)");
is(infos[0].type, "click", "Wrong type (1)");
is(infos[0].capturing, false, "Wrong phase (1)");
is(infos[0].allowsUntrusted, true, "Should allow untrusted events (1)");
root.removeAttribute("onclick");
infos = els.getListenerInfoFor(root, {});
is(infos.length, 0, "Element shouldn't have listeners (2)");
var l = function (e) { alert(e); };
root.addEventListener("foo", l, true, true);
root.addEventListener("foo", l, false, false);
infos = els.getListenerInfoFor(root, {});
is(infos.length, 2, "Element should have listeners (2)");
is(infos[0].toSource(), "(function (e) {alert(e);})",
"Unexpected serialization (2)");
is(infos[0].type, "foo", "Wrong type (2)");
is(infos[0].capturing, true, "Wrong phase (2)");
is(infos[0].allowsUntrusted, true, "Should allow untrusted events (2)");
is(infos[1].toSource(), "(function (e) {alert(e);})",
"Unexpected serialization (3)");
is(infos[1].type, "foo", "Wrong type (3)");
is(infos[1].capturing, false, "Wrong phase (3)");
is(infos[1].allowsUntrusted, false, "Shouldn't allow untrusted events (1)");
root.removeEventListener("foo", l, true);
root.removeEventListener("foo", l, false);
infos = els.getListenerInfoFor(root, {});
is(infos.length, 0, "Element shouldn't have listeners (3)");
root.onclick = l;
infos = els.getListenerInfoFor(root, {});
is(infos.length, 1, "Element should have listeners (3)");
is(infos[0].toSource(), '(function (e) {alert(e);})',
"Unexpected serialization (4)");
is(infos[0].type, "click", "Wrong type (4)");
is(infos[0].capturing, false, "Wrong phase (4)");
is(infos[0].allowsUntrusted, true, "Should allow untrusted events (3)");
// Event target chain tests
var l2 = document.getElementById("testlevel2");
var l3 = document.getElementById("testlevel3");
var textnode = l3.firstChild;
var chain = els.getEventTargetChainFor(textnode, {});
ok(chain.length > 3, "Too short event target chain.");
is(chain[0], textnode, "Wrong chain item (1)");
is(chain[1], l3, "Wrong chain item (2)");
is(chain[2], l2, "Wrong chain item (3)");
is(chain[3], root, "Wrong chain item (4)");
var hasDocumentInChain = false;
var hasWindowInChain = false;
for (var i = 0; i < chain.length; ++i) {
if (chain[i] == document) {
hasDocumentInChain = true;
} else if (chain[i] == window) {
hasWindowInChain = true;
}
}
ok(hasDocumentInChain, "Should have document in event target chain!");
ok(hasWindowInChain, "Should have window in event target chain!");
SimpleTest.finish();
}
SimpleTest.waitForExplicitFinish();
addLoadEvent(runTests);
</script>
</pre>
<div id="testroot">
<div id="testlevel2">
<div id="testlevel3">
Test
</div>
</div>
</div>
</body>
</html>

View file

@ -465,6 +465,8 @@
#include "nsIDOMNSMouseEvent.h"
#include "nsIEventListenerService.h"
static NS_DEFINE_CID(kDOMSOF_CID, NS_DOM_SCRIPT_OBJECT_FACTORY_CID);
static const char kDOMStringBundleURL[] =
@ -1356,6 +1358,8 @@ static nsDOMClassInfoData sClassInfoData[] = {
NS_DEFINE_CLASSINFO_DATA(ScrollAreaEvent, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
NS_DEFINE_CLASSINFO_DATA(EventListenerInfo, nsDOMGenericSH,
DOM_DEFAULT_SCRIPTABLE_FLAGS)
};
// Objects that shuld be constructable through |new Name();|
@ -3764,6 +3768,10 @@ nsDOMClassInfo::Init()
DOM_CLASSINFO_UI_EVENT_MAP_ENTRIES
DOM_CLASSINFO_MAP_END
DOM_CLASSINFO_MAP_BEGIN(EventListenerInfo, nsIEventListenerInfo)
DOM_CLASSINFO_MAP_ENTRY(nsIEventListenerInfo)
DOM_CLASSINFO_MAP_END
#ifdef NS_DEBUG
{
PRUint32 i = NS_ARRAY_LENGTH(sClassInfoData);

View file

@ -486,6 +486,8 @@ enum nsDOMClassInfoID {
eDOMClassInfo_ScrollAreaEvent_id,
eDOMClassInfo_EventListenerInfo_id,
// This one better be the last one in this list
eDOMClassInfoIDCount
};

View file

@ -45,8 +45,8 @@ class nsIDOMEventListener;
class nsIAtom;
#define NS_IJSEVENTLISTENER_IID \
{ 0xa6cf9118, 0x15b3, 0x11d2, \
{0x93, 0x2e, 0x00, 0x80, 0x5f, 0x8a, 0xdd, 0x32} }
{ 0xe16e7146, 0x109d, 0x4f54, \
{ 0x94, 0x78, 0xda, 0xc4, 0x3a, 0x71, 0x0b, 0x52 } }
// Implemented by script event listeners. Used to retrieve the
// script object corresponding to the event target.
@ -81,6 +81,8 @@ public:
virtual void SetEventName(nsIAtom* aName) = 0;
virtual void ToString(const nsAString& aEventName, nsAString& aResult) = 0;
protected:
virtual ~nsIJSEventListener()
{

View file

@ -51,9 +51,9 @@
#include "nsIMutableArray.h"
#include "nsVariant.h"
#include "nsIDOMBeforeUnloadEvent.h"
#ifdef NS_DEBUG
#include "nsPIDOMEventTarget.h"
#include "nsIJSContextStack.h"
#ifdef NS_DEBUG
#include "nsDOMJSUtils.h"
#include "nspr.h" // PR_fprintf
@ -140,6 +140,35 @@ nsJSEventListener::SetEventName(nsIAtom* aName)
mEventName = aName;
}
void
nsJSEventListener::ToString(const nsAString& aEventName, nsAString& aResult)
{
aResult.Truncate();
nsCOMPtr<nsIThreadJSContextStack> stack = nsContentUtils::ThreadJSContextStack();
nsCOMPtr<nsPIDOMEventTarget> target = do_QueryInterface(mTarget);
if (target && stack && mContext) {
JSContext* cx = static_cast<JSContext*>(mContext->GetNativeContext());
if (cx && NS_SUCCEEDED(stack->Push(cx))) {
JSAutoRequest ar(cx);
nsAutoString eventString = NS_LITERAL_STRING("on") + aEventName;
nsCOMPtr<nsIAtom> atomName = do_GetAtom(eventString);
nsScriptObjectHolder funcval(mContext);
mContext->GetBoundEventHandler(mTarget, mScopeObject, atomName,
funcval);
jsval funval =
OBJECT_TO_JSVAL(static_cast<JSObject*>(static_cast<void*>(funcval)));
JSString* str =
JS_ValueToSource(static_cast<JSContext*>(mContext->GetNativeContext()),
funval);
if (str) {
aResult.Assign(nsDependentJSString(str));
}
stack->Pop(&cx);
}
}
}
nsresult
nsJSEventListener::HandleEvent(nsIDOMEvent* aEvent)
{

View file

@ -65,6 +65,8 @@ public:
// nsIJSEventListener interface
virtual void SetEventName(nsIAtom* aName);
virtual void ToString(const nsAString& aEventName, nsAString& aResult);
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSEventListener,
nsIDOMEventListener)
protected:

View file

@ -100,6 +100,7 @@
#include "nsXULPopupManager.h"
#include "nsFocusManager.h"
#include "nsIEventListenerService.h"
// Transformiix stuff
#include "nsXPathEvaluator.h"
#include "txMozillaXSLTProcessor.h"
@ -423,6 +424,8 @@ nsresult NS_NewXBLService(nsIXBLService** aResult);
nsresult NS_NewContentPolicy(nsIContentPolicy** aResult);
nsresult NS_NewDOMEventGroup(nsIDOMEventGroup** aResult);
nsresult NS_NewEventListenerService(nsIEventListenerService** aResult);
NS_IMETHODIMP NS_NewXULControllers(nsISupports* aOuter, REFNSIID aIID, void** aResult);
#define MAKE_CTOR(ctor_, iface_, func_) \
@ -523,6 +526,7 @@ MAKE_CTOR(CreateXTFService, nsIXTFService, NS_NewXTF
MAKE_CTOR(CreateXMLContentBuilder, nsIXMLContentBuilder, NS_NewXMLContentBuilder)
#endif
MAKE_CTOR(CreateContentDLF, nsIDocumentLoaderFactory, NS_NewContentDocumentLoaderFactory)
MAKE_CTOR(CreateEventListenerService, nsIEventListenerService, NS_NewEventListenerService)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsWyciwygProtocolHandler)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsContentAreaDragDrop)
NS_GENERIC_FACTORY_CONSTRUCTOR(nsDataDocumentContentPolicy)
@ -1443,12 +1447,16 @@ static const nsModuleComponentInfo gComponents[] = {
"@mozilla.org/geolocation/service;1",
nsGeolocationServiceConstructor },
{ "Focus Manager",
NS_FOCUSMANAGER_CID,
"@mozilla.org/focus-manager;1",
CreateFocusManager },
{ "Focus Manager",
NS_FOCUSMANAGER_CID,
"@mozilla.org/focus-manager;1",
CreateFocusManager },
{ "Event Listener Service",
NS_EVENTLISTENERSERVICE_CID,
NS_EVENTLISTENERSERVICE_CONTRACTID,
CreateEventListenerService }
};
NS_IMPL_NSGETMODULE_WITH_CTOR(nsLayoutModule, gComponents, Initialize)