From 8ca6ed8d971f9cfa130fe70b10f937f7e06b3427 Mon Sep 17 00:00:00 2001 From: Bobby Holley Date: Thu, 5 Dec 2013 21:34:17 -0800 Subject: [PATCH] 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. --- content/events/src/nsDOMEventTargetHelper.cpp | 3 ++- content/events/src/nsEventListenerManager.cpp | 10 +++++--- content/xbl/src/nsXBLPrototypeHandler.cpp | 2 +- dom/base/nsGlobalWindow.cpp | 9 +++---- dom/base/nsJSTimeoutHandler.cpp | 2 +- dom/bindings/CallbackFunction.h | 5 ++-- dom/bindings/CallbackInterface.h | 5 ++-- dom/bindings/CallbackObject.cpp | 6 +++++ dom/bindings/CallbackObject.h | 24 +++++++++++++++---- dom/bindings/Codegen.py | 10 ++++---- 10 files changed, 52 insertions(+), 24 deletions(-) diff --git a/content/events/src/nsDOMEventTargetHelper.cpp b/content/events/src/nsDOMEventTargetHelper.cpp index 800c82ab017..a54519a6b67 100644 --- a/content/events/src/nsDOMEventTargetHelper.cpp +++ b/content/events/src/nsDOMEventTargetHelper.cpp @@ -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; diff --git a/content/events/src/nsEventListenerManager.cpp b/content/events/src/nsEventListenerManager.cpp index 928b3760a2e..bcdda331095 100644 --- a/content/events/src/nsEventListenerManager.cpp +++ b/content/events/src/nsEventListenerManager.cpp @@ -883,19 +883,23 @@ nsEventListenerManager::CompileEventHandlerInternal(nsListenerStruct *aListenerS JS::Rooted 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 handlerCallback = - new OnErrorEventHandlerNonNull(boundHandler); + new OnErrorEventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr); listener->SetHandler(handlerCallback); } else if (listener->EventName() == nsGkAtoms::onbeforeunload && win) { nsRefPtr handlerCallback = - new OnBeforeUnloadEventHandlerNonNull(boundHandler); + new OnBeforeUnloadEventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr); listener->SetHandler(handlerCallback); } else { nsRefPtr handlerCallback = - new EventHandlerNonNull(boundHandler); + new EventHandlerNonNull(boundHandler, /* aIncumbentGlobal = */ nullptr); listener->SetHandler(handlerCallback); } } diff --git a/content/xbl/src/nsXBLPrototypeHandler.cpp b/content/xbl/src/nsXBLPrototypeHandler.cpp index 995b96f21a2..325ec37ec43 100644 --- a/content/xbl/src/nsXBLPrototypeHandler.cpp +++ b/content/xbl/src/nsXBLPrototypeHandler.cpp @@ -322,7 +322,7 @@ nsXBLPrototypeHandler::ExecuteHandler(EventTarget* aTarget, } nsRefPtr handlerCallback = - new EventHandlerNonNull(bound); + new EventHandlerNonNull(bound, /* aIncumbentGlobal = */ nullptr); nsEventHandler eventHandler(handlerCallback); diff --git a/dom/base/nsGlobalWindow.cpp b/dom/base/nsGlobalWindow.cpp index 81622c25cf1..ccdce6a557b 100644 --- a/dom/base/nsGlobalWindow.cpp +++ b/dom/base/nsGlobalWindow.cpp @@ -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 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; \ diff --git a/dom/base/nsJSTimeoutHandler.cpp b/dom/base/nsJSTimeoutHandler.cpp index 8fa5d843813..57448916e17 100644 --- a/dom/base/nsJSTimeoutHandler.cpp +++ b/dom/base/nsJSTimeoutHandler.cpp @@ -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 diff --git a/dom/bindings/CallbackFunction.h b/dom/bindings/CallbackFunction.h index 0ad0892bcc5..a065ce27c9b 100644 --- a/dom/bindings/CallbackFunction.h +++ b/dom/bindings/CallbackFunction.h @@ -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)); } diff --git a/dom/bindings/CallbackInterface.h b/dom/bindings/CallbackInterface.h index 574d4243f7a..5555d083079 100644 --- a/dom/bindings/CallbackInterface.h +++ b/dom/bindings/CallbackInterface.h @@ -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) { } diff --git a/dom/bindings/CallbackObject.cpp b/dom/bindings/CallbackObject.cpp index 4e13fe95ab8..03ae43bfa0f 100644 --- a/dom/bindings/CallbackObject.cpp +++ b/dom/bindings/CallbackObject.cpp @@ -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 diff --git a/dom/bindings/CallbackObject.h b/dom/bindings/CallbackObject.h index 7a34a84723d..0f4797ebe5f 100644 --- a/dom/bindings/CallbackObject.h +++ b/dom/bindings/CallbackObject.h @@ -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::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 mCallback; + nsCOMPtr mIncumbentGlobal; class MOZ_STACK_CLASS CallSetup { @@ -154,6 +166,7 @@ protected: // And now members whose construction/destruction order we need to control. Maybe mAutoEntryScript; + Maybe 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 newCallback = new WebIDLCallbackT(obj); + // XXXbholley - This goes away in the next patch. + nsRefPtr newCallback = new WebIDLCallbackT(obj, /* aIncumbentGlobal = */ nullptr); return newCallback.forget(); } diff --git a/dom/bindings/Codegen.py b/dom/bindings/Codegen.py index a1529e322cc..54387c973db 100644 --- a/dom/bindings/Codegen.py +++ b/dom/bindings/Codegen.py @@ -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):