Bug 506961 - Add a method to get jsdIValue from JS implemented event listeners, r=bz

--HG--
extra : rebase_source : 93d446c1c94512be8cc0dc208c0bf8f1cd3cc936
This commit is contained in:
Olli Pettay 2009-10-17 17:40:44 +03:00
parent 547f5923a6
commit 79701a2085
10 changed files with 151 additions and 55 deletions

View File

@ -43,7 +43,7 @@ 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)]
[scriptable, uuid(cbc2ea6e-4043-4435-ba8f-64cbf6638622)]
interface nsIEventListenerInfo : nsISupports
{
/**
@ -59,6 +59,12 @@ interface nsIEventListenerInfo : nsISupports
* (for example with C++ listeners).
*/
AString toSource();
/**
* If jsdIDebuggerService is active and the listener is implemented in JS,
* this returns the listener as a jsdIValue. Otherwise null.
*/
nsISupports getDebugObject();
};
[scriptable, uuid(551cac0f-31ed-45e0-8d67-bc0d6e117b31)]

View File

@ -100,3 +100,9 @@ LOCAL_INCLUDES = \
$(NULL)
DEFINES += -D_IMPL_NS_LAYOUT
ifdef MOZ_JSDEBUGGER
DEFINES += -DMOZ_JSDEBUGGER
endif

View File

@ -51,6 +51,9 @@
#include "nsGUIEvent.h"
#include "nsEventDispatcher.h"
#include "nsIJSEventListener.h"
#ifdef MOZ_JSDEBUGGER
#include "jsdIDebuggerService.h"
#endif
NS_IMPL_CYCLE_COLLECTION_1(nsEventListenerInfo, mListener)
@ -93,37 +96,85 @@ nsEventListenerInfo::GetInSystemEventGroup(PRBool* aInSystemEventGroup)
NS_IMPL_ISUPPORTS1(nsEventListenerService, nsIEventListenerService)
NS_IMETHODIMP
nsEventListenerInfo::ToSource(nsAString& aResult)
// Caller must root *aJSVal!
PRBool
nsEventListenerInfo::GetJSVal(jsval* aJSVal)
{
aResult.SetIsVoid(PR_TRUE);
*aJSVal = JSVAL_NULL;
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);
*aJSVal = OBJECT_TO_JSVAL(object);
return PR_TRUE;
}
nsCOMPtr<nsIJSEventListener> jsl = do_QueryInterface(mListener);
if (jsl) {
nsresult rv = jsl->GetJSVal(mType, aJSVal);
if (NS_SUCCEEDED(rv)) {
return PR_TRUE;
}
}
return PR_FALSE;
}
NS_IMETHODIMP
nsEventListenerInfo::ToSource(nsAString& aResult)
{
aResult.SetIsVoid(PR_TRUE);
nsresult rv;
jsval v = JSVAL_NULL;
nsAutoGCRoot root(&v, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (GetJSVal(&v)) {
nsCOMPtr<nsIThreadJSContextStack> stack =
nsContentUtils::ThreadJSContextStack();
if (stack) {
JSContext* cx = nsnull;
stack->GetSafeJSContext(&cx);
if (cx && NS_SUCCEEDED(stack->Push(cx))) {
JSAutoRequest ar(cx);
JSString* str = JS_ValueToSource(cx, v);
if (str) {
aResult.Assign(nsDependentJSString(str));
}
stack->Pop(&cx);
}
}
}
return NS_OK;
}
NS_IMETHODIMP
nsEventListenerInfo::GetDebugObject(nsISupports** aRetVal)
{
*aRetVal = nsnull;
#ifdef MOZ_JSDEBUGGER
nsresult rv = NS_OK;
jsval v = JSVAL_NULL;
nsAutoGCRoot root(&v, &rv);
NS_ENSURE_SUCCESS(rv, rv);
if (GetJSVal(&v)) {
nsCOMPtr<jsdIDebuggerService> jsd =
do_GetService("@mozilla.org/js/jsd/debugger-service;1", &rv);
NS_ENSURE_SUCCESS(rv, NS_OK);
PRBool isOn = PR_FALSE;
jsd->GetIsOn(&isOn);
NS_ENSURE_TRUE(isOn, NS_OK);
nsCOMPtr<jsdIValue> jsdValue;
jsd->WrapJSValue(v, getter_AddRefs(jsdValue));
*aRetVal = jsdValue.forget().get();
return NS_OK;
}
#endif
return NS_OK;
}

View File

@ -44,6 +44,8 @@
#include "nsString.h"
#include "nsPIDOMEventTarget.h"
#include "nsCycleCollectionParticipant.h"
#include "jsapi.h"
class nsEventListenerInfo : public nsIEventListenerInfo
{
@ -59,6 +61,8 @@ public:
NS_DECL_CYCLE_COLLECTION_CLASS(nsEventListenerInfo)
NS_DECL_NSIEVENTLISTENERINFO
protected:
PRBool GetJSVal(jsval* aJSVal);
nsString mType;
// nsReftPtr because that is what nsListenerStruct uses too.
nsRefPtr<nsIDOMEventListener> mListener;

View File

@ -23,6 +23,9 @@ https://bugzilla.mozilla.org/show_bug.cgi?id=448602
function runTests() {
netscape.security.PrivilegeManager.enablePrivilege("UniversalXPConnect");
var jsdIDebuggerService = Components.interfaces.jsdIDebuggerService;
var jsd = Components.classes['@mozilla.org/js/jsd/debugger-service;1']
.getService(jsdIDebuggerService);
var els = Components.classes["@mozilla.org/eventlistenerservice;1"]
.getService(Components.interfaces.nsIEventListenerService);
@ -41,6 +44,16 @@ function runTests() {
is(infos[0].capturing, false, "Wrong phase (1)");
is(infos[0].allowsUntrusted, true, "Should allow untrusted events (1)");
var jsdOn = jsd.isOn;
if (!jsdOn) {
is(infos[0].getDebugObject(), null,
"If JSD isn't running, getDebugObject() should return null.")
jsd.on();
ok(jsd.isOn, "JSD should be running.");
}
var jsdvalue = infos[0].getDebugObject().QueryInterface(Components.interfaces.jsdIValue);
is(jsdvalue.jsType, 3, "Event listener should be a function! (1)");
root.removeAttribute("onclick");
infos = els.getListenerInfoFor(root, {});
is(infos.length, 0, "Element shouldn't have listeners (2)");
@ -55,12 +68,19 @@ function runTests() {
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)");
jsdvalue = infos[0].getDebugObject().QueryInterface(Components.interfaces.jsdIValue);
is(jsdvalue.jsType, 3, "Event listener should be a function!(2)");
is(jsdvalue.getWrappedValue(), l, "Wrong JS value! (1)");
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)");
jsdvalue2 = infos[1].getDebugObject().QueryInterface(Components.interfaces.jsdIValue);
is(jsdvalue2.jsType, 3, "Event listener should be a function! (3)");
is(jsdvalue2.getWrappedValue(), l, "Wrong JS value! (2)");
root.removeEventListener("foo", l, true);
root.removeEventListener("foo", l, false);
infos = els.getListenerInfoFor(root, {});
@ -99,6 +119,12 @@ function runTests() {
ok(hasDocumentInChain, "Should have document in event target chain!");
ok(hasWindowInChain, "Should have window in event target chain!");
if (!jsdOn) {
jsd.off();
ok(!jsd.isOn, "JSD shouldn't be running anymore.");
}
SimpleTest.finish();
}

View File

@ -39,14 +39,15 @@
#define nsIJSEventListener_h__
#include "nsIScriptContext.h"
#include "jsapi.h"
class nsIScriptObjectOwner;
class nsIDOMEventListener;
class nsIAtom;
#define NS_IJSEVENTLISTENER_IID \
{ 0xe16e7146, 0x109d, 0x4f54, \
{ 0x94, 0x78, 0xda, 0xc4, 0x3a, 0x71, 0x0b, 0x52 } }
{ 0x8b4f3ad1, 0x1c2a, 0x43f0, \
{ 0xac, 0x6c, 0x83, 0x33, 0xe9, 0xe1, 0xcb, 0x7e } }
// Implemented by script event listeners. Used to retrieve the
// script object corresponding to the event target.
@ -81,7 +82,7 @@ public:
virtual void SetEventName(nsIAtom* aName) = 0;
virtual void ToString(const nsAString& aEventName, nsAString& aResult) = 0;
virtual nsresult GetJSVal(const nsAString& aEventName, jsval* aJSVal) = 0;
protected:
virtual ~nsIJSEventListener()

View File

@ -140,33 +140,23 @@ nsJSEventListener::SetEventName(nsIAtom* aName)
mEventName = aName;
}
void
nsJSEventListener::ToString(const nsAString& aEventName, nsAString& aResult)
nsresult
nsJSEventListener::GetJSVal(const nsAString& aEventName, jsval* aJSVal)
{
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);
}
if (target && mContext) {
nsAutoString eventString = NS_LITERAL_STRING("on") + aEventName;
nsCOMPtr<nsIAtom> atomName = do_GetAtom(eventString);
nsScriptObjectHolder funcval(mContext);
nsresult rv = mContext->GetBoundEventHandler(mTarget, mScopeObject,
atomName, funcval);
NS_ENSURE_SUCCESS(rv, rv);
jsval funval =
OBJECT_TO_JSVAL(static_cast<JSObject*>(static_cast<void*>(funcval)));
*aJSVal = funval;
return NS_OK;
}
return NS_ERROR_FAILURE;
}
nsresult

View File

@ -65,7 +65,7 @@ public:
// nsIJSEventListener interface
virtual void SetEventName(nsIAtom* aName);
virtual void ToString(const nsAString& aEventName, nsAString& aResult);
virtual nsresult GetJSVal(const nsAString& aEventName, jsval* aJSVal);
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS_AMBIGUOUS(nsJSEventListener,
nsIDOMEventListener)

View File

@ -53,6 +53,7 @@
[ptr] native JSDValue(JSDValue);
[ptr] native JSRuntime(JSRuntime);
[ptr] native JSContext(JSContext);
native JSVal(jsval);
/* interfaces we declare in this file */
interface jsdIDebuggerService;
@ -77,7 +78,7 @@ interface jsdIProperty;
* Debugger service. It's not a good idea to have more than one active client of
* the debugger service.
*/
[scriptable, uuid(e86c0173-49e2-48f3-b53f-b5b2691a7e45)]
[scriptable, uuid(dc0a24db-f8ac-4889-80d0-6016545a2dda)]
interface jsdIDebuggerService : nsISupports
{
/** Internal use only. */
@ -359,6 +360,11 @@ interface jsdIDebuggerService : nsISupports
* defined error code.
*/
jsdIValue wrapValue (/*in jsvalue value*/);
/**
* The same as above but to be called from C++.
*/
[noscript] jsdIValue wrapJSValue (in JSVal value);
/* XXX these two routines are candidates for refactoring. The only problem
* is that it is not clear where and how they should land.

View File

@ -2938,7 +2938,13 @@ jsdService::WrapValue(jsdIValue **_rval)
if (NS_FAILED(rv))
return rv;
JSDValue *jsdv = JSD_NewValue (mCx, argv[0]);
return WrapJSValue(argv[0], _rval);
}
NS_IMETHODIMP
jsdService::WrapJSValue(jsval value, jsdIValue** _rval)
{
JSDValue *jsdv = JSD_NewValue(mCx, value);
if (!jsdv)
return NS_ERROR_FAILURE;