Bug 715634 - Cleanup XPCJSContextStack; r=bholley

This commit is contained in:
Ms2ger 2012-01-11 09:23:07 +01:00
parent 74a9e0514a
commit 123076fb33
8 changed files with 204 additions and 212 deletions

View File

@ -40,8 +40,12 @@
/* Call context. */
#include "mozilla/Util.h"
#include "xpcprivate.h"
using namespace mozilla;
XPCCallContext::XPCCallContext(XPCContext::LangType callerLanguage,
JSContext* cx /* = nsnull */,
JSObject* obj /* = nsnull */,
@ -107,15 +111,15 @@ XPCCallContext::Init(XPCContext::LangType callerLanguage,
return;
XPCJSContextStack* stack = mThreadData->GetJSContextStack();
JSContext* topJSContext;
if (!stack || NS_FAILED(stack->Peek(&topJSContext))) {
if (!stack) {
// If we don't have a stack we're probably in shutdown.
NS_ASSERTION(!stack, "Bad, Peek failed!");
mJSContext = nsnull;
return;
}
JSContext *topJSContext = stack->Peek();
if (!mJSContext) {
// This is slightly questionable. If called without an explicit
// JSContext (generally a call to a wrappedJS) we will use the JSContext
@ -125,14 +129,17 @@ XPCCallContext::Init(XPCContext::LangType callerLanguage,
// have JS stack 'continuity' for purposes of stack traces etc.
// Note: this *is* what the pre-XPCCallContext xpconnect did too.
if (topJSContext)
if (topJSContext) {
mJSContext = topJSContext;
else if (NS_FAILED(stack->GetSafeJSContext(&mJSContext)) || !mJSContext)
return;
} else {
mJSContext = stack->GetSafeJSContext();
if (!mJSContext)
return;
}
}
if (topJSContext != mJSContext) {
if (NS_FAILED(stack->Push(mJSContext))) {
if (!stack->Push(mJSContext)) {
NS_ERROR("bad!");
return;
}
@ -346,13 +353,8 @@ XPCCallContext::~XPCCallContext()
XPCJSContextStack* stack = mThreadData->GetJSContextStack();
NS_ASSERTION(stack, "bad!");
if (stack) {
#ifdef DEBUG
JSContext* poppedCX;
nsresult rv = stack->Pop(&poppedCX);
NS_ASSERTION(NS_SUCCEEDED(rv) && poppedCX == mJSContext, "bad pop");
#else
(void) stack->Pop(nsnull);
#endif
DebugOnly<JSContext*> poppedCX = stack->Pop();
NS_ASSERTION(poppedCX == mJSContext, "bad pop");
}
}
@ -519,10 +521,7 @@ XPCLazyCallContext::AssertContextIsTopOfStack(JSContext* cx)
XPCPerThreadData* tls = XPCPerThreadData::GetData(cx);
XPCJSContextStack* stack = tls->GetJSContextStack();
JSContext* topJSContext;
nsresult rv = stack->Peek(&topJSContext);
NS_ASSERTION(NS_SUCCEEDED(rv), "XPCJSContextStack::Peek failed");
JSContext *topJSContext = stack->Peek();
NS_ASSERTION(cx == topJSContext, "wrong context on XPCJSContextStack!");
}
#endif

View File

@ -42,6 +42,8 @@
/* The "Components" xpcom objects for JavaScript. */
#include "mozilla/unused.h"
#include "xpcprivate.h"
#include "nsReadableUtils.h"
#include "xpcIJSModuleLoader.h"
@ -59,6 +61,7 @@
#include "jsgc.h"
#include "jsfriendapi.h"
using namespace mozilla;
using namespace js;
/***************************************************************************/
// stuff used by all
@ -3444,7 +3447,7 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
XPCPerThreadData *data = XPCPerThreadData::GetData(cx);
XPCJSContextStack *stack = nsnull;
if (data && (stack = data->GetJSContextStack())) {
if (NS_FAILED(stack->Push(sandcx->GetJSContext()))) {
if (!stack->Push(sandcx->GetJSContext())) {
JS_ReportError(cx,
"Unable to initialize XPConnect with the sandbox context");
JSPRINCIPALS_DROP(cx, jsPrincipals);
@ -3467,9 +3470,8 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
JSString *str = nsnull;
if (!ac.enter(sandcx->GetJSContext(), sandbox)) {
if (stack) {
stack->Pop(nsnull);
}
if (stack)
unused << stack->Pop();
return NS_ERROR_FAILURE;
}
@ -3545,9 +3547,8 @@ xpc_EvalInSandbox(JSContext *cx, JSObject *sandbox, const nsAString& source,
}
}
if (stack) {
stack->Pop(nsnull);
}
if (stack)
unused << stack->Pop();
JSPRINCIPALS_DROP(cx, jsPrincipals);

View File

@ -1125,9 +1125,7 @@ xpc_qsAssertContextOK(JSContext *cx)
XPCPerThreadData *thread = XPCPerThreadData::GetData(cx);
XPCJSContextStack* stack = thread->GetJSContextStack();
JSContext* topJSContext = nsnull;
nsresult rv = stack->Peek(&topJSContext);
NS_ASSERTION(NS_SUCCEEDED(rv), "XPCJSContextStack::Peek failed");
JSContext *topJSContext = stack->Peek();
// This is what we're actually trying to assert here.
NS_ASSERTION(cx == topJSContext, "wrong context on XPCJSContextStack!");

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
* vim: set ts=8 sw=4 et tw=80:
*
* ***** BEGIN LICENSE BLOCK *****
@ -52,14 +52,6 @@ using namespace mozilla;
/***************************************************************************/
XPCJSContextStack::XPCJSContextStack()
: mStack(),
mSafeJSContext(nsnull),
mOwnSafeJSContext(nsnull)
{
// empty...
}
XPCJSContextStack::~XPCJSContextStack()
{
if (mOwnSafeJSContext) {
@ -69,54 +61,37 @@ XPCJSContextStack::~XPCJSContextStack()
}
}
/* readonly attribute PRInt32 count; */
NS_IMETHODIMP
XPCJSContextStack::GetCount(PRInt32 *aCount)
JSContext*
XPCJSContextStack::Pop()
{
*aCount = mStack.Length();
return NS_OK;
}
MOZ_ASSERT(!mStack.IsEmpty());
/* JSContext peek (); */
NS_IMETHODIMP
XPCJSContextStack::Peek(JSContext * *_retval)
{
*_retval = mStack.IsEmpty() ? nsnull : mStack[mStack.Length() - 1].cx;
return NS_OK;
}
uint32_t idx = mStack.Length() - 1; // The thing we're popping
/* JSContext pop (); */
NS_IMETHODIMP
XPCJSContextStack::Pop(JSContext * *_retval)
{
NS_ASSERTION(!mStack.IsEmpty(), "ThreadJSContextStack underflow");
PRUint32 idx = mStack.Length() - 1; // The thing we're popping
if (_retval)
*_retval = mStack[idx].cx;
JSContext *cx = mStack[idx].cx;
mStack.RemoveElementAt(idx);
if (idx > 0) {
--idx; // Advance to new top of the stack
if (idx == 0)
return cx;
XPCJSContextInfo & e = mStack[idx];
NS_ASSERTION(!e.suspendDepth || e.cx, "Shouldn't have suspendDepth without a cx!");
if (e.cx) {
if (e.suspendDepth) {
JS_ResumeRequest(e.cx, e.suspendDepth);
e.suspendDepth = 0;
}
--idx; // Advance to new top of the stack
if (e.savedFrameChain) {
// Pop() can be called outside any request for e.cx.
JSAutoRequest ar(e.cx);
JS_RestoreFrameChain(e.cx);
e.savedFrameChain = false;
}
XPCJSContextInfo &e = mStack[idx];
NS_ASSERTION(!e.suspendDepth || e.cx, "Shouldn't have suspendDepth without a cx!");
if (e.cx) {
if (e.suspendDepth) {
JS_ResumeRequest(e.cx, e.suspendDepth);
e.suspendDepth = 0;
}
if (e.savedFrameChain) {
// Pop() can be called outside any request for e.cx.
JSAutoRequest ar(e.cx);
JS_RestoreFrameChain(e.cx);
e.savedFrameChain = false;
}
}
return NS_OK;
return cx;
}
static nsIPrincipal*
@ -131,53 +106,54 @@ GetPrincipalFromCx(JSContext *cx)
return nsnull;
}
/* void push (in JSContext cx); */
NS_IMETHODIMP
XPCJSContextStack::Push(JSContext * cx)
bool
XPCJSContextStack::Push(JSContext *cx)
{
JS_ASSERT_IF(cx, JS_GetContextThread(cx));
if (mStack.Length() > 0) {
XPCJSContextInfo & e = mStack[mStack.Length() - 1];
if (e.cx) {
if (e.cx == cx) {
nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
if (ssm) {
if (nsIPrincipal* globalObjectPrincipal = GetPrincipalFromCx(cx)) {
nsIPrincipal* subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
bool equals = false;
globalObjectPrincipal->Equals(subjectPrincipal, &equals);
if (equals) {
goto append;
}
MOZ_ASSERT_IF(cx, JS_GetContextThread(cx));
if (mStack.Length() == 0) {
mStack.AppendElement(cx);
return true;
}
XPCJSContextInfo &e = mStack[mStack.Length() - 1];
if (e.cx) {
if (e.cx == cx) {
nsIScriptSecurityManager* ssm = XPCWrapper::GetSecurityManager();
if (ssm) {
if (nsIPrincipal* globalObjectPrincipal = GetPrincipalFromCx(cx)) {
nsIPrincipal* subjectPrincipal = ssm->GetCxSubjectPrincipal(cx);
bool equals = false;
globalObjectPrincipal->Equals(subjectPrincipal, &equals);
if (equals) {
mStack.AppendElement(cx);
return true;
}
}
}
{
// Push() can be called outside any request for e.cx.
JSAutoRequest ar(e.cx);
if (!JS_SaveFrameChain(e.cx))
return NS_ERROR_OUT_OF_MEMORY;
e.savedFrameChain = true;
}
if (!cx)
e.suspendDepth = JS_SuspendRequest(e.cx);
}
{
// Push() can be called outside any request for e.cx.
JSAutoRequest ar(e.cx);
if (!JS_SaveFrameChain(e.cx))
return false;
e.savedFrameChain = true;
}
if (!cx)
e.suspendDepth = JS_SuspendRequest(e.cx);
}
append:
if (!mStack.AppendElement(cx))
return NS_ERROR_OUT_OF_MEMORY;
return NS_OK;
mStack.AppendElement(cx);
return true;
}
#ifdef DEBUG
JSBool
XPCJSContextStack::DEBUG_StackHasJSContext(JSContext* aJSContext)
bool
XPCJSContextStack::DEBUG_StackHasJSContext(JSContext *cx)
{
for (PRUint32 i = 0; i < mStack.Length(); i++)
if (aJSContext == mStack[i].cx)
if (cx == mStack[i].cx)
return true;
return false;
}
@ -211,88 +187,89 @@ static JSClass global_class = {
extern void
mozJSLoaderErrorReporter(JSContext *cx, const char *message, JSErrorReport *rep);
/* attribute JSContext safeJSContext; */
NS_IMETHODIMP
XPCJSContextStack::GetSafeJSContext(JSContext * *aSafeJSContext)
JSContext*
XPCJSContextStack::GetSafeJSContext()
{
if (!mSafeJSContext) {
// Start by getting the principal holder and principal for this
// context. If we can't manage that, don't bother with the rest.
nsRefPtr<nsNullPrincipal> principal = new nsNullPrincipal();
nsCOMPtr<nsIScriptObjectPrincipal> sop;
if (principal) {
nsresult rv = principal->Init();
if (NS_SUCCEEDED(rv))
sop = new PrincipalHolder(principal);
}
if (!sop) {
*aSafeJSContext = nsnull;
return NS_ERROR_FAILURE;
if (mSafeJSContext)
return mSafeJSContext;
// Start by getting the principal holder and principal for this
// context. If we can't manage that, don't bother with the rest.
nsRefPtr<nsNullPrincipal> principal = new nsNullPrincipal();
nsresult rv = principal->Init();
if (NS_FAILED(rv))
return NULL;
nsCOMPtr<nsIScriptObjectPrincipal> sop = new PrincipalHolder(principal);
nsRefPtr<nsXPConnect> xpc = nsXPConnect::GetXPConnect();
if (!xpc)
return NULL;
XPCJSRuntime* xpcrt = xpc->GetRuntime();
if (!xpcrt)
return NULL;
JSRuntime *rt = xpcrt->GetJSRuntime();
if (!rt)
return NULL;
mSafeJSContext = JS_NewContext(rt, 8192);
if (!mSafeJSContext)
return NULL;
JSObject *glob;
{
// scoped JS Request
JSAutoRequest req(mSafeJSContext);
JS_SetErrorReporter(mSafeJSContext, mozJSLoaderErrorReporter);
// Because we can run off the main thread, we create an MT
// global object. Our principal is the unique key.
JSCompartment *compartment;
nsresult rv = xpc_CreateMTGlobalObject(mSafeJSContext,
&global_class,
principal, &glob,
&compartment);
if (NS_FAILED(rv))
glob = nsnull;
if (glob) {
// Make sure the context is associated with a proper compartment
// and not the default compartment.
JS_SetGlobalObject(mSafeJSContext, glob);
// Note: make sure to set the private before calling
// InitClasses
nsIScriptObjectPrincipal* priv = nsnull;
sop.swap(priv);
if (!JS_SetPrivate(mSafeJSContext, glob, priv)) {
// Drop the whole thing
NS_RELEASE(priv);
glob = nsnull;
}
}
JSRuntime *rt;
XPCJSRuntime* xpcrt;
nsXPConnect* xpc = nsXPConnect::GetXPConnect();
nsCOMPtr<nsIXPConnect> xpcholder(static_cast<nsIXPConnect*>(xpc));
if (xpc && (xpcrt = xpc->GetRuntime()) && (rt = xpcrt->GetJSRuntime())) {
JSObject *glob;
mSafeJSContext = JS_NewContext(rt, 8192);
if (mSafeJSContext) {
// scoped JS Request
JSAutoRequest req(mSafeJSContext);
JS_SetErrorReporter(mSafeJSContext, mozJSLoaderErrorReporter);
// Because we can run off the main thread, we create an MT
// global object. Our principal is the unique key.
JSCompartment *compartment;
nsresult rv = xpc_CreateMTGlobalObject(mSafeJSContext,
&global_class,
principal, &glob,
&compartment);
if (NS_FAILED(rv))
glob = nsnull;
if (glob) {
// Make sure the context is associated with a proper compartment
// and not the default compartment.
JS_SetGlobalObject(mSafeJSContext, glob);
// Note: make sure to set the private before calling
// InitClasses
nsIScriptObjectPrincipal* priv = nsnull;
sop.swap(priv);
if (!JS_SetPrivate(mSafeJSContext, glob, priv)) {
// Drop the whole thing
NS_RELEASE(priv);
glob = nsnull;
}
}
// After this point either glob is null and the
// nsIScriptObjectPrincipal ownership is either handled by the
// nsCOMPtr or dealt with, or we'll release in the finalize
// hook.
if (glob && NS_FAILED(xpc->InitClasses(mSafeJSContext, glob))) {
glob = nsnull;
}
}
if (mSafeJSContext && !glob) {
// Destroy the context outside the scope of JSAutoRequest that
// uses the context in its destructor.
JS_DestroyContext(mSafeJSContext);
mSafeJSContext = nsnull;
}
// Save it off so we can destroy it later.
mOwnSafeJSContext = mSafeJSContext;
// After this point either glob is null and the
// nsIScriptObjectPrincipal ownership is either handled by the
// nsCOMPtr or dealt with, or we'll release in the finalize
// hook.
if (glob && NS_FAILED(xpc->InitClasses(mSafeJSContext, glob))) {
glob = nsnull;
}
}
if (mSafeJSContext && !glob) {
// Destroy the context outside the scope of JSAutoRequest that
// uses the context in its destructor.
JS_DestroyContext(mSafeJSContext);
mSafeJSContext = nsnull;
}
*aSafeJSContext = mSafeJSContext;
return mSafeJSContext ? NS_OK : NS_ERROR_UNEXPECTED;
// Save it off so we can destroy it later.
mOwnSafeJSContext = mSafeJSContext;
return mSafeJSContext;
}
/***************************************************************************/

View File

@ -534,9 +534,8 @@ GetContextFromObject(JSObject *obj)
// Don't stomp over a running context.
XPCJSContextStack* stack =
XPCPerThreadData::GetData(nsnull)->GetJSContextStack();
JSContext* topJSContext;
if (stack && NS_SUCCEEDED(stack->Peek(&topJSContext)) && topJSContext)
if (stack && stack->Peek())
return nsnull;
// In order to get a context, we need a context.

View File

@ -1311,7 +1311,7 @@ public:
~AutoPopJSContext()
{
if (mCx)
mStack->Pop(nsnull);
mStack->Pop();
}
void PushIfNotTop(JSContext *cx)
@ -1319,10 +1319,9 @@ public:
NS_ASSERTION(cx, "Null context!");
NS_ASSERTION(!mCx, "This class is only meant to be used once!");
JSContext *cxTop = nsnull;
mStack->Peek(&cxTop);
JSContext *cxTop = mStack->Peek();
if (cxTop != cx && NS_SUCCEEDED(mStack->Push(cx)))
if (cxTop != cx && mStack->Push(cx))
mCx = cx;
}

View File

@ -1,4 +1,4 @@
/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
*
* ***** BEGIN LICENSE BLOCK *****
* Version: MPL 1.1/GPL 2.0/LGPL 2.1
@ -2463,8 +2463,7 @@ nsXPConnect::UnregisterGCCallback(JSGCCallback func)
NS_IMETHODIMP
nsXPConnect::GetCount(PRInt32 *aCount)
{
if (!aCount)
return NS_ERROR_NULL_POINTER;
MOZ_ASSERT(aCount);
XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull);
@ -2473,15 +2472,15 @@ nsXPConnect::GetCount(PRInt32 *aCount)
return NS_ERROR_FAILURE;
}
return data->GetJSContextStack()->GetCount(aCount);
*aCount = data->GetJSContextStack()->Count();
return NS_OK;
}
/* JSContext Peek (); */
NS_IMETHODIMP
nsXPConnect::Peek(JSContext * *_retval)
{
if (!_retval)
return NS_ERROR_NULL_POINTER;
MOZ_ASSERT(_retval);
XPCPerThreadData* data = XPCPerThreadData::GetData(nsnull);
@ -2490,7 +2489,8 @@ nsXPConnect::Peek(JSContext * *_retval)
return NS_ERROR_FAILURE;
}
return data->GetJSContextStack()->Peek(_retval);
*_retval = data->GetJSContextStack()->Peek();
return NS_OK;
}
void
@ -2581,11 +2581,14 @@ nsXPConnect::Pop(JSContext * *_retval)
if (!data) {
if (_retval)
*_retval = nsnull;
*_retval = NULL;
return NS_ERROR_FAILURE;
}
return data->GetJSContextStack()->Pop(_retval);
JSContext *cx = data->GetJSContextStack()->Pop();
if (_retval)
*_retval = cx;
return NS_OK;
}
/* void Push (in JSContext cx); */
@ -2598,7 +2601,7 @@ nsXPConnect::Push(JSContext * cx)
return NS_ERROR_FAILURE;
if (gDebugMode != gDesiredDebugMode && NS_IsMainThread()) {
const nsTArray<XPCJSContextInfo>* stack = data->GetJSContextStack()->GetStack();
const InfallibleTArray<XPCJSContextInfo>* stack = data->GetJSContextStack()->GetStack();
if (!gDesiredDebugMode) {
/* Turn off debug mode immediately, even if JS code is currently running */
CheckForDebugMode(mRuntime->GetJSRuntime());
@ -2616,7 +2619,7 @@ nsXPConnect::Push(JSContext * cx)
}
}
return data->GetJSContextStack()->Push(cx);
return data->GetJSContextStack()->Push(cx) ? NS_OK : NS_ERROR_OUT_OF_MEMORY;
}
/* attribute JSContext SafeJSContext; */
@ -2632,7 +2635,8 @@ nsXPConnect::GetSafeJSContext(JSContext * *aSafeJSContext)
return NS_ERROR_FAILURE;
}
return data->GetJSContextStack()->GetSafeJSContext(aSafeJSContext);
*aSafeJSContext = data->GetJSContextStack()->GetSafeJSContext();
return *aSafeJSContext ? NS_OK : NS_ERROR_FAILURE;
}
nsIPrincipal*

View File

@ -3583,21 +3583,36 @@ struct XPCJSContextInfo {
class XPCJSContextStack
{
public:
NS_DECL_NSIJSCONTEXTSTACK
NS_DECL_NSITHREADJSCONTEXTSTACK
XPCJSContextStack()
: mSafeJSContext(NULL)
, mOwnSafeJSContext(NULL)
{ }
XPCJSContextStack();
virtual ~XPCJSContextStack();
uint32_t Count()
{
return mStack.Length();
}
JSContext *Peek()
{
return mStack.IsEmpty() ? NULL : mStack[mStack.Length() - 1].cx;
}
JSContext *Pop();
bool Push(JSContext *cx);
JSContext *GetSafeJSContext();
#ifdef DEBUG
JSBool DEBUG_StackHasJSContext(JSContext* aJSContext);
bool DEBUG_StackHasJSContext(JSContext *cx);
#endif
const nsTArray<XPCJSContextInfo>* GetStack()
const InfallibleTArray<XPCJSContextInfo>* GetStack()
{ return &mStack; }
private:
nsAutoTArray<XPCJSContextInfo, 16> mStack;
AutoInfallibleTArray<XPCJSContextInfo, 16> mStack;
JSContext* mSafeJSContext;
JSContext* mOwnSafeJSContext;
};
@ -3615,7 +3630,7 @@ public:
NS_DECL_NSIJSCONTEXTSTACKITERATOR
private:
const nsTArray<XPCJSContextInfo> *mStack;
const InfallibleTArray<XPCJSContextInfo> *mStack;
PRUint32 mPosition;
};