Bug 807226 part 4. Allow event handlers to have a null nsIScriptContext if they won't have to compile from a string. r=smaug

This commit is contained in:
Boris Zbarsky 2012-11-09 08:00:25 -08:00
parent d15bb45f3e
commit 2daa94afcd
8 changed files with 43 additions and 71 deletions

View File

@ -2035,8 +2035,7 @@ nsINode::SizeOfExcludingThis(nsMallocSizeOfFun aMallocSizeOf) const
/* Just silently do nothing */ \
return NS_OK; \
} \
return elm->SetEventHandlerToJsval(nsGkAtoms::on##name_, cx, obj, v, \
true); \
return elm->SetEventHandlerToJsval(nsGkAtoms::on##name_, cx, obj, v); \
}
#define TOUCH_EVENT EVENT
#define DOCUMENT_ONLY_EVENT EVENT

View File

@ -238,8 +238,7 @@ nsDOMEventTargetHelper::SetEventHandler(nsIAtom* aType,
return NS_OK;
}
return elm->SetEventHandlerToJsval(aType, aCx, obj, aValue,
HasOrHasHadOwner());
return elm->SetEventHandlerToJsval(aType, aCx, obj, aValue);
}
void

View File

@ -522,14 +522,14 @@ nsEventListenerManager::FindEventHandler(uint32_t aEventType,
nsresult
nsEventListenerManager::SetEventHandlerInternal(nsIScriptContext *aContext,
JSContext* aCx,
JSObject* aScopeObject,
nsIAtom* aName,
const nsEventHandler& aHandler,
bool aPermitUntrustedEvents,
nsListenerStruct **aListenerStruct)
{
NS_ASSERTION(aContext || aCx, "Must have one or the other!");
NS_ASSERTION(aContext || aHandler.HasEventHandler(),
"Must have one or the other!");
nsresult rv = NS_OK;
uint32_t eventType = nsContentUtils::GetEventId(aName);
@ -538,53 +538,23 @@ nsEventListenerManager::SetEventHandlerInternal(nsIScriptContext *aContext,
if (!ls) {
// If we didn't find a script listener or no listeners existed
// create and add a new one.
const uint32_t flags = NS_EVENT_FLAG_BUBBLE |
(aContext ? NS_PRIV_EVENT_FLAG_SCRIPT : 0);
nsCOMPtr<nsIDOMEventListener> listener;
const uint32_t flags = NS_EVENT_FLAG_BUBBLE | NS_PRIV_EVENT_FLAG_SCRIPT;
if (aContext) {
nsCOMPtr<nsIJSEventListener> scriptListener;
rv = NS_NewJSEventListener(aContext, aScopeObject, mTarget, aName,
aHandler, getter_AddRefs(scriptListener));
listener = scriptListener.forget();
} else {
// If we don't have a script context, we're setting an event handler from
// a component or other odd scope. Ask XPConnect if it can make us an
// nsIDOMEventListener.
MOZ_ASSERT(aHandler.HasEventHandler());
rv = nsContentUtils::XPConnect()->WrapJS(aCx,
aHandler.Ptr()->Callable(),
NS_GET_IID(nsIDOMEventListener),
getter_AddRefs(listener));
}
nsCOMPtr<nsIJSEventListener> scriptListener;
rv = NS_NewJSEventListener(aContext, aScopeObject, mTarget, aName,
aHandler, getter_AddRefs(scriptListener));
if (NS_SUCCEEDED(rv)) {
AddEventListener(listener, eventType, aName, flags, true);
AddEventListener(scriptListener, eventType, aName, flags, true);
ls = FindEventHandler(eventType, aName);
}
} else {
// Don't mix 'real' JS event handlers and 'fake' JS event handlers.
nsIJSEventListener* scriptListener = ls->GetJSListener();
MOZ_ASSERT(scriptListener,
"How can we have an event handler with no nsIJSEventListener?");
if ((!aContext && scriptListener) ||
(aContext && !scriptListener)) {
return NS_ERROR_FAILURE;
}
if (scriptListener) {
scriptListener->SetHandler(aHandler);
} else {
MOZ_ASSERT(aHandler.HasEventHandler());
nsCOMPtr<nsIDOMEventListener> listener;
rv = nsContentUtils::XPConnect()->WrapJS(aCx,
aHandler.Ptr()->Callable(),
NS_GET_IID(nsIDOMEventListener),
getter_AddRefs(listener));
if (NS_SUCCEEDED(rv)) {
ls->mListener = listener.forget();
}
}
scriptListener->SetHandler(aHandler, aContext, aScopeObject);
}
if (NS_SUCCEEDED(rv) && ls) {
@ -710,7 +680,7 @@ nsEventListenerManager::SetEventHandler(nsIAtom *aName,
JSObject* scope = global->GetGlobalJSObject();
nsListenerStruct *ls;
rv = SetEventHandlerInternal(context, nullptr, scope, aName, nsEventHandler(),
rv = SetEventHandlerInternal(context, scope, aName, nsEventHandler(),
aPermitUntrustedEvents, &ls);
NS_ENSURE_SUCCESS(rv, rv);
@ -1194,8 +1164,7 @@ nsresult
nsEventListenerManager::SetEventHandlerToJsval(nsIAtom* aEventName,
JSContext* cx,
JSObject* aScope,
const jsval& v,
bool aExpectScriptContext)
const jsval& v)
{
JSObject *callable;
if (JSVAL_IS_PRIMITIVE(v) ||
@ -1234,17 +1203,12 @@ nsEventListenerManager::SetEventHandlerToJsval(nsIAtom* aEventName,
handler.SetHandler(handlerCallback);
}
// We might not have a script context, e.g. if we're setting a listener
// on a dead Window.
nsIScriptContext *context = nsJSUtils::GetStaticScriptContext(aScope);
NS_ENSURE_TRUE(context || !aExpectScriptContext, NS_ERROR_FAILURE);
JSAutoCompartment ac(cx, aScope);
JSObject *scope = ::JS_GetGlobalForObject(cx, aScope);
// Untrusted events are always permitted for non-chrome script
// handlers.
nsListenerStruct *ignored;
return SetEventHandlerInternal(context, cx, scope, aEventName, handler,
return SetEventHandlerInternal(nullptr, scope, aEventName, handler,
!nsContentUtils::IsCallerChrome(), &ignored);
}

View File

@ -263,11 +263,11 @@ protected:
/**
* Set the "inline" event listener for aName to aHandler. aHandler may be
* have no actual handler set to indicate that we should lazily get and
* compile the string for this listener. The nsListenerStruct that results,
* if any, is returned in aListenerStruct.
* compile the string for this listener, but in that case aContext must be
* non-null. Otherwise, aContext is allowed to be null. The nsListenerStruct
* that results, if any, is returned in aListenerStruct.
*/
nsresult SetEventHandlerInternal(nsIScriptContext *aContext,
JSContext* aCx,
JSObject* aScopeGlobal,
nsIAtom* aName,
const nsEventHandler& aHandler,
@ -284,12 +284,10 @@ public:
* might actually remove the event listener, depending on the value
* of |v|. Note that on entry to this function cx and aScope might
* not be in the same compartment, though cx and v are guaranteed to
* be in the same compartment. If aExpectScriptContext is false,
* not finding an nsIScriptContext does not cause failure.
* be in the same compartment.
*/
nsresult SetEventHandlerToJsval(nsIAtom* aEventName, JSContext* cx,
JSObject* aScope, const jsval& v,
bool aExpectScriptContext);
JSObject* aScope, const jsval& v);
/**
* Get the value of the "inline" event listener for aEventName.
* This may cause lazy compilation if the listener is uncompiled.

View File

@ -309,7 +309,7 @@ nsXBLPrototypeHandler::ExecuteHandler(nsIDOMEventTarget* aTarget,
// Execute it.
nsCOMPtr<nsIJSEventListener> eventListener;
rv = NS_NewJSEventListener(boundContext, scope,
rv = NS_NewJSEventListener(nullptr, scope,
scriptTarget, onEventAtom,
eventHandler,
getter_AddRefs(eventListener));

View File

@ -11209,8 +11209,7 @@ nsGlobalWindow::DisableNetworkEvent(uint32_t aType)
if (!obj) { \
return NS_ERROR_UNEXPECTED; \
} \
return elm->SetEventHandlerToJsval(nsGkAtoms::on##name_, cx, obj, v, \
true); \
return elm->SetEventHandlerToJsval(nsGkAtoms::on##name_, cx, obj, v); \
}
#define WINDOW_ONLY_EVENT EVENT
#define TOUCH_EVENT EVENT

View File

@ -176,6 +176,7 @@ public:
mTarget = base.get();
}
// Can return null if we already have a handler.
nsIScriptContext *GetEventContext() const
{
return mContext;
@ -208,9 +209,12 @@ public:
// Set a handler for this event listener. The handler must already
// be bound to the right target.
void SetHandler(const nsEventHandler& aHandler)
void SetHandler(const nsEventHandler& aHandler, nsIScriptContext* aContext,
JSObject* aScopeObject)
{
mHandler.SetHandler(aHandler);
mContext = aContext;
mScopeObject = aScopeObject;
}
void SetHandler(mozilla::dom::EventHandlerNonNull* aHandler)
{
@ -260,7 +264,9 @@ protected:
NS_DEFINE_STATIC_IID_ACCESSOR(nsIJSEventListener, NS_IJSEVENTLISTENER_IID)
/* factory function. aHandler must already be bound to aTarget */
/* factory function. aHandler must already be bound to aTarget.
aContext is allowed to be null if aHandler is already set up.
*/
nsresult NS_NewJSEventListener(nsIScriptContext *aContext,
JSObject* aScopeObject, nsISupports* aTarget,
nsIAtom* aType, const nsEventHandler& aHandler,

View File

@ -55,21 +55,21 @@ nsJSEventListener::nsJSEventListener(nsIScriptContext *aContext,
{
// aScopeObject is the inner window's JS object, which we need to lock
// until we are done with it.
NS_ASSERTION(aScopeObject && aContext,
NS_ASSERTION(aScopeObject,
"EventListener with no context or scope?");
NS_HOLD_JS_OBJECTS(this, nsJSEventListener);
}
nsJSEventListener::~nsJSEventListener()
{
if (mContext) {
if (mScopeObject) {
NS_DROP_JS_OBJECTS(this, nsJSEventListener);
}
}
NS_IMPL_CYCLE_COLLECTION_CLASS(nsJSEventListener)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(nsJSEventListener)
if (tmp->mContext) {
if (tmp->mScopeObject) {
NS_DROP_JS_OBJECTS(tmp, nsJSEventListener);
tmp->mScopeObject = nullptr;
NS_IMPL_CYCLE_COLLECTION_UNLINK_NSCOMPTR(mContext)
@ -134,10 +134,15 @@ NS_IMPL_CYCLE_COLLECTING_RELEASE(nsJSEventListener)
bool
nsJSEventListener::IsBlackForCC()
{
if (mContext &&
(!mScopeObject || !xpc_IsGrayGCThing(mScopeObject)) &&
// We can claim to be black if all the things we reference are
// effectively black already.
if ((!mScopeObject || !xpc_IsGrayGCThing(mScopeObject)) &&
(!mHandler.HasEventHandler() ||
!mHandler.Ptr()->HasGrayCallable())) {
if (!mContext) {
// Well, we certainly won't be marking it, so move on!
return true;
}
nsIScriptGlobalObject* sgo =
static_cast<nsJSContext*>(mContext.get())->GetCachedGlobalObject();
return sgo && sgo->IsBlackForCC();
@ -149,7 +154,7 @@ nsresult
nsJSEventListener::HandleEvent(nsIDOMEvent* aEvent)
{
nsCOMPtr<nsIDOMEventTarget> target = do_QueryInterface(mTarget);
if (!target || !mContext || !mHandler.HasEventHandler())
if (!target || !mHandler.HasEventHandler())
return NS_ERROR_FAILURE;
if (mHandler.Type() == nsEventHandler::eOnError) {
@ -256,6 +261,8 @@ NS_NewJSEventListener(nsIScriptContext* aContext, JSObject* aScopeObject,
const nsEventHandler& aHandler,
nsIJSEventListener** aReturn)
{
MOZ_ASSERT(aContext || aHandler.HasEventHandler(),
"Must have a handler if we don't have an nsIScriptContext");
NS_ENSURE_ARG(aEventType);
nsJSEventListener* it =
new nsJSEventListener(aContext, aScopeObject, aTarget, aEventType,