Bug 937317 - When invoking a callback object, restore the incumbent script settings object from when the callback was created. r=bz

See the 'incumbent script' stuff in the WebIDL spec.
This commit is contained in:
Bobby Holley 2013-12-05 21:34:17 -08:00
parent 8ed094280d
commit 8ca6ed8d97
10 changed files with 52 additions and 24 deletions

View File

@ -9,6 +9,7 @@
#include "nsIDocument.h"
#include "prprf.h"
#include "nsGlobalWindow.h"
#include "ScriptSettings.h"
#include "mozilla/Likely.h"
using namespace mozilla;
@ -274,7 +275,7 @@ nsDOMEventTargetHelper::SetEventHandler(nsIAtom* aType,
JSObject* callable;
if (aValue.isObject() &&
JS_ObjectIsCallable(aCx, callable = &aValue.toObject())) {
handler = new EventHandlerNonNull(callable);
handler = new EventHandlerNonNull(callable, mozilla::dom::GetIncumbentGlobal());
}
SetEventHandler(aType, EmptyString(), handler);
return NS_OK;

View File

@ -883,19 +883,23 @@ nsEventListenerManager::CompileEventHandlerInternal(nsListenerStruct *aListenerS
JS::Rooted<JSObject*> scope(cx, listener->GetEventScope());
context->BindCompiledEventHandler(mTarget, scope, handler, &boundHandler);
aListenerStruct = nullptr;
// Note - We pass null for aIncumbentGlobal below. We could also pass the
// compilation global, but since the handler is guaranteed to be scripted,
// there's no need to use an override, since the JS engine will always give
// us the right answer.
if (!boundHandler) {
listener->ForgetHandler();
} else if (listener->EventName() == nsGkAtoms::onerror && win) {
nsRefPtr<OnErrorEventHandlerNonNull> handlerCallback =
new OnErrorEventHandlerNonNull(boundHandler);
new OnErrorEventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr);
listener->SetHandler(handlerCallback);
} else if (listener->EventName() == nsGkAtoms::onbeforeunload && win) {
nsRefPtr<OnBeforeUnloadEventHandlerNonNull> handlerCallback =
new OnBeforeUnloadEventHandlerNonNull(boundHandler);
new OnBeforeUnloadEventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr);
listener->SetHandler(handlerCallback);
} else {
nsRefPtr<EventHandlerNonNull> handlerCallback =
new EventHandlerNonNull(boundHandler);
new EventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr);
listener->SetHandler(handlerCallback);
}
}

View File

@ -322,7 +322,7 @@ nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget,
}
nsRefPtr<EventHandlerNonNull> handlerCallback =
new EventHandlerNonNull(bound);
new EventHandlerNonNull(bound, /* aIncumbentGlobal = */ nullptr);
nsEventHandler eventHandler(handlerCallback);

View File

@ -44,6 +44,7 @@
#include "nsReadableUtils.h"
#include "nsDOMClassInfo.h"
#include "nsJSEnvironment.h"
#include "ScriptSettings.h"
#include "mozilla/Preferences.h"
#include "mozilla/Likely.h"
@ -4938,7 +4939,7 @@ nsGlobalWindow::RequestAnimationFrame(const JS::Value& aCallback,
}
nsRefPtr<FrameRequestCallback> callback =
new FrameRequestCallback(&aCallback.toObject());
new FrameRequestCallback(&aCallback.toObject(), GetIncumbentGlobal());
ErrorResult rv;
*aHandle = RequestAnimationFrame(*callback, rv);
@ -13225,7 +13226,7 @@ nsGlobalWindow::DisableNetworkEvent(uint32_t aType)
JSObject *callable; \
if (v.isObject() && \
JS_ObjectIsCallable(cx, callable = &v.toObject())) { \
handler = new EventHandlerNonNull(callable); \
handler = new EventHandlerNonNull(callable, GetIncumbentGlobal()); \
} \
SetOn##name_(handler); \
return NS_OK; \
@ -13255,7 +13256,7 @@ nsGlobalWindow::DisableNetworkEvent(uint32_t aType)
JSObject *callable; \
if (v.isObject() && \
JS_ObjectIsCallable(cx, callable = &v.toObject())) { \
handler = new OnErrorEventHandlerNonNull(callable); \
handler = new OnErrorEventHandlerNonNull(callable, GetIncumbentGlobal()); \
} \
elm->SetEventHandler(handler); \
return NS_OK; \
@ -13286,7 +13287,7 @@ nsGlobalWindow::DisableNetworkEvent(uint32_t aType)
JSObject *callable; \
if (v.isObject() && \
JS_ObjectIsCallable(cx, callable = &v.toObject())) { \
handler = new OnBeforeUnloadEventHandlerNonNull(callable); \
handler = new OnBeforeUnloadEventHandlerNonNull(callable, GetIncumbentGlobal()); \
} \
elm->SetEventHandler(handler); \
return NS_OK; \

View File

@ -360,7 +360,7 @@ nsJSScriptTimeoutHandler::Init(nsGlobalWindow *aWindow, bool *aIsInterval,
mozilla::HoldJSObjects(this);
mFunction = new Function(funobj);
mFunction = new Function(funobj, GetIncumbentGlobal());
// Create our arg array. argc is the number of arguments passed
// to setTimeout or setInterval; the first two are our callback

View File

@ -25,8 +25,9 @@ namespace dom {
class CallbackFunction : public CallbackObject
{
public:
explicit CallbackFunction(JSObject* aCallable)
: CallbackObject(aCallable)
explicit CallbackFunction(JSObject* aCallable,
nsIGlobalObject* aIncumbentGlobal)
: CallbackObject(aCallable, aIncumbentGlobal)
{
MOZ_ASSERT(JS_ObjectIsCallable(nullptr, mCallback));
}

View File

@ -24,8 +24,9 @@ namespace dom {
class CallbackInterface : public CallbackObject
{
public:
explicit CallbackInterface(JSObject* aCallback)
: CallbackObject(aCallback)
explicit CallbackInterface(JSObject* aCallback,
nsIGlobalObject *aIncumbentGlobal)
: CallbackObject(aCallback, aIncumbentGlobal)
{
}

View File

@ -36,9 +36,11 @@ NS_IMPL_CYCLE_COLLECTION_CLASS(CallbackObject)
NS_IMPL_CYCLE_COLLECTION_UNLINK_BEGIN(CallbackObject)
tmp->DropCallback();
NS_IMPL_CYCLE_COLLECTION_UNLINK(mIncumbentGlobal)
NS_IMPL_CYCLE_COLLECTION_UNLINK_END
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_BEGIN(CallbackObject)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_SCRIPT_OBJECTS
NS_IMPL_CYCLE_COLLECTION_TRAVERSE(mIncumbentGlobal)
NS_IMPL_CYCLE_COLLECTION_TRAVERSE_END
NS_IMPL_CYCLE_COLLECTION_TRACE_BEGIN(CallbackObject)
NS_IMPL_CYCLE_COLLECTION_TRACE_JS_MEMBER_CALLBACK(mCallback)
@ -101,6 +103,9 @@ CallbackObject::CallSetup::CallSetup(CallbackObject* aCallback,
}
mAutoEntryScript.construct(globalObject, mIsMainThread, cx);
if (aCallback->IncumbentGlobalOrNull()) {
mAutoIncumbentScript.construct(aCallback->IncumbentGlobalOrNull());
}
// Unmark the callable (by invoking Callback() and not the CallbackPreserveColor()
// variant), and stick it in a Rooted before it can go gray again.
@ -204,6 +209,7 @@ CallbackObject::CallSetup::~CallSetup()
// But be careful: it might not have been constructed at all!
mAc.destroyIfConstructed();
mAutoIncumbentScript.destroyIfConstructed();
mAutoEntryScript.destroyIfConstructed();
// It is important that this is the last thing we do, after leaving the

View File

@ -46,9 +46,13 @@ public:
NS_DECL_CYCLE_COLLECTING_ISUPPORTS
NS_DECL_CYCLE_COLLECTION_SCRIPT_HOLDER_CLASS(CallbackObject)
explicit CallbackObject(JSObject* aCallback)
// The caller may pass a global object which will act as an override for the
// incumbent script settings object when the callback is invoked (overriding
// the entry point computed from aCallback). If no override is required, the
// caller should pass null.
explicit CallbackObject(JSObject* aCallback, nsIGlobalObject *aIncumbentGlobal)
{
Init(aCallback);
Init(aCallback, aIncumbentGlobal);
}
virtual ~CallbackObject()
@ -77,6 +81,11 @@ public:
return JS::Handle<JSObject*>::fromMarkedLocation(mCallback.address());
}
nsIGlobalObject* IncumbentGlobalOrNull() const
{
return mIncumbentGlobal;
}
enum ExceptionHandling {
// Report any exception and don't throw it to the caller code.
eReportExceptions,
@ -91,17 +100,19 @@ public:
protected:
explicit CallbackObject(CallbackObject* aCallbackObject)
{
Init(aCallbackObject->mCallback);
Init(aCallbackObject->mCallback, aCallbackObject->mIncumbentGlobal);
}
private:
inline void Init(JSObject* aCallback)
inline void Init(JSObject* aCallback, nsIGlobalObject* aIncumbentGlobal)
{
MOZ_ASSERT(aCallback && !mCallback);
// Set mCallback before we hold, on the off chance that a GC could somehow
// happen in there... (which would be pretty odd, granted).
mCallback = aCallback;
mozilla::HoldJSObjects(this);
mIncumbentGlobal = aIncumbentGlobal;
}
CallbackObject(const CallbackObject&) MOZ_DELETE;
@ -117,6 +128,7 @@ protected:
}
JS::Heap<JSObject*> mCallback;
nsCOMPtr<nsIGlobalObject> mIncumbentGlobal;
class MOZ_STACK_CLASS CallSetup
{
@ -154,6 +166,7 @@ protected:
// And now members whose construction/destruction order we need to control.
Maybe<AutoEntryScript> mAutoEntryScript;
Maybe<AutoIncumbentScript> mAutoIncumbentScript;
// Constructed the rooter within the scope of mCxPusher above, so that it's
// always within a request during its lifetime.
@ -356,7 +369,8 @@ public:
JSAutoCompartment ac(cx, obj);
nsRefPtr<WebIDLCallbackT> newCallback = new WebIDLCallbackT(obj);
// XXXbholley - This goes away in the next patch.
nsRefPtr<WebIDLCallbackT> newCallback = new WebIDLCallbackT(obj, /* aIncumbentGlobal = */ nullptr);
return newCallback.forget();
}

View File

@ -3343,7 +3343,7 @@ for (uint32_t i = 0; i < length; ++i) {
else:
declType = CGGeneric("OwningNonNull<%s>" % name)
conversion = (
"${declName} = new %s(&${val}.toObject());\n" % name)
"${declName} = new %s(&${val}.toObject(), mozilla::dom::GetIncumbentGlobal());\n" % name)
template = wrapObjectTemplate(conversion, type,
"${declName} = nullptr",
@ -3676,7 +3676,7 @@ for (uint32_t i = 0; i < length; ++i) {
else:
declType = CGGeneric("OwningNonNull<%s>" % name)
conversion = (
" ${declName} = new %s(&${val}.toObject());\n" % name)
" ${declName} = new %s(&${val}.toObject(), mozilla::dom::GetIncumbentGlobal());\n" % name)
if allowTreatNonCallableAsNull and type.treatNonCallableAsNull():
haveCallable = "JS_ObjectIsCallable(cx, &${val}.toObject())"
@ -10421,7 +10421,7 @@ class CGJSImplClass(CGBindingImplClass):
decorators = "MOZ_FINAL"
destructor = None
baseConstructors=["mImpl(new %s(aJSImplObject))" % jsImplName(descriptor.name),
baseConstructors=["mImpl(new %s(aJSImplObject, /* aIncumbentGlobal = */ nullptr))" % jsImplName(descriptor.name),
"mParent(aParent)"]
parentInterface = descriptor.interface.parent
while parentInterface:
@ -10548,12 +10548,12 @@ class CGCallback(CGClass):
def getConstructors(self):
return [ClassConstructor(
[Argument("JSObject*", "aCallback")],
[Argument("JSObject*", "aCallback"), Argument("nsIGlobalObject*", "aIncumbentGlobal")],
bodyInHeader=True,
visibility="public",
explicit=True,
baseConstructors=[
"%s(aCallback)" % self.baseName
"%s(aCallback, aIncumbentGlobal)" % self.baseName,
])]
def getMethodImpls(self, method):