Bug 901291 - Get WebIDL callbacks working on Workers. r=khuey

This commit is contained in:
Nikhil Marathe 2013-09-09 14:58:29 -07:00
parent f7153b838b
commit 5e1c3cf276
6 changed files with 97 additions and 47 deletions

View File

@ -1534,6 +1534,7 @@ public:
static JSContext *GetCurrentJSContext();
static JSContext *GetSafeJSContext();
static JSContext *GetDefaultJSContextForThread();
/**
* Case insensitive comparison between two strings. However it only ignores

View File

@ -1766,6 +1766,7 @@ namespace mozilla {
namespace dom {
namespace workers {
extern bool IsCurrentThreadRunningChromeWorker();
extern JSContext* GetCurrentThreadJSContext();
}
}
}
@ -5197,6 +5198,17 @@ nsContentUtils::GetSafeJSContext()
return sXPConnect->GetSafeJSContext();
}
/* static */
JSContext *
nsContentUtils::GetDefaultJSContextForThread()
{
if (MOZ_LIKELY(NS_IsMainThread())) {
return GetSafeJSContext();
} else {
return workers::GetCurrentThreadJSContext();
}
}
/* static */
nsresult
nsContentUtils::ASCIIToLower(nsAString& aStr)

View File

@ -673,6 +673,28 @@ NativeInterface2JSObjectAndThrowIfFailed(JSContext* aCx,
bool aAllowNativeWrapper)
{
nsresult rv;
// Inline some logic from XPCConvert::NativeInterfaceToJSObject that we need
// on all threads.
nsWrapperCache *cache = aHelper.GetWrapperCache();
if (cache && cache->IsDOMBinding()) {
JS::RootedObject obj(aCx, cache->GetWrapper());
if (!obj) {
obj = cache->WrapObject(aCx, aScope);
}
if (obj && aAllowNativeWrapper && !JS_WrapObject(aCx, obj.address())) {
return false;
}
if (obj) {
*aRetval = JS::ObjectValue(*obj);
return true;
}
}
MOZ_ASSERT(NS_IsMainThread());
if (!XPCConvert::NativeInterface2JSObject(aRetval, NULL, aHelper, aIID,
NULL, aAllowNativeWrapper, &rv)) {
// I can't tell if NativeInterface2JSObject throws JS exceptions

View File

@ -17,6 +17,7 @@
#include "nsCxPusher.h"
#include "nsIScriptSecurityManager.h"
#include "xpcprivate.h"
#include "WorkerPrivate.h"
namespace mozilla {
namespace dom {
@ -49,7 +50,11 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
, mCompartment(aCompartment)
, mErrorResult(aRv)
, mExceptionHandling(aExceptionHandling)
, mIsMainThread(NS_IsMainThread())
{
if (mIsMainThread) {
nsContentUtils::EnterMicroTask();
}
// We need to produce a useful JSContext here. Ideally one that the callback
// is in some sense associated with, so that we can sort of treat it as a
// "script entry point". Though once we actually have script entry points,
@ -58,46 +63,50 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
// First, find the real underlying callback.
JSObject* realCallback = js::UncheckedUnwrap(aCallback);
// Now get the nsIScriptGlobalObject for this callback.
JSContext* cx = nullptr;
nsIScriptContext* ctx = nullptr;
nsIScriptGlobalObject* sgo = nsJSUtils::GetStaticScriptGlobal(realCallback);
if (sgo) {
// Make sure that if this is a window it's the current inner, since the
// nsIScriptContext and hence JSContext are associated with the outer
// window. Which means that if someone holds on to a function from a
// now-unloaded document we'd have the new document as the script entry
// point...
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(sgo);
if (win) {
MOZ_ASSERT(win->IsInnerWindow());
nsPIDOMWindow* outer = win->GetOuterWindow();
if (!outer || win != outer->GetCurrentInnerWindow()) {
// Just bail out from here
return;
if (mIsMainThread) {
// Now get the nsIScriptGlobalObject for this callback.
nsIScriptContext* ctx = nullptr;
nsIScriptGlobalObject* sgo = nsJSUtils::GetStaticScriptGlobal(realCallback);
if (sgo) {
// Make sure that if this is a window it's the current inner, since the
// nsIScriptContext and hence JSContext are associated with the outer
// window. Which means that if someone holds on to a function from a
// now-unloaded document we'd have the new document as the script entry
// point...
nsCOMPtr<nsPIDOMWindow> win = do_QueryInterface(sgo);
if (win) {
MOZ_ASSERT(win->IsInnerWindow());
nsPIDOMWindow* outer = win->GetOuterWindow();
if (!outer || win != outer->GetCurrentInnerWindow()) {
// Just bail out from here
return;
}
}
// if not a window at all, just press on
ctx = sgo->GetContext();
if (ctx) {
// We don't check whether scripts are enabled on ctx, because
// CheckFunctionAccess will do that anyway... and because we ignore them
// being disabled if the callee is system.
cx = ctx->GetNativeContext();
}
}
// if not a window at all, just press on
ctx = sgo->GetContext();
if (ctx) {
// We don't check whether scripts are enabled on ctx, because
// CheckFunctionAccess will do that anyway... and because we ignore them
// being disabled if the callee is system.
cx = ctx->GetNativeContext();
if (!cx) {
// We didn't manage to hunt down a script global to work with. Just fall
// back on using the safe context.
cx = nsContentUtils::GetSafeJSContext();
}
}
if (!cx) {
// We didn't manage to hunt down a script global to work with. Just fall
// back on using the safe context.
cx = nsContentUtils::GetSafeJSContext();
// Make sure our JSContext is pushed on the stack.
mCxPusher.Push(cx);
} else {
cx = workers::GetCurrentThreadJSContext();
}
// Make sure our JSContext is pushed on the stack.
mCxPusher.Push(cx);
// Unmark the callable, and stick it in a Rooted before it can go gray again.
// Nothing before us in this function can trigger a CC, so it's safe to wait
// until here it do the unmark. This allows us to order the following two
@ -109,16 +118,18 @@ CallbackObject::CallSetup::CallSetup(JS::Handle<JSObject*> aCallback,
JS::ExposeObjectToActiveJS(aCallback);
mRootedCallable.construct(cx, aCallback);
// Check that it's ok to run this callback at all.
// FIXME: Bug 807371: we want a less silly check here.
// Make sure to unwrap aCallback before passing it in, because
// getting principals from wrappers is silly.
nsresult rv = nsContentUtils::GetSecurityManager()->
CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr);
if (mIsMainThread) {
// Check that it's ok to run this callback at all.
// FIXME: Bug 807371: we want a less silly check here.
// Make sure to unwrap aCallback before passing it in, because
// getting principals from wrappers is silly.
nsresult rv = nsContentUtils::GetSecurityManager()->
CheckFunctionAccess(cx, js::UncheckedUnwrap(aCallback), nullptr);
if (NS_FAILED(rv)) {
// Security check failed. We're done here.
return;
if (NS_FAILED(rv)) {
// Security check failed. We're done here.
return;
}
}
// Enter the compartment of our callback, so we can actually work with it.
@ -204,12 +215,19 @@ CallbackObject::CallSetup::~CallSetup()
// Popping an nsCxPusher is safe even if it never got pushed.
mCxPusher.Pop();
// It is important that this is the last thing we do, after leaving the
// compartment and popping the context.
if (mIsMainThread) {
nsContentUtils::LeaveMicroTask();
}
}
already_AddRefed<nsISupports>
CallbackObjectHolderBase::ToXPCOMCallback(CallbackObject* aCallback,
const nsIID& aIID) const
{
MOZ_ASSERT(NS_IsMainThread());
if (!aCallback) {
return nullptr;
}

View File

@ -151,10 +151,6 @@ protected:
// And now members whose construction/destruction order we need to control.
// Put our nsAutoMicrotask first, so it gets destroyed after everything else
// is gone
nsAutoMicroTask mMt;
nsCxPusher mCxPusher;
// Constructed the rooter within the scope of mCxPusher above, so that it's
@ -172,6 +168,7 @@ protected:
ErrorResult& mErrorResult;
const ExceptionHandling mExceptionHandling;
uint32_t mSavedJSContextOptions;
const bool mIsMainThread;
};
};

View File

@ -827,7 +827,7 @@ XPCConvert::NativeInterface2JSObject(jsval* d,
return false;
// First, see if this object supports the wrapper cache.
// Note: If |cache->IsProxy()| is true, then it means that the object
// Note: If |cache->IsDOMBinding()| is true, then it means that the object
// implementing it doesn't want a wrapped native as its JS Object, but
// instead it provides its own proxy object. In that case, the object
// to use is found as cache->GetWrapper(). If that is null, then the
@ -862,7 +862,7 @@ XPCConvert::NativeInterface2JSObject(jsval* d,
if (cpow) {
if (!JS_WrapObject(cx, cpow.address()))
return false;
*d = OBJECT_TO_JSVAL(cpow);
*d = JS::ObjectValue(*cpow);
return true;
}